llama_cpp_v3_agent_sdk/tools/
edit_file.rs1use crate::error::AgentError;
2use crate::tool::{Tool, ToolResult};
3
4pub struct EditFileTool;
6
7impl Tool for EditFileTool {
8 fn name(&self) -> &str {
9 "edit"
10 }
11
12 fn description(&self) -> &str {
13 "Edit a file by searching for an exact string and replacing it. \
14 The 'old' string must match exactly (including whitespace). \
15 Only the first occurrence is replaced unless 'all' is set to true."
16 }
17
18 fn parameters_schema(&self) -> serde_json::Value {
19 serde_json::json!({
20 "type": "object",
21 "properties": {
22 "path": {
23 "type": "string",
24 "description": "Path to the file to edit"
25 },
26 "old": {
27 "type": "string",
28 "description": "The exact text to search for"
29 },
30 "new": {
31 "type": "string",
32 "description": "The replacement text"
33 },
34 "all": {
35 "type": "boolean",
36 "description": "If true, replace all occurrences. Default: false."
37 }
38 },
39 "required": ["path", "old", "new"]
40 })
41 }
42
43 fn execute(&self, args: &serde_json::Value) -> Result<ToolResult, AgentError> {
44 let path = args["path"].as_str().ok_or_else(|| AgentError::Tool {
45 tool: "edit".to_string(),
46 message: "Missing 'path' argument".to_string(),
47 })?;
48
49 let old = args["old"].as_str().ok_or_else(|| AgentError::Tool {
50 tool: "edit".to_string(),
51 message: "Missing 'old' argument".to_string(),
52 })?;
53
54 let new = args["new"].as_str().ok_or_else(|| AgentError::Tool {
55 tool: "edit".to_string(),
56 message: "Missing 'new' argument".to_string(),
57 })?;
58
59 let replace_all = args["all"].as_bool().unwrap_or(false);
60
61 let content = match std::fs::read_to_string(path) {
62 Ok(c) => c,
63 Err(e) => {
64 return Ok(ToolResult::err(format!(
65 "Failed to read '{}': {}",
66 path, e
67 )))
68 }
69 };
70
71 if !content.contains(old) {
72 return Ok(ToolResult::err(format!(
73 "Search string not found in '{}'. Make sure the 'old' text matches exactly (including whitespace and newlines).",
74 path
75 )));
76 }
77
78 let (new_content, count) = if replace_all {
79 let count = content.matches(old).count();
80 (content.replace(old, new), count)
81 } else {
82 (content.replacen(old, new, 1), 1)
83 };
84
85 match std::fs::write(path, &new_content) {
86 Ok(_) => Ok(ToolResult::ok(format!(
87 "Replaced {} occurrence(s) in '{}'",
88 count, path
89 ))),
90 Err(e) => Ok(ToolResult::err(format!(
91 "Failed to write '{}': {}",
92 path, e
93 ))),
94 }
95 }
96
97 fn requires_permission(&self) -> bool {
98 true
99 }
100}