Skip to content

Commit 37ca805

Browse files
committed
implement delete_file, delete_directory, move_file, copy_file
1 parent 6e2a420 commit 37ca805

5 files changed

Lines changed: 193 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ Claude can automatically use these tools:
9090
- `morph_edit_file` - Ultra-fast code editing (requires MORPH_API_KEY)
9191
- `list_directory` - List directory contents
9292
- `create_directory` - Create directories
93+
- `delete_file` - Delete files (asks for confirmation)
94+
- `delete_directory` - Delete directories recursively (asks for confirmation)
95+
- `move_file` - Move or rename files/directories
96+
- `copy_file` - Copy files
9397

9498
**Code & Search:**
9599
- `search_code` - Fast regex-based code search (requires `ripgrep`)

src/conversation.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ When helping users:
4545
- Execute bash commands to test code and verify functionality (read-only, no sudo or file modifications)
4646
- Explain your reasoning when using tools
4747
48+
File deletion safety:
49+
- ALWAYS ask the user for explicit confirmation before using delete_file or delete_directory
50+
- List the files/directories that will be deleted and wait for user approval
51+
- Never delete files without prior user confirmation
52+
4853
Testing after code changes:
4954
- After editing code files (not comments, README, or documentation), ALWAYS test the changes using execute_bash
5055
- Run appropriate build/test commands based on the project type:

src/tools/filesystem.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl FileSystemTool {
157157
Ok(full_path.exists())
158158
}
159159

