tycode_core/file/modify/
delete_file.rs1use crate::chat::events::{ToolExecutionResult, ToolRequest as ToolRequestEvent, ToolRequestType};
2use crate::file::access::FileAccessManager;
3use crate::file::manager::FileModificationManager;
4use crate::tools::r#trait::{
5 ContinuationPreference, FileModification, FileOperation, ToolCallHandle, ToolCategory,
6 ToolExecutor, ToolOutput, ToolRequest,
7};
8use crate::tools::ToolName;
9use anyhow::Result;
10use serde_json::{json, Value};
11use std::path::PathBuf;
12
13#[derive(Clone)]
14pub struct DeleteFileTool {
15 file_manager: FileAccessManager,
16}
17
18impl DeleteFileTool {
19 pub fn tool_name() -> ToolName {
20 ToolName::new("delete_file")
21 }
22
23 pub fn new(workspace_roots: Vec<PathBuf>) -> anyhow::Result<Self> {
24 let file_manager = FileAccessManager::new(workspace_roots)?;
25 Ok(Self { file_manager })
26 }
27}
28
29struct DeleteFileHandle {
30 file_path: String,
31 original_content: Option<String>,
32 tool_use_id: String,
33 file_manager: FileAccessManager,
34}
35
36#[async_trait::async_trait(?Send)]
37impl ToolCallHandle for DeleteFileHandle {
38 fn tool_request(&self) -> ToolRequestEvent {
39 ToolRequestEvent {
40 tool_call_id: self.tool_use_id.clone(),
41 tool_name: "delete_file".to_string(),
42 tool_type: ToolRequestType::ModifyFile {
43 file_path: self.file_path.clone(),
44 before: self.original_content.clone().unwrap_or_default(),
45 after: String::new(),
46 },
47 }
48 }
49
50 async fn execute(self: Box<Self>) -> ToolOutput {
51 let modification = FileModification {
52 path: PathBuf::from(&self.file_path),
53 operation: FileOperation::Delete,
54 original_content: self.original_content,
55 new_content: None,
56 warning: None,
57 };
58
59 let manager = FileModificationManager::new(self.file_manager);
60 match manager.apply_modification(modification).await {
61 Ok(stats) => ToolOutput::Result {
62 content: json!({
63 "success": true,
64 "path": self.file_path,
65 "lines_removed": stats.lines_removed
66 })
67 .to_string(),
68 is_error: false,
69 continuation: ContinuationPreference::Continue,
70 ui_result: ToolExecutionResult::Other {
71 result: json!({
72 "deleted": true,
73 "path": self.file_path,
74 "lines_removed": stats.lines_removed
75 }),
76 },
77 },
78 Err(e) => ToolOutput::Result {
79 content: format!("Failed to delete file: {e:?}"),
80 is_error: true,
81 continuation: ContinuationPreference::Continue,
82 ui_result: ToolExecutionResult::Error {
83 short_message: "Delete failed".to_string(),
84 detailed_message: format!("{e:?}"),
85 },
86 },
87 }
88 }
89}
90
91#[async_trait::async_trait(?Send)]
92impl ToolExecutor for DeleteFileTool {
93 fn name(&self) -> String {
94 "delete_file".to_string()
95 }
96
97 fn description(&self) -> String {
98 "Delete a file or empty directory".to_string()
99 }
100
101 fn input_schema(&self) -> Value {
102 json!({
103 "type": "object",
104 "properties": {
105 "file_path": {
106 "type": "string",
107 "description": "Path to the file or directory to delete"
108 }
109 },
110 "required": ["file_path"]
111 })
112 }
113
114 fn category(&self) -> ToolCategory {
115 ToolCategory::Execution
116 }
117
118 async fn process(&self, request: &ToolRequest) -> Result<Box<dyn ToolCallHandle>> {
119 let file_path = request
120 .arguments
121 .get("file_path")
122 .and_then(|v| v.as_str())
123 .ok_or_else(|| anyhow::anyhow!("Missing required parameter: file_path"))?;
124
125 let original_content = self.file_manager.read_file(file_path).await.ok();
126
127 Ok(Box::new(DeleteFileHandle {
128 file_path: file_path.to_string(),
129 original_content,
130 tool_use_id: request.tool_use_id.clone(),
131 file_manager: self.file_manager.clone(),
132 }))
133 }
134}