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