codetether_agent/tool/
mod.rs1pub mod bash;
6pub mod batch;
7pub mod codesearch;
8pub mod confirm_edit;
9pub mod confirm_multiedit;
10pub mod edit;
11pub mod file;
12pub mod invalid;
13pub mod lsp;
14pub mod multiedit;
15pub mod patch;
16pub mod plan;
17pub mod prd;
18pub mod question;
19pub mod ralph;
20pub mod rlm;
21pub mod search;
22pub mod skill;
23pub mod task;
24pub mod todo;
25pub mod undo;
26pub mod webfetch;
27pub mod websearch;
28
29use anyhow::Result;
30use async_trait::async_trait;
31use serde::{Deserialize, Serialize};
32use serde_json::Value;
33use std::collections::HashMap;
34use std::sync::Arc;
35
36use crate::provider::Provider;
37
38#[async_trait]
40pub trait Tool: Send + Sync {
41 fn id(&self) -> &str;
43
44 fn name(&self) -> &str;
46
47 fn description(&self) -> &str;
49
50 fn parameters(&self) -> Value;
52
53 async fn execute(&self, args: Value) -> Result<ToolResult>;
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ToolResult {
60 pub output: String,
61 pub success: bool,
62 #[serde(default)]
63 pub metadata: HashMap<String, Value>,
64}
65
66impl ToolResult {
67 pub fn success(output: impl Into<String>) -> Self {
68 Self {
69 output: output.into(),
70 success: true,
71 metadata: HashMap::new(),
72 }
73 }
74
75 pub fn error(message: impl Into<String>) -> Self {
76 Self {
77 output: message.into(),
78 success: false,
79 metadata: HashMap::new(),
80 }
81 }
82
83 pub fn structured_error(
87 code: &str,
88 tool: &str,
89 message: &str,
90 missing_fields: Option<Vec<&str>>,
91 example: Option<Value>,
92 ) -> Self {
93 let mut error_obj = serde_json::json!({
94 "code": code,
95 "tool": tool,
96 "message": message,
97 });
98
99 if let Some(fields) = missing_fields {
100 error_obj["missing_fields"] = serde_json::json!(fields);
101 }
102
103 if let Some(ex) = example {
104 error_obj["example"] = ex;
105 }
106
107 let output = serde_json::to_string_pretty(&serde_json::json!({
108 "error": error_obj
109 }))
110 .unwrap_or_else(|_| format!("Error: {}", message));
111
112 let mut metadata = HashMap::new();
113 metadata.insert("error_code".to_string(), serde_json::json!(code));
114 metadata.insert("tool".to_string(), serde_json::json!(tool));
115
116 Self {
117 output,
118 success: false,
119 metadata,
120 }
121 }
122
123 pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
124 self.metadata.insert(key.into(), value);
125 self
126 }
127}
128
129pub struct ToolRegistry {
131 tools: HashMap<String, Arc<dyn Tool>>,
132}
133
134impl std::fmt::Debug for ToolRegistry {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 f.debug_struct("ToolRegistry")
137 .field("tools", &self.tools.keys().collect::<Vec<_>>())
138 .finish()
139 }
140}
141
142impl ToolRegistry {
143 pub fn new() -> Self {
144 Self {
145 tools: HashMap::new(),
146 }
147 }
148
149 pub fn register(&mut self, tool: Arc<dyn Tool>) {
151 self.tools.insert(tool.id().to_string(), tool);
152 }
153
154 pub fn get(&self, id: &str) -> Option<Arc<dyn Tool>> {
156 self.tools.get(id).cloned()
157 }
158
159 pub fn list(&self) -> Vec<&str> {
161 self.tools.keys().map(|s| s.as_str()).collect()
162 }
163
164 pub fn definitions(&self) -> Vec<crate::provider::ToolDefinition> {
166 self.tools
167 .values()
168 .map(|t| crate::provider::ToolDefinition {
169 name: t.id().to_string(),
170 description: t.description().to_string(),
171 parameters: t.parameters(),
172 })
173 .collect()
174 }
175
176 pub fn with_defaults() -> Self {
178 let mut registry = Self::new();
179
180 registry.register(Arc::new(file::ReadTool::new()));
181 registry.register(Arc::new(file::WriteTool::new()));
182 registry.register(Arc::new(file::ListTool::new()));
183 registry.register(Arc::new(file::GlobTool::new()));
184 registry.register(Arc::new(search::GrepTool::new()));
185 registry.register(Arc::new(edit::EditTool::new()));
186 registry.register(Arc::new(bash::BashTool::new()));
187 registry.register(Arc::new(lsp::LspTool::new()));
188 registry.register(Arc::new(webfetch::WebFetchTool::new()));
189 registry.register(Arc::new(multiedit::MultiEditTool::new()));
190 registry.register(Arc::new(websearch::WebSearchTool::new()));
191 registry.register(Arc::new(codesearch::CodeSearchTool::new()));
192 registry.register(Arc::new(patch::ApplyPatchTool::new()));
193 registry.register(Arc::new(todo::TodoReadTool::new()));
194 registry.register(Arc::new(todo::TodoWriteTool::new()));
195 registry.register(Arc::new(question::QuestionTool::new()));
196 registry.register(Arc::new(task::TaskTool::new()));
197 registry.register(Arc::new(plan::PlanEnterTool::new()));
198 registry.register(Arc::new(plan::PlanExitTool::new()));
199 registry.register(Arc::new(skill::SkillTool::new()));
200 registry.register(Arc::new(rlm::RlmTool::new()));
201 registry.register(Arc::new(ralph::RalphTool::new()));
202 registry.register(Arc::new(prd::PrdTool::new()));
203 registry.register(Arc::new(confirm_edit::ConfirmEditTool::new()));
204 registry.register(Arc::new(confirm_multiedit::ConfirmMultiEditTool::new()));
205 registry.register(Arc::new(undo::UndoTool));
206 registry.register(Arc::new(invalid::InvalidTool::new()));
208
209 registry
210 }
211
212 pub fn with_provider(provider: Arc<dyn Provider>, model: String) -> Self {
214 let mut registry = Self::new();
215
216 registry.register(Arc::new(file::ReadTool::new()));
217 registry.register(Arc::new(file::WriteTool::new()));
218 registry.register(Arc::new(file::ListTool::new()));
219 registry.register(Arc::new(file::GlobTool::new()));
220 registry.register(Arc::new(search::GrepTool::new()));
221 registry.register(Arc::new(edit::EditTool::new()));
222 registry.register(Arc::new(bash::BashTool::new()));
223 registry.register(Arc::new(lsp::LspTool::new()));
224 registry.register(Arc::new(webfetch::WebFetchTool::new()));
225 registry.register(Arc::new(multiedit::MultiEditTool::new()));
226 registry.register(Arc::new(websearch::WebSearchTool::new()));
227 registry.register(Arc::new(codesearch::CodeSearchTool::new()));
228 registry.register(Arc::new(patch::ApplyPatchTool::new()));
229 registry.register(Arc::new(todo::TodoReadTool::new()));
230 registry.register(Arc::new(todo::TodoWriteTool::new()));
231 registry.register(Arc::new(question::QuestionTool::new()));
232 registry.register(Arc::new(task::TaskTool::new()));
233 registry.register(Arc::new(plan::PlanEnterTool::new()));
234 registry.register(Arc::new(plan::PlanExitTool::new()));
235 registry.register(Arc::new(skill::SkillTool::new()));
236 registry.register(Arc::new(rlm::RlmTool::new()));
237 registry.register(Arc::new(ralph::RalphTool::with_provider(provider, model)));
239 registry.register(Arc::new(prd::PrdTool::new()));
240 registry.register(Arc::new(undo::UndoTool));
241 registry.register(Arc::new(invalid::InvalidTool::new()));
243
244 registry
245 }
246
247 pub fn with_defaults_arc() -> Arc<Self> {
251 let mut registry = Self::with_defaults();
252
253 let batch_tool = Arc::new(batch::BatchTool::new());
255 registry.register(batch_tool.clone());
256
257 let registry = Arc::new(registry);
259
260 batch_tool.set_registry(Arc::downgrade(®istry));
262
263 registry
264 }
265
266 #[allow(dead_code)]
270 pub fn with_provider_arc(provider: Arc<dyn Provider>, model: String) -> Arc<Self> {
271 let mut registry = Self::with_provider(provider, model);
272
273 let batch_tool = Arc::new(batch::BatchTool::new());
275 registry.register(batch_tool.clone());
276
277 let registry = Arc::new(registry);
279
280 batch_tool.set_registry(Arc::downgrade(®istry));
282
283 registry
284 }
285}
286
287impl Default for ToolRegistry {
288 fn default() -> Self {
289 Self::with_defaults()
290 }
291}