-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtools.py
More file actions
215 lines (175 loc) · 6.06 KB
/
tools.py
File metadata and controls
215 lines (175 loc) · 6.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import os
import json
import subprocess
from agent import ToolDefinition
def read_file(input: dict) -> str:
path = input["path"]
with open(path, "r") as f:
return f.read()
ReadFileDefinition = ToolDefinition(
name="read_file",
description="Read the contents of a given relative file path. Use this when you want to see what's inside a file. Do not use this with directory names.",
input_schema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The relative path of a file in the working directory.",
}
},
"required": ["path"],
},
function=read_file,
)
def list_files(input: dict) -> str:
path = input.get("path", ".")
if not path:
path = "."
# Only list immediate children, don't recurse (avoids venv explosion)
files = []
try:
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
if os.path.isdir(full_path):
files.append(entry + "/")
else:
files.append(entry)
except OSError as e:
return json.dumps({"error": str(e)})
return json.dumps(sorted(files))
ListFilesDefinition = ToolDefinition(
name="list_files",
description="List files and directories at a given path. If no path is provided, lists files in the current directory.",
input_schema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Optional relative path to list files from. Defaults to current directory if not provided.",
}
},
"required": [],
},
function=list_files,
)
def edit_file(input: dict) -> str:
path = input.get("path", "")
old_str = input.get("old_str", "")
new_str = input.get("new_str", "")
# Validate inputs
if not path:
raise ValueError("path is required")
if old_str == new_str:
raise ValueError("old_str and new_str must be different")
# If file doesn't exist and old_str is empty, create new file
if not os.path.exists(path):
if old_str == "":
return create_new_file(path, new_str)
raise FileNotFoundError(f"File not found: {path}")
# Read existing content
with open(path, "r") as f:
content = f.read()
# Check that old_str exists in file
if old_str and old_str not in content:
raise ValueError("old_str not found in file")
# Replace old_str with new_str
new_content = content.replace(old_str, new_str)
# Write back
with open(path, "w") as f:
f.write(new_content)
return "OK"
def create_new_file(file_path: str, content: str) -> str:
"""Helper to create a new file, including parent directories if needed."""
dir_path = os.path.dirname(file_path)
if dir_path:
os.makedirs(dir_path, exist_ok=True)
with open(file_path, "w") as f:
f.write(content)
return f"Successfully created file {file_path}"
EditFileDefinition = ToolDefinition(
name="edit_file",
description="""Make edits to a text file.
Replaces 'old_str' with 'new_str' in the given file. 'old_str' and 'new_str' MUST be different from each other.
If the file specified with path doesn't exist, it will be created.""",
input_schema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The path to the file",
},
"old_str": {
"type": "string",
"description": "Text to search for - must match exactly and must only have one match exactly",
},
"new_str": {
"type": "string",
"description": "Text to replace old_str with",
},
},
"required": ["path", "old_str", "new_str"],
},
function=edit_file,
)
# Dangerous command patterns to block
DANGEROUS_PATTERNS = ["rm -rf", "sudo", "> /dev", "mkfs", "dd if=", ":(){"]
def bash(input: dict) -> str:
command = input.get("command", "")
if not command:
raise ValueError("command is required")
# Block dangerous commands
for pattern in DANGEROUS_PATTERNS:
if pattern in command:
raise ValueError(f"Blocked dangerous command pattern: {pattern}")
result = subprocess.run(
command, shell=True, capture_output=True, text=True, timeout=30
)
return result.stdout + result.stderr
BashDefinition = ToolDefinition(
name="bash",
description="""Execute a bash command and return its output.
Use this to run shell commands like git, npm, python, cat, grep, etc.
Some dangerous commands (rm -rf, sudo, etc.) are blocked for safety.""",
input_schema={
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The bash command to execute",
},
},
"required": ["command"],
},
function=bash,
)
def code_search(input: dict) -> str:
pattern = input.get("pattern", "")
path = input.get("path", ".")
if not pattern:
raise ValueError("pattern is required")
result = subprocess.run(
["rg", "--line-number", pattern, path],
capture_output=True, text=True, timeout=30
)
return result.stdout or "No matches found"
CodeSearchDefinition = ToolDefinition(
name="code_search",
description="""Search for a pattern in the codebase using ripgrep.
Use this to find specific code, strings, or patterns across files.
Returns matching lines with file paths and line numbers.""",
input_schema={
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "The regex pattern to search for",
},
"path": {
"type": "string",
"description": "Optional path to search in. Defaults to current directory.",
},
},
"required": ["pattern"],
},
function=code_search,
)