steer_core/tools/static_tools/
edit.rs1use super::workspace_op_error;
2use crate::tools::capability::Capabilities;
3use crate::tools::static_tool::{StaticTool, StaticToolContext, StaticToolError};
4use async_trait::async_trait;
5use steer_tools::result::{EditResult, MultiEditResult};
6use steer_tools::tools::edit::multi_edit::{MultiEditError, MultiEditParams, MultiEditToolSpec};
7use steer_tools::tools::edit::{EditError, EditParams, EditToolSpec};
8use steer_workspace::{ApplyEditsRequest, EditOperation, WorkspaceOpContext};
9
10pub struct EditTool;
11
12const EDIT_DESCRIPTION: &str = r"This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the replace tool to overwrite files.
13
14Before using this tool:
15
161. Use the View tool to understand the file's contents and context
17
182. Verify the directory path is correct (only applicable when creating new files):
19 - Use the LS tool to verify the parent directory exists and is the correct location
20
21To make a file edit, provide the following:
221. file_path: The absolute path to the file to modify (must be absolute, not relative)
232. old_string: The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)
243. new_string: The edited text to replace the old_string
25
26The tool will replace ONE occurrence of old_string with new_string in the specified file.
27
28CRITICAL REQUIREMENTS FOR USING THIS TOOL:
29
301. UNIQUENESS: The old_string MUST uniquely identify the specific instance you want to change. This means:
31 - Include AT LEAST 3-5 lines of context BEFORE the change point
32 - Include AT LEAST 3-5 lines of context AFTER the change point
33 - Include all whitespace, indentation, and surrounding code exactly as it appears in the file
34
352. SINGLE INSTANCE: This tool can only change ONE instance at a time. If you need to change multiple instances:
36 - Make separate calls to this tool for each instance
37 - Each call must uniquely identify its specific instance using extensive context
38
393. VERIFICATION: Before using this tool:
40 - Check how many instances of the target text exist in the file
41 - If multiple instances exist, gather enough context to uniquely identify each one
42 - Plan separate tool calls for each instance
43
44WARNING: If you do not follow these requirements:
45 - The tool will fail if old_string matches multiple locations
46 - The tool will fail if old_string doesn't match exactly (including whitespace)
47 - You may change the wrong instance if you don't include enough context
48
49When making edits:
50 - Ensure the edit results in idiomatic, correct code
51 - Do not leave the code in a broken state
52 - Always use absolute file paths (starting with /)
53
54If you want to create a new file, use:
55 - A new file path, including dir name if needed
56 - An empty old_string
57 - The new file's contents as new_string
58
59Remember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.";
60
61#[async_trait]
62impl StaticTool for EditTool {
63 type Params = EditParams;
64 type Output = EditResult;
65 type Spec = EditToolSpec;
66
67 const DESCRIPTION: &'static str = EDIT_DESCRIPTION;
68 const REQUIRES_APPROVAL: bool = true;
69 const REQUIRED_CAPABILITIES: Capabilities = Capabilities::WORKSPACE;
70
71 async fn execute(
72 &self,
73 params: Self::Params,
74 ctx: &StaticToolContext,
75 ) -> Result<Self::Output, StaticToolError<EditError>> {
76 let request = ApplyEditsRequest {
77 file_path: params.file_path,
78 edits: vec![EditOperation {
79 old_string: params.old_string,
80 new_string: params.new_string,
81 }],
82 };
83 let op_ctx =
84 WorkspaceOpContext::new(ctx.tool_call_id.0.clone(), ctx.cancellation_token.clone());
85 ctx.services
86 .workspace
87 .apply_edits(request, &op_ctx)
88 .await
89 .map_err(|e| StaticToolError::execution(EditError::Workspace(workspace_op_error(e))))
90 }
91}
92
93pub struct MultiEditTool;
94
95#[async_trait]
96impl StaticTool for MultiEditTool {
97 type Params = MultiEditParams;
98 type Output = MultiEditResult;
99 type Spec = MultiEditToolSpec;
100
101 const DESCRIPTION: &'static str = "This is a tool for making multiple edits to a single file in one operation. Prefer this tool over the edit_file tool when you need to make multiple edits to the same file.";
102 const REQUIRES_APPROVAL: bool = true;
103 const REQUIRED_CAPABILITIES: Capabilities = Capabilities::WORKSPACE;
104
105 async fn execute(
106 &self,
107 params: Self::Params,
108 ctx: &StaticToolContext,
109 ) -> Result<Self::Output, StaticToolError<MultiEditError>> {
110 let request = ApplyEditsRequest {
111 file_path: params.file_path,
112 edits: params
113 .edits
114 .into_iter()
115 .map(|e| EditOperation {
116 old_string: e.old_string,
117 new_string: e.new_string,
118 })
119 .collect(),
120 };
121 let op_ctx =
122 WorkspaceOpContext::new(ctx.tool_call_id.0.clone(), ctx.cancellation_token.clone());
123 let result = ctx
124 .services
125 .workspace
126 .apply_edits(request, &op_ctx)
127 .await
128 .map_err(|e| {
129 StaticToolError::execution(MultiEditError::Workspace(workspace_op_error(e)))
130 })?;
131 Ok(MultiEditResult(result))
132 }
133}