Skip to main content

codetether_agent/tool/
mod.rs

1//! Tool system
2//!
3//! Tools are the executable capabilities available to agents.
4
5pub mod bash;
6pub mod batch;
7pub mod codesearch;
8pub mod edit;
9pub mod file;
10pub mod invalid;
11pub mod lsp;
12pub mod multiedit;
13pub mod patch;
14pub mod plan;
15pub mod prd;
16pub mod question;
17pub mod ralph;
18pub mod rlm;
19pub mod search;
20pub mod skill;
21pub mod task;
22pub mod todo;
23pub mod webfetch;
24pub mod websearch;
25
26use anyhow::Result;
27use async_trait::async_trait;
28use serde::{Deserialize, Serialize};
29use serde_json::Value;
30use std::collections::HashMap;
31use std::sync::Arc;
32
33use crate::provider::Provider;
34
35/// A tool that can be executed by an agent
36#[async_trait]
37pub trait Tool: Send + Sync {
38    /// Tool identifier
39    fn id(&self) -> &str;
40
41    /// Human-readable name
42    fn name(&self) -> &str;
43
44    /// Description for the LLM
45    fn description(&self) -> &str;
46
47    /// JSON Schema for parameters
48    fn parameters(&self) -> Value;
49
50    /// Execute the tool with given arguments
51    async fn execute(&self, args: Value) -> Result<ToolResult>;
52}
53
54/// Result from tool execution
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ToolResult {
57    pub output: String,
58    pub success: bool,
59    #[serde(default)]
60    pub metadata: HashMap<String, Value>,
61}
62
63impl ToolResult {
64    pub fn success(output: impl Into<String>) -> Self {
65        Self {
66            output: output.into(),
67            success: true,
68            metadata: HashMap::new(),
69        }
70    }
71
72    pub fn error(message: impl Into<String>) -> Self {
73        Self {
74            output: message.into(),
75            success: false,
76            metadata: HashMap::new(),
77        }
78    }
79
80    pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
81        self.metadata.insert(key.into(), value);
82        self
83    }
84}
85
86/// Registry of available tools
87pub struct ToolRegistry {
88    tools: HashMap<String, Arc<dyn Tool>>,
89}
90
91impl ToolRegistry {
92    pub fn new() -> Self {
93        Self {
94            tools: HashMap::new(),
95        }
96    }
97
98    /// Register a tool
99    pub fn register(&mut self, tool: Arc<dyn Tool>) {
100        self.tools.insert(tool.id().to_string(), tool);
101    }
102
103    /// Get a tool by ID
104    pub fn get(&self, id: &str) -> Option<Arc<dyn Tool>> {
105        self.tools.get(id).cloned()
106    }
107
108    /// List all tool IDs
109    pub fn list(&self) -> Vec<&str> {
110        self.tools.keys().map(|s| s.as_str()).collect()
111    }
112
113    /// Get tool definitions for LLM
114    pub fn definitions(&self) -> Vec<crate::provider::ToolDefinition> {
115        self.tools
116            .values()
117            .map(|t| crate::provider::ToolDefinition {
118                name: t.id().to_string(),
119                description: t.description().to_string(),
120                parameters: t.parameters(),
121            })
122            .collect()
123    }
124
125    /// Create registry with all default tools (without batch)
126    pub fn with_defaults() -> Self {
127        let mut registry = Self::new();
128        
129        registry.register(Arc::new(file::ReadTool::new()));
130        registry.register(Arc::new(file::WriteTool::new()));
131        registry.register(Arc::new(file::ListTool::new()));
132        registry.register(Arc::new(file::GlobTool::new()));
133        registry.register(Arc::new(search::GrepTool::new()));
134        registry.register(Arc::new(edit::EditTool::new()));
135        registry.register(Arc::new(bash::BashTool::new()));
136        registry.register(Arc::new(lsp::LspTool::new()));
137        registry.register(Arc::new(webfetch::WebFetchTool::new()));
138        registry.register(Arc::new(multiedit::MultiEditTool::new()));
139        registry.register(Arc::new(websearch::WebSearchTool::new()));
140        registry.register(Arc::new(codesearch::CodeSearchTool::new()));
141        registry.register(Arc::new(patch::ApplyPatchTool::new()));
142        registry.register(Arc::new(todo::TodoReadTool::new()));
143        registry.register(Arc::new(todo::TodoWriteTool::new()));
144        registry.register(Arc::new(question::QuestionTool::new()));
145        registry.register(Arc::new(task::TaskTool::new()));
146        registry.register(Arc::new(plan::PlanEnterTool::new()));
147        registry.register(Arc::new(plan::PlanExitTool::new()));
148        registry.register(Arc::new(skill::SkillTool::new()));
149        registry.register(Arc::new(rlm::RlmTool::new()));
150        registry.register(Arc::new(ralph::RalphTool::new()));
151        registry.register(Arc::new(prd::PrdTool::new()));
152        // Register the invalid tool handler for graceful error handling
153        registry.register(Arc::new(invalid::InvalidTool::new()));
154        
155        registry
156    }
157
158    /// Create registry with provider for tools that need it (like RalphTool)
159    pub fn with_provider(provider: Arc<dyn Provider>, model: String) -> Self {
160        let mut registry = Self::new();
161        
162        registry.register(Arc::new(file::ReadTool::new()));
163        registry.register(Arc::new(file::WriteTool::new()));
164        registry.register(Arc::new(file::ListTool::new()));
165        registry.register(Arc::new(file::GlobTool::new()));
166        registry.register(Arc::new(search::GrepTool::new()));
167        registry.register(Arc::new(edit::EditTool::new()));
168        registry.register(Arc::new(bash::BashTool::new()));
169        registry.register(Arc::new(lsp::LspTool::new()));
170        registry.register(Arc::new(webfetch::WebFetchTool::new()));
171        registry.register(Arc::new(multiedit::MultiEditTool::new()));
172        registry.register(Arc::new(websearch::WebSearchTool::new()));
173        registry.register(Arc::new(codesearch::CodeSearchTool::new()));
174        registry.register(Arc::new(patch::ApplyPatchTool::new()));
175        registry.register(Arc::new(todo::TodoReadTool::new()));
176        registry.register(Arc::new(todo::TodoWriteTool::new()));
177        registry.register(Arc::new(question::QuestionTool::new()));
178        registry.register(Arc::new(task::TaskTool::new()));
179        registry.register(Arc::new(plan::PlanEnterTool::new()));
180        registry.register(Arc::new(plan::PlanExitTool::new()));
181        registry.register(Arc::new(skill::SkillTool::new()));
182        registry.register(Arc::new(rlm::RlmTool::new()));
183        // RalphTool with provider for autonomous execution
184        registry.register(Arc::new(ralph::RalphTool::with_provider(provider, model)));
185        registry.register(Arc::new(prd::PrdTool::new()));
186        // Register the invalid tool handler for graceful error handling
187        registry.register(Arc::new(invalid::InvalidTool::new()));
188        
189        registry
190    }
191
192    /// Create Arc-wrapped registry with batch tool properly initialized.
193    /// The batch tool needs a weak reference to the registry, so we use
194    /// a two-phase initialization pattern.
195    pub fn with_defaults_arc() -> Arc<Self> {
196        let mut registry = Self::with_defaults();
197        
198        // Create batch tool without registry reference
199        let batch_tool = Arc::new(batch::BatchTool::new());
200        registry.register(batch_tool.clone());
201        
202        // Wrap registry in Arc
203        let registry = Arc::new(registry);
204        
205        // Now give batch tool a weak reference to the registry
206        batch_tool.set_registry(Arc::downgrade(&registry));
207        
208        registry
209    }
210
211    /// Create Arc-wrapped registry with provider and batch tool properly initialized.
212    /// The batch tool needs a weak reference to the registry, so we use
213    /// a two-phase initialization pattern.
214    #[allow(dead_code)]
215    pub fn with_provider_arc(provider: Arc<dyn Provider>, model: String) -> Arc<Self> {
216        let mut registry = Self::with_provider(provider, model);
217        
218        // Create batch tool without registry reference
219        let batch_tool = Arc::new(batch::BatchTool::new());
220        registry.register(batch_tool.clone());
221        
222        // Wrap registry in Arc
223        let registry = Arc::new(registry);
224        
225        // Now give batch tool a weak reference to the registry
226        batch_tool.set_registry(Arc::downgrade(&registry));
227        
228        registry
229    }
230}
231
232impl Default for ToolRegistry {
233    fn default() -> Self {
234        Self::with_defaults()
235    }
236}