Skip to main content

pawan/tools/
mod.rs

1//! Tools for Pawan agent
2//!
3//! This module provides all the tools that Pawan can use to interact with
4//! the filesystem, execute commands, and perform coding operations.
5//!
6//! Native tools (rg, fd, sd, erd, mise) are thin wrappers over CLI binaries
7//! that provide structured JSON output and auto-install hints.
8
9pub mod agent;
10pub mod bash;
11pub mod edit;
12#[cfg(test)]
13mod edit_tests;
14pub mod file;
15pub mod git;
16pub mod native;
17pub mod search;
18
19#[cfg(feature = "ares")]
20pub mod ares_bridge;
21
22use async_trait::async_trait;
23use serde_json::Value;
24use std::collections::HashMap;
25use std::sync::Arc;
26
27/// Tool definition for LLM
28#[derive(Debug, Clone)]
29pub struct ToolDefinition {
30    /// Tool name
31    pub name: String,
32    /// Tool description
33    pub description: String,
34    /// JSON Schema for parameters
35    pub parameters: Value,
36}
37
38/// Trait for implementing tools
39#[async_trait]
40pub trait Tool: Send + Sync {
41    /// Returns the unique name of this tool
42    fn name(&self) -> &str;
43
44    /// Returns a description of what this tool does
45    fn description(&self) -> &str;
46
47    /// Returns the JSON schema for this tool's parameters
48    fn parameters_schema(&self) -> Value;
49
50    /// Executes the tool with the given arguments
51    async fn execute(&self, args: Value) -> crate::Result<Value>;
52
53    /// Convert to ToolDefinition
54    fn to_definition(&self) -> ToolDefinition {
55        ToolDefinition {
56            name: self.name().to_string(),
57            description: self.description().to_string(),
58            parameters: self.parameters_schema(),
59        }
60    }
61}
62
63/// Registry for managing tools
64pub struct ToolRegistry {
65    tools: HashMap<String, Arc<dyn Tool>>,
66}
67
68impl ToolRegistry {
69    /// Create a new empty registry
70    pub fn new() -> Self {
71        Self {
72            tools: HashMap::new(),
73        }
74    }
75
76    /// Create a registry with all default tools
77    pub fn with_defaults(workspace_root: std::path::PathBuf) -> Self {
78        let mut registry = Self::new();
79
80        // File tools
81        registry.register(Arc::new(file::ReadFileTool::new(workspace_root.clone())));
82        registry.register(Arc::new(file::WriteFileTool::new(workspace_root.clone())));
83        registry.register(Arc::new(file::ListDirectoryTool::new(
84            workspace_root.clone(),
85        )));
86
87        // Edit tools (anchor mode is most reliable, then insert_after, then line numbers)
88        registry.register(Arc::new(edit::EditFileLinesTool::new(workspace_root.clone())));
89        registry.register(Arc::new(edit::EditFileTool::new(workspace_root.clone())));
90        registry.register(Arc::new(edit::InsertAfterTool::new(workspace_root.clone())));
91        registry.register(Arc::new(edit::AppendFileTool::new(workspace_root.clone())));
92
93        // Search tools (native rg/fd wrappers override old search module)
94        registry.register(Arc::new(native::GlobSearchTool::new(workspace_root.clone())));
95        registry.register(Arc::new(native::GrepSearchTool::new(workspace_root.clone())));
96
97        // Bash tool
98        registry.register(Arc::new(bash::BashTool::new(workspace_root.clone())));
99
100        // Git tools
101        registry.register(Arc::new(git::GitStatusTool::new(workspace_root.clone())));
102        registry.register(Arc::new(git::GitDiffTool::new(workspace_root.clone())));
103        registry.register(Arc::new(git::GitAddTool::new(workspace_root.clone())));
104        registry.register(Arc::new(git::GitCommitTool::new(workspace_root.clone())));
105        registry.register(Arc::new(git::GitLogTool::new(workspace_root.clone())));
106        registry.register(Arc::new(git::GitBlameTool::new(workspace_root.clone())));
107        registry.register(Arc::new(git::GitBranchTool::new(workspace_root.clone())));
108        registry.register(Arc::new(git::GitCheckoutTool::new(workspace_root.clone())));
109        registry.register(Arc::new(git::GitStashTool::new(workspace_root.clone())));
110
111        // Sub-agent tools
112        registry.register(Arc::new(agent::SpawnAgentsTool::new(workspace_root.clone())));
113        registry.register(Arc::new(agent::SpawnAgentTool::new(workspace_root.clone())));
114
115        // Native CLI tools (rg, fd, sd, erd, mise)
116        registry.register(Arc::new(native::RipgrepTool::new(workspace_root.clone())));
117        registry.register(Arc::new(native::FdTool::new(workspace_root.clone())));
118        registry.register(Arc::new(native::SdTool::new(workspace_root.clone())));
119        registry.register(Arc::new(native::ErdTool::new(workspace_root.clone())));
120        registry.register(Arc::new(native::MiseTool::new(workspace_root.clone())));
121        registry.register(Arc::new(native::ZoxideTool::new(workspace_root)));
122
123        registry
124    }
125
126    /// Register a tool
127    pub fn register(&mut self, tool: Arc<dyn Tool>) {
128        self.tools.insert(tool.name().to_string(), tool);
129    }
130
131    /// Get a tool by name
132    pub fn get(&self, name: &str) -> Option<&Arc<dyn Tool>> {
133        self.tools.get(name)
134    }
135
136    /// Check if a tool exists
137    pub fn has_tool(&self, name: &str) -> bool {
138        self.tools.contains_key(name)
139    }
140
141    /// Execute a tool by name
142    pub async fn execute(&self, name: &str, args: Value) -> crate::Result<Value> {
143        match self.tools.get(name) {
144            Some(tool) => tool.execute(args).await,
145            None => Err(crate::PawanError::NotFound(format!(
146                "Tool not found: {}",
147                name
148            ))),
149        }
150    }
151
152    /// Get all tool definitions
153    pub fn get_definitions(&self) -> Vec<ToolDefinition> {
154        self.tools.values().map(|t| t.to_definition()).collect()
155    }
156
157    /// Get tool names
158    pub fn tool_names(&self) -> Vec<&str> {
159        self.tools.keys().map(|s| s.as_str()).collect()
160    }
161}
162
163impl Default for ToolRegistry {
164    fn default() -> Self {
165        Self::new()
166    }
167}