Skip to main content

dot/tools/
mod.rs

1pub mod file;
2pub mod glob;
3pub mod grep;
4pub mod patch;
5pub mod shell;
6pub mod web;
7
8use crate::provider::ToolDefinition;
9
10pub trait Tool: Send + Sync {
11    fn name(&self) -> &str;
12    fn description(&self) -> &str;
13    fn input_schema(&self) -> serde_json::Value;
14    fn execute(&self, input: serde_json::Value) -> anyhow::Result<String>;
15}
16
17pub struct ToolRegistry {
18    tools: Vec<Box<dyn Tool>>,
19}
20
21impl ToolRegistry {
22    pub fn new() -> Self {
23        ToolRegistry { tools: Vec::new() }
24    }
25
26    pub fn register(&mut self, tool: Box<dyn Tool>) {
27        self.tools.push(tool);
28    }
29
30    pub fn register_many(&mut self, tools: Vec<Box<dyn Tool>>) {
31        self.tools.extend(tools);
32    }
33
34    pub fn definitions(&self) -> Vec<ToolDefinition> {
35        self.tools
36            .iter()
37            .map(|t| ToolDefinition {
38                name: t.name().to_string(),
39                description: t.description().to_string(),
40                input_schema: t.input_schema(),
41            })
42            .collect()
43    }
44
45    /// Return tool definitions filtered by an allow/deny map.
46    /// If the map is empty, all tools are returned. Otherwise, tools
47    /// explicitly set to `false` are excluded.
48    pub fn definitions_filtered(
49        &self,
50        filter: &std::collections::HashMap<String, bool>,
51    ) -> Vec<ToolDefinition> {
52        if filter.is_empty() {
53            return self.definitions();
54        }
55        self.tools
56            .iter()
57            .filter(|t| filter.get(t.name()).copied().unwrap_or(true))
58            .map(|t| ToolDefinition {
59                name: t.name().to_string(),
60                description: t.description().to_string(),
61                input_schema: t.input_schema(),
62            })
63            .collect()
64    }
65
66    pub fn execute(&self, name: &str, input: serde_json::Value) -> anyhow::Result<String> {
67        for tool in &self.tools {
68            if tool.name() == name {
69                tracing::debug!("Executing tool: {}", name);
70                return tool.execute(input);
71            }
72        }
73        anyhow::bail!("Unknown tool: {}", name)
74    }
75
76    pub fn tool_count(&self) -> usize {
77        self.tools.len()
78    }
79
80    pub fn default_tools() -> Self {
81        let mut registry = Self::new();
82        registry.register(Box::new(file::ReadFileTool));
83        registry.register(Box::new(file::WriteFileTool));
84        registry.register(Box::new(file::ListDirectoryTool));
85        registry.register(Box::new(file::SearchFilesTool));
86        registry.register(Box::new(shell::RunCommandTool));
87        registry.register(Box::new(glob::GlobTool));
88        registry.register(Box::new(grep::GrepTool));
89        registry.register(Box::new(web::WebFetchTool));
90        registry.register(Box::new(patch::ApplyPatchTool));
91        registry
92    }
93}
94
95impl Default for ToolRegistry {
96    fn default() -> Self {
97        Self::new()
98    }
99}