11from __future__ import annotations
22
3- import os
43import subprocess
54from pathlib import Path
6- from typing import Literal
75
86from patchwork .steps .ResolveIssue .tools .tool import Tool
97
@@ -29,14 +27,9 @@ def json_schema(self) -> dict:
2927* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.""" ,
3028 "input_schema" : {
3129 "type" : "object" ,
32- "properties" : {
33- "command" : {
34- "type" : "string" ,
35- "description" : "The bash command to run."
36- }
37- },
38- "required" : ["command" ]
39- }
30+ "properties" : {"command" : {"type" : "string" , "description" : "The bash command to run." }},
31+ "required" : ["command" ],
32+ },
4033 }
4134
4235 def execute (
@@ -51,87 +44,10 @@ def execute(
5144
5245 try :
5346 result = subprocess .run (
54- command ,
55- shell = True ,
56- cwd = self .path ,
57- capture_output = True ,
58- text = True ,
59- timeout = 60 # Add timeout for safety
47+ command , shell = True , cwd = self .path , capture_output = True , text = True , timeout = 60 # Add timeout for safety
6048 )
6149 return result .stdout if result .returncode == 0 else f"Error: { result .stderr } "
6250 except subprocess .TimeoutExpired :
6351 return "Error: Command timed out after 60 seconds"
6452 except Exception as e :
6553 return f"Error: { str (e )} "
66-
67- @property
68- def tool_records (self ):
69- return dict (modified_files = [{"path" : file } for file in self .modified_files ])
70-
71- def __get_abs_path (self , path : str ):
72- abs_path = (self .repo_path / path .lstrip ("/" )).resolve ()
73- if not abs_path .is_relative_to (self .repo_path ):
74- raise ValueError (f"Path { path } contains illegal path traversal" )
75-
76- return abs_path
77-
78- def __view (self , path , view_range ):
79- abs_path = self .__get_abs_path (path )
80- if not abs_path .exists ():
81- return f"Error: Path { path } does not exist"
82-
83- if abs_path .is_file ():
84- with open (abs_path , "r" ) as f :
85- content = f .read ()
86-
87- if view_range :
88- lines = content .splitlines ()
89- start , end = view_range
90- content = "\n " .join (lines [start - 1 : end ])
91- return content
92- elif abs_path .is_dir ():
93- result = []
94- for root , dirs , files in os .walk (abs_path ):
95- level = root [len (abs_path ) :].count (os .sep )
96- if level <= 2 :
97- for d in dirs :
98- result .append (d )
99- for f in files :
100- result .append (f )
101- return "\n " .join (result )
102-
103- def __create (self , file_text , path ):
104- abs_path = self .__get_abs_path (path )
105- if abs_path .exists ():
106- return f"Error: File { path } already exists"
107- abs_path .parent .mkdir (parents = True , exist_ok = True )
108- abs_path .write_text (file_text )
109- return f"File created successfully at: { path } "
110-
111- def __str_replace (self , new_str , old_str , path ):
112- abs_path = self .__get_abs_path (path )
113- if not abs_path .exists ():
114- return f"Error: File { path } does not exist"
115- if not abs_path .is_file ():
116- return f"Error: File { path } is not a file"
117- content = abs_path .read_text ()
118- occurrences = content .count (old_str )
119- if occurrences != 1 :
120- return f"Error: Found { occurrences } occurrences of old_str, expected exactly 1"
121- new_content = content .replace (old_str , new_str )
122- with open (abs_path , "w" ) as f :
123- f .write (new_content )
124- return "Replacement successful"
125-
126- def __insert (self , insert_line , new_str , path ):
127- abs_path = self .__get_abs_path (path )
128- if not abs_path .is_file ():
129- return f"Error: File { path } does not exist or is not a file"
130-
131- lines = abs_path .read_text ().splitlines (keepends = True )
132- if insert_line is None or insert_line < 1 or insert_line > len (lines ):
133- return f"Error: Invalid insert line { insert_line } "
134-
135- lines .insert (insert_line , new_str + "\n " )
136- abs_path .write_text ("" .join (lines ))
137- return "Insert successful"
0 commit comments