matrixcode_core/tools/
mod.rs1pub 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; pub mod read;
12pub mod registry; pub 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
22pub 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
35pub 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 #[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 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 fn risk_level(&self) -> RiskLevel {
79 RiskLevel::Safe
80 }
81}
82
83pub fn all_tools() -> Vec<Box<dyn Tool>> {
86 all_tools_with_skills(Arc::new(Vec::new()))
87}
88
89fn 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
117pub fn all_tools_with_skills(skills: Arc<Vec<Skill>>) -> Vec<Box<dyn Tool>> {
119 let mut tools = base_tools(skills);
120 tools.extend(workflow::workflow_tools());
122 tools
123}
124
125pub 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 tools.extend(workflow::workflow_tools_with_provider(provider));
133 tools
134}
135
136pub 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 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 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 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
187pub 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
195pub 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 let arc_provider = boxed_provider.clone_arc();
203 all_tools_with_provider(skills, arc_provider)
204}