codetether_agent/tool/
mod.rs1pub mod advanced_edit;
6pub mod agent;
7pub mod avatar;
8pub mod bash;
9pub mod batch;
10pub mod codesearch;
11pub mod confirm_edit;
12pub mod confirm_multiedit;
13pub mod edit;
14pub mod file;
15pub mod file_extras;
16pub mod go;
17pub mod image;
18pub mod invalid;
19pub mod lsp;
20pub mod mcp_bridge;
21pub mod mcp_tools;
22pub mod memory;
23pub mod multiedit;
24pub mod okr;
25pub mod patch;
26pub mod plan;
27pub mod podcast;
28pub mod prd;
29pub mod question;
30pub mod ralph;
31pub mod relay_autochat;
32pub mod rlm;
33pub mod sandbox;
34pub mod search;
35pub mod skill;
36pub mod swarm_execute;
37pub mod swarm_share;
38pub mod task;
39pub mod todo;
40pub mod undo;
41pub mod voice;
42pub mod webfetch;
43pub mod websearch;
44pub mod youtube;
45
46use anyhow::Result;
47use async_trait::async_trait;
48use serde::{Deserialize, Serialize};
49use serde_json::Value;
50use std::collections::HashMap;
51use std::sync::Arc;
52
53use crate::provider::Provider;
54pub use mcp_tools::{McpToolManager, McpToolWrapper};
55pub use sandbox::{PluginManifest, PluginRegistry, SigningKey, hash_bytes, hash_file};
56
57#[async_trait]
59pub trait Tool: Send + Sync {
60 fn id(&self) -> &str;
62
63 fn name(&self) -> &str;
65
66 fn description(&self) -> &str;
68
69 fn parameters(&self) -> Value;
71
72 async fn execute(&self, args: Value) -> Result<ToolResult>;
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ToolResult {
79 pub output: String,
80 pub success: bool,
81 #[serde(default)]
82 pub metadata: HashMap<String, Value>,
83}
84
85impl ToolResult {
86 pub fn success(output: impl Into<String>) -> Self {
87 Self {
88 output: output.into(),
89 success: true,
90 metadata: HashMap::new(),
91 }
92 }
93
94 pub fn error(message: impl Into<String>) -> Self {
95 Self {
96 output: message.into(),
97 success: false,
98 metadata: HashMap::new(),
99 }
100 }
101
102 pub fn structured_error(
106 code: &str,
107 tool: &str,
108 message: &str,
109 missing_fields: Option<Vec<&str>>,
110 example: Option<Value>,
111 ) -> Self {
112 let mut error_obj = serde_json::json!({
113 "code": code,
114 "tool": tool,
115 "message": message,
116 });
117
118 if let Some(fields) = missing_fields {
119 error_obj["missing_fields"] = serde_json::json!(fields);
120 }
121
122 if let Some(ex) = example {
123 error_obj["example"] = ex;
124 }
125
126 let output = serde_json::to_string_pretty(&serde_json::json!({
127 "error": error_obj
128 }))
129 .unwrap_or_else(|_| format!("Error: {}", message));
130
131 let mut metadata = HashMap::new();
132 metadata.insert("error_code".to_string(), serde_json::json!(code));
133 metadata.insert("tool".to_string(), serde_json::json!(tool));
134
135 Self {
136 output,
137 success: false,
138 metadata,
139 }
140 }
141
142 pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
143 self.metadata.insert(key.into(), value);
144 self
145 }
146}
147
148pub struct ToolRegistry {
150 tools: HashMap<String, Arc<dyn Tool>>,
151 plugin_registry: PluginRegistry,
152}
153
154impl std::fmt::Debug for ToolRegistry {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 f.debug_struct("ToolRegistry")
157 .field("tools", &self.tools.keys().collect::<Vec<_>>())
158 .finish()
159 }
160}
161
162impl ToolRegistry {
163 pub fn new() -> Self {
164 let _ = std::any::TypeId::of::<McpToolManager>();
165 let _ = std::any::TypeId::of::<McpToolWrapper>();
166 Self {
167 tools: HashMap::new(),
168 plugin_registry: PluginRegistry::from_env(),
169 }
170 }
171
172 pub fn plugins(&self) -> &PluginRegistry {
174 &self.plugin_registry
175 }
176
177 pub fn register(&mut self, tool: Arc<dyn Tool>) {
179 self.tools.insert(tool.id().to_string(), tool);
180 }
181
182 pub fn get(&self, id: &str) -> Option<Arc<dyn Tool>> {
184 self.tools.get(id).cloned()
185 }
186
187 pub fn list(&self) -> Vec<&str> {
189 self.tools.keys().map(|s| s.as_str()).collect()
190 }
191
192 pub fn definitions(&self) -> Vec<crate::provider::ToolDefinition> {
194 self.tools
195 .values()
196 .map(|t| crate::provider::ToolDefinition {
197 name: t.id().to_string(),
198 description: t.description().to_string(),
199 parameters: t.parameters(),
200 })
201 .collect()
202 }
203
204 pub fn register_all(&mut self, tools: Vec<Arc<dyn Tool>>) {
206 for tool in tools {
207 self.register(tool);
208 }
209 }
210
211 pub fn unregister(&mut self, id: &str) -> Option<Arc<dyn Tool>> {
213 self.tools.remove(id)
214 }
215
216 pub fn contains(&self, id: &str) -> bool {
218 self.tools.contains_key(id)
219 }
220
221 pub fn len(&self) -> usize {
223 self.tools.len()
224 }
225
226 pub fn is_empty(&self) -> bool {
228 self.tools.is_empty()
229 }
230
231 pub fn with_defaults() -> Self {
233 let mut registry = Self::new();
234
235 registry.register(Arc::new(file::ReadTool::new()));
236 registry.register(Arc::new(file::WriteTool::new()));
237 registry.register(Arc::new(file::ListTool::new()));
238 registry.register(Arc::new(file::GlobTool::new()));
239 registry.register(Arc::new(file_extras::TreeTool::new()));
240 registry.register(Arc::new(file_extras::FileInfoTool::new()));
241 registry.register(Arc::new(file_extras::HeadTailTool::new()));
242 registry.register(Arc::new(file_extras::DiffTool::new()));
243 registry.register(Arc::new(search::GrepTool::new()));
244 registry.register(Arc::new(advanced_edit::AdvancedEditTool::new()));
245 registry.register(Arc::new(bash::BashTool::new()));
246 registry.register(Arc::new(lsp::LspTool::with_root(
247 std::env::current_dir()
248 .map(|p| format!("file://{}", p.display()))
249 .unwrap_or_default(),
250 )));
251 registry.register(Arc::new(webfetch::WebFetchTool::new()));
252 registry.register(Arc::new(multiedit::MultiEditTool::new()));
253 registry.register(Arc::new(websearch::WebSearchTool::new()));
254 registry.register(Arc::new(codesearch::CodeSearchTool::new()));
255 registry.register(Arc::new(patch::ApplyPatchTool::new()));
256 registry.register(Arc::new(todo::TodoReadTool::new()));
257 registry.register(Arc::new(todo::TodoWriteTool::new()));
258 registry.register(Arc::new(question::QuestionTool::new()));
259 registry.register(Arc::new(task::TaskTool::new()));
260 registry.register(Arc::new(plan::PlanEnterTool::new()));
261 registry.register(Arc::new(plan::PlanExitTool::new()));
262 registry.register(Arc::new(skill::SkillTool::new()));
263 registry.register(Arc::new(memory::MemoryTool::new()));
264 registry.register(Arc::new(ralph::RalphTool::new()));
265 registry.register(Arc::new(prd::PrdTool::new()));
266 registry.register(Arc::new(undo::UndoTool));
267 registry.register(Arc::new(voice::VoiceTool::new()));
268 registry.register(Arc::new(podcast::PodcastTool::new()));
269 registry.register(Arc::new(youtube::YouTubeTool::new()));
270 registry.register(Arc::new(avatar::AvatarTool::new()));
271 registry.register(Arc::new(image::ImageTool::new()));
272 registry.register(Arc::new(mcp_bridge::McpBridgeTool::new()));
273 registry.register(Arc::new(okr::OkrTool::new()));
274 registry.register(Arc::new(invalid::InvalidTool::new()));
276 registry.register(Arc::new(agent::AgentTool::new()));
278 registry.register(Arc::new(swarm_execute::SwarmExecuteTool::new()));
280 registry.register(Arc::new(relay_autochat::RelayAutoChatTool::new()));
282 registry.register(Arc::new(go::GoTool::new()));
284
285 registry
286 }
287
288 pub fn with_provider(provider: Arc<dyn Provider>, model: String) -> Self {
290 let mut registry = Self::new();
291
292 registry.register(Arc::new(file::ReadTool::new()));
293 registry.register(Arc::new(file::WriteTool::new()));
294 registry.register(Arc::new(file::ListTool::new()));
295 registry.register(Arc::new(file::GlobTool::new()));
296 registry.register(Arc::new(file_extras::TreeTool::new()));
297 registry.register(Arc::new(file_extras::FileInfoTool::new()));
298 registry.register(Arc::new(file_extras::HeadTailTool::new()));
299 registry.register(Arc::new(file_extras::DiffTool::new()));
300 registry.register(Arc::new(search::GrepTool::new()));
301 registry.register(Arc::new(advanced_edit::AdvancedEditTool::new()));
302 registry.register(Arc::new(bash::BashTool::new()));
303 registry.register(Arc::new(lsp::LspTool::with_root(
304 std::env::current_dir()
305 .map(|p| format!("file://{}", p.display()))
306 .unwrap_or_default(),
307 )));
308 registry.register(Arc::new(webfetch::WebFetchTool::new()));
309 registry.register(Arc::new(multiedit::MultiEditTool::new()));
310 registry.register(Arc::new(websearch::WebSearchTool::new()));
311 registry.register(Arc::new(codesearch::CodeSearchTool::new()));
312 registry.register(Arc::new(patch::ApplyPatchTool::new()));
313 registry.register(Arc::new(todo::TodoReadTool::new()));
314 registry.register(Arc::new(todo::TodoWriteTool::new()));
315 registry.register(Arc::new(question::QuestionTool::new()));
316 registry.register(Arc::new(task::TaskTool::new()));
317 registry.register(Arc::new(plan::PlanEnterTool::new()));
318 registry.register(Arc::new(plan::PlanExitTool::new()));
319 registry.register(Arc::new(skill::SkillTool::new()));
320 registry.register(Arc::new(memory::MemoryTool::new()));
321 registry.register(Arc::new(rlm::RlmTool::new(
322 Arc::clone(&provider),
323 model.clone(),
324 )));
325 registry.register(Arc::new(ralph::RalphTool::with_provider(provider, model)));
327 registry.register(Arc::new(prd::PrdTool::new()));
328 registry.register(Arc::new(undo::UndoTool));
329 registry.register(Arc::new(voice::VoiceTool::new()));
330 registry.register(Arc::new(podcast::PodcastTool::new()));
331 registry.register(Arc::new(youtube::YouTubeTool::new()));
332 registry.register(Arc::new(avatar::AvatarTool::new()));
333 registry.register(Arc::new(image::ImageTool::new()));
334 registry.register(Arc::new(mcp_bridge::McpBridgeTool::new()));
335 registry.register(Arc::new(okr::OkrTool::new()));
336 registry.register(Arc::new(invalid::InvalidTool::new()));
338 registry.register(Arc::new(agent::AgentTool::new()));
340 registry.register(Arc::new(swarm_execute::SwarmExecuteTool::new()));
342 registry.register(Arc::new(relay_autochat::RelayAutoChatTool::new()));
344 registry.register(Arc::new(go::GoTool::new()));
346
347 registry
348 }
349
350 pub fn with_defaults_arc() -> Arc<Self> {
354 let mut registry = Self::with_defaults();
355
356 let batch_tool = Arc::new(batch::BatchTool::new());
358 registry.register(batch_tool.clone());
359
360 let registry = Arc::new(registry);
362
363 batch_tool.set_registry(Arc::downgrade(®istry));
365
366 registry
367 }
368
369 #[allow(dead_code)]
373 pub fn with_provider_arc(provider: Arc<dyn Provider>, model: String) -> Arc<Self> {
374 let mut registry = Self::with_provider(provider, model);
375
376 let batch_tool = Arc::new(batch::BatchTool::new());
378 registry.register(batch_tool.clone());
379
380 let registry = Arc::new(registry);
382
383 batch_tool.set_registry(Arc::downgrade(®istry));
385
386 registry
387 }
388}
389
390impl Default for ToolRegistry {
391 fn default() -> Self {
392 Self::with_defaults()
393 }
394}