matrixcode_core/tools/
mod.rs1pub mod ask;
2pub mod bash;
3pub mod browser;
4pub mod codegraph;
5pub mod edit;
6pub mod glob;
7pub mod grep;
8pub mod ls;
9pub mod monitor;
10pub mod multi_edit;
11pub mod plan_mode;
12pub mod toolproxy; pub mod read;
14pub mod registry; pub mod search;
16pub mod skill;
17pub mod task;
18pub mod todo_write;
19pub mod webfetch;
20pub mod websearch;
21pub mod workflow;
22pub mod write;
23
24pub use toolproxy::{ProxyToolExecutor, ProxyToolDef, ProxyMetadata, ProxyTool, ProxyToolResponse, ProxyToolRequest};
26
27use std::sync::Arc;
28
29use anyhow::Result;
30use async_trait::async_trait;
31use serde::{Deserialize, Serialize};
32use serde_json::{Value, json};
33
34use crate::approval::RiskLevel;
35use crate::skills::Skill;
36use std::path::PathBuf;
37
38pub type BoxedTool = Box<dyn Tool>;
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ToolDefinition {
43 pub name: String,
44 pub description: String,
45 pub parameters: Value,
46 #[serde(default)]
49 pub is_priority: bool,
50}
51
52impl Default for ToolDefinition {
53 fn default() -> Self {
54 Self {
55 name: String::new(),
56 description: String::new(),
57 parameters: json!({"type": "object"}),
58 is_priority: false,
59 }
60 }
61}
62
63impl ToolDefinition {
64 pub fn description_for_llm(&self) -> String {
66 if self.is_priority {
67 format!("[优先] {}", self.description)
68 } else {
69 self.description.clone()
70 }
71 }
72}
73
74#[async_trait]
75pub trait Tool: Send + Sync {
76 fn definition(&self) -> ToolDefinition;
77 async fn execute(&self, params: Value) -> Result<String>;
78
79 fn risk_level(&self) -> RiskLevel {
82 RiskLevel::Safe
83 }
84}
85
86pub fn all_tools() -> Vec<Box<dyn Tool>> {
89 all_tools_with_skills(Arc::new(Vec::new()))
90}
91
92fn base_tools(skills: Arc<Vec<Skill>>) -> Vec<Box<dyn Tool>> {
94 vec![
95 Box::new(ask::AskTool),
96 Box::new(read::ReadTool),
97 Box::new(write::WriteTool),
98 Box::new(edit::EditTool),
99 Box::new(multi_edit::MultiEditTool),
100 Box::new(search::SearchTool),
101 Box::new(grep::GrepTool),
102 Box::new(glob::GlobTool),
103 Box::new(ls::LsTool),
104 Box::new(bash::BashTool),
105 Box::new(browser::BrowserOpenTool),
106 Box::new(todo_write::TodoWriteTool),
107 Box::new(websearch::WebSearchTool::new()),
108 Box::new(webfetch::WebFetchTool),
109 Box::new(skill::SkillTool::new(skills)),
110 Box::new(task::TaskTool),
111 Box::new(task::TaskCreateTool),
112 Box::new(task::TaskGetTool),
113 Box::new(task::TaskListTool),
114 Box::new(task::TaskStopTool),
115 Box::new(plan_mode::EnterPlanModeTool),
116 Box::new(plan_mode::ExitPlanModeTool),
117 Box::new(monitor::MonitorTool),
118 ]
119}
120
121pub fn all_tools_with_skills(skills: Arc<Vec<Skill>>) -> Vec<Box<dyn Tool>> {
123 let mut tools = base_tools(skills);
124 tools.extend(workflow::workflow_tools());
126 tools
127}
128
129pub fn all_tools_with_provider(
131 skills: Arc<Vec<Skill>>,
132 provider: Arc<dyn crate::providers::Provider>,
133) -> Vec<Box<dyn Tool>> {
134 let mut tools = base_tools(skills);
135 tools.extend(workflow::workflow_tools_with_provider(provider));
137 tools
138}
139
140pub fn generate_tools_prompt() -> String {
142 generate_tools_prompt_with_path(None)
143}
144
145pub fn generate_tools_prompt_with_path(project_path: Option<&PathBuf>) -> String {
147 let mut tools = base_tools(Arc::new(Vec::new()));
148
149 if let Some(path) = project_path
151 && codegraph::should_inject_codegraph_tools(path) {
152 tools.extend(codegraph::codegraph_tools_with_auto_detect(path));
153 }
154
155 tools.extend(workflow::workflow_tools());
157
158 let mut lines = vec!["可用工具:".to_string()];
159
160 for tool in tools {
161 let def = tool.definition();
162 let brief = def
164 .description
165 .split('.')
166 .next()
167 .or_else(|| def.description.split('\n').next())
168 .unwrap_or(&def.description);
169 let brief = if brief.len() > 60 {
170 format!("{}...", brief.chars().take(57).collect::<String>())
171 } else {
172 brief.to_string()
173 };
174 lines.push(format!("- {}: {}", def.name, brief));
175 }
176
177 lines.join("\n")
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use std::path::PathBuf;
184
185 #[test]
186 fn test_all_tools_includes_workflow_tools() {
187 let tools = all_tools();
188 let tool_names: Vec<String> = tools.iter().map(|t| t.definition().name).collect();
189
190 assert!(tool_names.contains(&"workflow_discover".to_string()), "workflow_discover should be in tools");
192 assert!(tool_names.contains(&"workflow_run".to_string()), "workflow_run should be in tools");
193 assert!(tool_names.contains(&"workflow_match".to_string()), "workflow_match should be in tools");
194 }
195
196 #[test]
197 fn test_generate_tools_prompt_includes_workflow() {
198 let prompt = generate_tools_prompt();
199
200 assert!(prompt.contains("workflow_discover"), "prompt should mention workflow_discover");
202 assert!(prompt.contains("workflow_run"), "prompt should mention workflow_run");
203 assert!(prompt.contains("workflow_match"), "prompt should mention workflow_match");
204 }
205
206 #[test]
207 fn test_generate_tools_prompt_with_path_includes_codegraph() {
208 let path = PathBuf::from(".");
209 let prompt = generate_tools_prompt_with_path(Some(&path));
210
211 if codegraph::should_inject_codegraph_tools(&path) {
216 assert!(prompt.contains("code_search"), "prompt should mention code_search when conditions met");
217 assert!(prompt.contains("code_callers"), "prompt should mention code_callers when conditions met");
218 } else {
219 assert!(!prompt.contains("code_search"), "prompt should NOT mention code_search without .codegraph");
221 }
222 }
223
224 #[test]
225 fn test_generate_tools_prompt_without_path_excludes_codegraph() {
226 let prompt = generate_tools_prompt();
227
228 assert!(!prompt.contains("code_search"), "prompt should NOT mention code_search without path");
230 }
231}
232
233pub fn all_tools_with_arc_provider(
235 skills: Arc<Vec<Skill>>,
236 provider: Arc<dyn crate::providers::Provider>,
237) -> Vec<Box<dyn Tool>> {
238 all_tools_with_provider(skills, provider)
239}
240
241pub fn all_tools_with_box_provider(
244 skills: Arc<Vec<Skill>>,
245 boxed_provider: Box<dyn crate::providers::Provider>,
246) -> Vec<Box<dyn Tool>> {
247 let arc_provider = boxed_provider.clone_arc();
249 all_tools_with_provider(skills, arc_provider)
250}
251
252pub fn all_tools_with_project_path(
254 skills: Arc<Vec<Skill>>,
255 project_path: PathBuf,
256) -> Vec<Box<dyn Tool>> {
257 let mut tools = base_tools(skills);
258 tools.extend(codegraph::codegraph_tools(&project_path));
260 tools.extend(workflow::workflow_tools());
262 tools
263}
264
265pub fn all_tools_full(
267 skills: Arc<Vec<Skill>>,
268 provider: Arc<dyn crate::providers::Provider>,
269 project_path: PathBuf,
270) -> Vec<Box<dyn Tool>> {
271 let mut tools = base_tools(skills);
272 if codegraph::should_inject_codegraph_tools(&project_path) {
274 tools.extend(codegraph::codegraph_tools(&project_path));
275 }
276 tools.extend(workflow::workflow_tools_with_provider(provider));
278 tools
279}