Skip to main content

tycode_core/file/modify/
delete_file.rs

1use 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}