160-
pub fn _delete_file(&self, path: &str) -> Result<()> {
160+
pub fn delete_file(&self, path: &str) -> Result<()> {
161161
let full_path = self.validate_path(path)?;
162162

163163
if !full_path.exists() {
@@ -175,6 +175,67 @@ impl FileSystemTool {
175175
Ok(())
176176
}
177177

178+
pub fn delete_directory(&self, path: &str) -> Result<()> {
179+
let full_path = self.validate_path(path)?;
180+
181+
if !full_path.exists() {
182+
return Err(SofosError::FileNotFound(path.to_string()));
183+
}
184+
185+
if !full_path.is_dir() {
186+
return Err(SofosError::InvalidPath(format!(
187+
"'{}' is not a directory",
188+
path
189+
)));
190+
}
191+
192+
fs::remove_dir_all(&full_path)?;
193+
Ok(())
194+
}
195+
196+
pub fn move_file(&self, source: &str, destination: &str) -> Result<()> {
197+
let source_path = self.validate_path(source)?;
198+
let dest_path = self.validate_path(destination)?;
199+
200+
if !source_path.exists() {
201+
return Err(SofosError::FileNotFound(source.to_string()));
202+
}
203+
204+
if let Some(parent) = dest_path.parent() {
205+
if !parent.exists() {
206+
fs::create_dir_all(parent)?;
207+
}
208+
}
209+
210+
fs::rename(&source_path, &dest_path)?;
211+
Ok(())
212+
}
213+
214+
pub fn copy_file(&self, source: &str, destination: &str) -> Result<()> {
215+
let source_path = self.validate_path(source)?;
216+
let dest_path = self.validate_path(destination)?;
217+
218+
if !source_path.exists() {
219+
return Err(SofosError::FileNotFound(source.to_string()));
220+
}
221+
222+
if !source_path.is_file() {
223+
return Err(SofosError::InvalidPath(format!(
224+
"'{}' is not a file",
225+
source
226+
)));
227+
}
228+
229+
if let Some(parent) = dest_path.parent() {
230+
if !parent.exists() {
231+
fs::create_dir_all(parent)?;
232+
}
233+
}
234+
235+
fs::copy(&source_path, &dest_path)?;
236+
Ok(())
237+
}
238+
178239
pub fn _workspace(&self) -> &Path {
179240
&self.workspace
180241
}

src/tools/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,44 @@ impl ToolExecutor {
150150
self.fs_tool.write_file(path, &merged_code)?;
151151
Ok(format!("Successfully applied Morph edit to '{}'", path))
152152
}
153+
"delete_file" => {
154+
let path = input["path"]
155+
.as_str()
156+
.ok_or_else(|| SofosError::ToolExecution("Missing 'path' parameter".to_string()))?;
157+
158+
self.fs_tool.delete_file(path)?;
159+
Ok(format!("Successfully deleted file '{}'", path))
160+
}
161+
"delete_directory" => {
162+
let path = input["path"]
163+
.as_str()
164+
.ok_or_else(|| SofosError::ToolExecution("Missing 'path' parameter".to_string()))?;
165+
166+
self.fs_tool.delete_directory(path)?;
167+
Ok(format!("Successfully deleted directory '{}'", path))
168+
}
169+
"move_file" => {
170+
let source = input["source"]
171+
.as_str()
172+
.ok_or_else(|| SofosError::ToolExecution("Missing 'source' parameter".to_string()))?;
173+
let destination = input["destination"]
174+
.as_str()
175+
.ok_or_else(|| SofosError::ToolExecution("Missing 'destination' parameter".to_string()))?;
176+
177+
self.fs_tool.move_file(source, destination)?;
178+
Ok(format!("Successfully moved '{}' to '{}'", source, destination))
179+
}
180+
"copy_file" => {
181+
let source = input["source"]
182+
.as_str()
183+
.ok_or_else(|| SofosError::ToolExecution("Missing 'source' parameter".to_string()))?;
184+
let destination = input["destination"]
185+
.as_str()
186+
.ok_or_else(|| SofosError::ToolExecution("Missing 'destination' parameter".to_string()))?;
187+
188+
self.fs_tool.copy_file(source, destination)?;
189+
Ok(format!("Successfully copied '{}' to '{}'", source, destination))
190+
}
153191
"execute_bash" => {
154192
let command = input["command"]
155193
.as_str()

src/tools/types.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,82 @@ fn execute_bash_tool() -> Tool {
118118
}
119119
}
120120

121+
fn delete_file_tool() -> Tool {
122+
Tool {
123+
name: "delete_file".to_string(),
124+
description: "Delete a file in the current workspace. IMPORTANT: Always ask the user for confirmation before deleting files. Use this only after explicit user approval.".to_string(),
125+
input_schema: json!({
126+
"type": "object",
127+
"properties": {
128+
"path": {
129+
"type": "string",
130+
"description": "The relative path to the file to delete (e.g., 'src/old_file.rs')"
131+
}
132+
},
133+
"required": ["path"]
134+
}),
135+
}
136+
}
137+
138+
fn delete_directory_tool() -> Tool {
139+
Tool {
140+
name: "delete_directory".to_string(),
141+
description: "Delete a directory and all its contents in the current workspace. IMPORTANT: Always ask the user for confirmation before deleting directories. Use this only after explicit user approval.".to_string(),
142+
input_schema: json!({
143+
"type": "object",
144+
"properties": {
145+
"path": {
146+
"type": "string",
147+
"description": "The relative path to the directory to delete (e.g., 'src/old_module')"
148+
}
149+
},
150+
"required": ["path"]
151+
}),
152+
}
153+
}
154+
155+
fn move_file_tool() -> Tool {
156+
Tool {
157+
name: "move_file".to_string(),
158+
description: "Move or rename a file or directory within the workspace. Creates parent directories if needed.".to_string(),
159+
input_schema: json!({
160+
"type": "object",
161+
"properties": {
162+
"source": {
163+
"type": "string",
164+
"description": "The relative path to the source file/directory (e.g., 'src/old_name.rs')"
165+
},
166+
"destination": {
167+
"type": "string",
168+
"description": "The relative path to the destination (e.g., 'src/new_name.rs' or 'new_folder/file.rs')"
169+
}
170+
},
171+
"required": ["source", "destination"]
172+
}),
173+
}
174+
}
175+
176+
fn copy_file_tool() -> Tool {
177+
Tool {
178+
name: "copy_file".to_string(),
179+
description: "Copy a file to a new location within the workspace. Creates parent directories if needed.".to_string(),
180+
input_schema: json!({
181+
"type": "object",
182+
"properties": {
183+
"source": {
184+
"type": "string",
185+
"description": "The relative path to the source file (e.g., 'src/template.rs')"
186+
},
187+
"destination": {
188+
"type": "string",
189+
"description": "The relative path to the destination (e.g., 'src/new_file.rs')"
190+
}
191+
},
192+
"required": ["source", "destination"]
193+
}),
194+
}
195+
}
196+
121197
fn morph_edit_file_tool() -> Tool {
122198
Tool {
123199
name: "morph_edit_file".to_string(),
@@ -150,6 +226,10 @@ pub fn get_tools() -> Vec<Tool> {
150226
write_file_tool(false),
151227
list_directory_tool(),
152228
create_directory_tool(),
229+
delete_file_tool(),
230+
delete_directory_tool(),
231+
move_file_tool(),
232+
copy_file_tool(),
153233
web_search_tool(),
154234
execute_bash_tool(),
155235
]
@@ -162,6 +242,10 @@ pub fn get_tools_with_morph() -> Vec<Tool> {
162242
write_file_tool(true),
163243
list_directory_tool(),
164244
create_directory_tool(),
245+
delete_file_tool(),
246+
delete_directory_tool(),
247+
move_file_tool(),
248+
copy_file_tool(),
165249
web_search_tool(),
166250
execute_bash_tool(),
167251
morph_edit_file_tool(),

0 commit comments

Comments
 (0)