Skip to main content

matrixcode_core/tools/
mod.rs

1pub mod ask;
2pub mod bash;
3pub mod edit;
4pub mod glob;
5pub mod grep;
6pub mod ls;
7pub mod monitor;
8pub mod multi_edit;
9pub mod plan_mode;
10pub mod toolproxy;  // 代理工具模块
11pub mod read;
12pub mod registry;  // 工具注册中心
13pub mod search;
14pub mod skill;
15pub mod task;
16pub mod todo_write;
17pub mod webfetch;
18pub mod websearch;
19pub mod workflow;
20pub mod write;
21
22// Re-export proxy types for convenience
23pub use toolproxy::{ProxyToolExecutor, ProxyToolDef, ProxyMetadata, ProxyTool, ProxyToolResponse, ProxyToolRequest};
24
25use std::sync::Arc;
26
27use anyhow::Result;
28use async_trait::async_trait;
29use serde::{Deserialize, Serialize};
30use serde_json::{Value, json};
31
32use crate::approval::RiskLevel;
33use crate::skills::Skill;
34
35/// Type alias for boxed tool
36pub type BoxedTool = Box<dyn Tool>;
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct ToolDefinition {
40    pub name: String,
41    pub description: String,
42    pub parameters: Value,
43    /// 是否为优先工具。true 时会在描述前添加 "[优先]" 提示,
44    /// 让 LLM 更倾向选择此工具。默认 false。
45    #[serde(default)]
46    pub is_priority: bool,
47}
48
49impl Default for ToolDefinition {
50    fn default() -> Self {
51        Self {
52            name: String::new(),
53            description: String::new(),
54            parameters: json!({"type": "object"}),
55            is_priority: false,
56        }
57    }
58}
59
60impl ToolDefinition {
61    /// 获取发送给 LLM 的描述(带优先标记)
62    pub fn description_for_llm(&self) -> String {
63        if self.is_priority {
64            format!("[优先] {}", self.description)
65        } else {
66            self.description.clone()
67        }
68    }
69}
70
71#[async_trait]
72pub trait Tool: Send + Sync {
73    fn definition(&self) -> ToolDefinition;
74    async fn execute(&self, params: Value) -> Result<String>;
75
76    /// Risk level of this tool. Defaults to Safe (read-only).
77    /// Override in tools that modify state.
78    fn risk_level(&self) -> RiskLevel {
79        RiskLevel::Safe
80    }
81}
82
83/// Default toolset without any skill integration. Kept for callers
84/// (and the existing tests) that don't care about skills.
85pub fn all_tools() -> Vec<Box<dyn Tool>> {
86    all_tools_with_skills(Arc::new(Vec::new()))
87}
88
89/// Base toolset without workflow tools (to avoid duplicates).
90fn base_tools(skills: Arc<Vec<Skill>>) -> Vec<Box<dyn Tool>> {
91    vec![
92        Box::new(ask::AskTool),
93        Box::new(read::ReadTool),
94        Box::new(write::WriteTool),
95        Box::new(edit::EditTool),
96        Box::new(multi_edit::MultiEditTool),
97        Box::new(search::SearchTool),
98        Box::new(grep::GrepTool),
99        Box::new(glob::GlobTool),
100        Box::new(ls::LsTool),
101        Box::new(bash::BashTool),
102        Box::new(todo_write::TodoWriteTool),
103        Box::new(websearch::WebSearchTool::new()),
104        Box::new(webfetch::WebFetchTool),
105        Box::new(skill::SkillTool::new(skills)),
106        Box::new(task::TaskTool),
107        Box::new(task::TaskCreateTool),
108        Box::new(task::TaskGetTool),
109        Box::new(task::TaskListTool),
110        Box::new(task::TaskStopTool),
111        Box::new(plan_mode::EnterPlanModeTool),
112        Box::new(plan_mode::ExitPlanModeTool),
113        Box::new(monitor::MonitorTool),
114    ]
115}
116
117/// Build the toolset with skill support but without provider.
118pub fn all_tools_with_skills(skills: Arc<Vec<Skill>>) -> Vec<Box<dyn Tool>> {
119    let mut tools = base_tools(skills);
120    // Add workflow tools without provider
121    tools.extend(workflow::workflow_tools());
122    tools
123}
124
125/// Build toolset with Provider for AI-powered tools.
126pub fn all_tools_with_provider(
127    skills: Arc<Vec<Skill>>,
128    provider: Arc<dyn crate::providers::Provider>,
129) -> Vec<Box<dyn Tool>> {
130    let mut tools = base_tools(skills);
131    // Add AI-powered workflow tools (with provider)
132    tools.extend(workflow::workflow_tools_with_provider(provider));
133    tools
134}
135
136/// Generate tools description for system prompt
137pub fn generate_tools_prompt() -> String {
138    let tools = all_tools();
139    let mut lines = vec!["可用工具:".to_string()];
140
141    for tool in tools {
142        let def = tool.definition();
143        // Extract brief description (first sentence or up to 50 chars)
144        let brief = def
145            .description
146            .split('.')
147            .next()
148            .or_else(|| def.description.split('\n').next())
149            .unwrap_or(&def.description);
150        let brief = if brief.len() > 60 {
151            format!("{}...", brief.chars().take(57).collect::<String>())
152        } else {
153            brief.to_string()
154        };
155        lines.push(format!("- {}: {}", def.name, brief));
156    }
157
158    lines.join("\n")
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_all_tools_includes_workflow_tools() {
167        let tools = all_tools();
168        let tool_names: Vec<String> = tools.iter().map(|t| t.definition().name).collect();
169
170        // Verify workflow tools are present
171        assert!(tool_names.contains(&"workflow_discover".to_string()), "workflow_discover should be in tools");
172        assert!(tool_names.contains(&"workflow_run".to_string()), "workflow_run should be in tools");
173        assert!(tool_names.contains(&"workflow_match".to_string()), "workflow_match should be in tools");
174    }
175
176    #[test]
177    fn test_generate_tools_prompt_includes_workflow() {
178        let prompt = generate_tools_prompt();
179
180        // Verify workflow tools appear in prompt
181        assert!(prompt.contains("workflow_discover"), "prompt should mention workflow_discover");
182        assert!(prompt.contains("workflow_run"), "prompt should mention workflow_run");
183        assert!(prompt.contains("workflow_match"), "prompt should mention workflow_match");
184    }
185}
186
187/// Build toolset with Arc Provider (preferred method)
188pub fn all_tools_with_arc_provider(
189    skills: Arc<Vec<Skill>>,
190    provider: Arc<dyn crate::providers::Provider>,
191) -> Vec<Box<dyn Tool>> {
192    all_tools_with_provider(skills, provider)
193}
194
195/// Build toolset with Box Provider (for CLI compatibility - safe implementation)
196/// Uses clone_arc to safely convert Box to Arc without unsafe code.
197pub fn all_tools_with_box_provider(
198    skills: Arc<Vec<Skill>>,
199    boxed_provider: Box<dyn crate::providers::Provider>,
200) -> Vec<Box<dyn Tool>> {
201    // Safe conversion: clone_arc creates a new Arc without unsafe pointer manipulation
202    let arc_provider = boxed_provider.clone_arc();
203    all_tools_with_provider(skills, arc_provider)
204}