matrixcode_core/command/handlers/
tools.rs1use std::future::Future;
4use std::pin::Pin;
5
6use crate::command::{Command, BackendContext};
7
8pub struct Tools;
9
10impl Command for Tools {
11 fn name(&self) -> &'static str {
12 "tools"
13 }
14
15 fn help(&self) -> Option<&'static str> {
16 Some("列出可用工具")
17 }
18
19 fn execute<'a>(&'a self, ctx: &'a mut BackendContext<'_>)
20 -> Pin<Box<dyn Future<Output = bool> + Send + 'a>>
21 {
22 Box::pin(async move {
23 let tools = ctx.agent.get_tools();
24 let mut info = format!("🔧 可用工具:{}\n\n", tools.len());
25
26 let mut core_tools: Vec<_> = Vec::new();
28 let mut file_tools: Vec<_> = Vec::new();
29 let mut search_tools: Vec<_> = Vec::new();
30 let mut web_tools: Vec<_> = Vec::new();
31 let mut code_tools: Vec<_> = Vec::new();
32 let mut mcp_tools: Vec<_> = Vec::new();
33 let mut workflow_tools: Vec<_> = Vec::new();
34 let mut other_tools: Vec<_> = Vec::new();
35
36 for tool in tools.iter() {
37 let def = tool.definition();
38 let name = def.name.as_str();
39 let desc = def.description.as_str();
40
41 if name.starts_with("mcp_") || name.starts_with("mcp__") {
42 mcp_tools.push(tool);
43 } else if name.starts_with("workflow_") || name.contains("workflow") {
44 workflow_tools.push(tool);
45 } else if name.starts_with("code_") || desc.contains("CodeGraph") {
46 code_tools.push(tool);
47 } else if name.starts_with("proxy_") || desc.contains("代理") {
48 other_tools.push(tool);
49 } else {
50 match name {
51 "read" | "write" | "edit" | "multi_edit" | "ls" => file_tools.push(tool),
52 "grep" | "glob" | "search" => search_tools.push(tool),
53 "websearch" | "webfetch" => web_tools.push(tool),
54 "bash" | "task" | "todo_write" | "notebook_edit"
55 | "task_create" | "task_get" | "task_list" | "task_stop" => core_tools.push(tool),
56 "ask" | "enter_plan_mode" | "exit_plan_mode" | "monitor" => core_tools.push(tool),
57 _ => other_tools.push(tool),
58 }
59 }
60 }
61
62 if !core_tools.is_empty() {
63 info.push_str("📁 核心:\n");
64 for tool in core_tools.iter().take(12) {
65 let def = tool.definition();
66 info.push_str(&format!(" {} - {}\n", def.name,
67 truncate_description(&def.description, 35)));
68 }
69 }
70
71 if !file_tools.is_empty() {
72 info.push_str("\n📄 文件:\n");
73 for tool in file_tools.iter() {
74 let def = tool.definition();
75 info.push_str(&format!(" {} - {}\n", def.name,
76 truncate_description(&def.description, 35)));
77 }
78 }
79
80 if !search_tools.is_empty() {
81 info.push_str("\n🔍 搜索:\n");
82 for tool in search_tools.iter() {
83 let def = tool.definition();
84 info.push_str(&format!(" {} - {}\n", def.name,
85 truncate_description(&def.description, 35)));
86 }
87 }
88
89 if !code_tools.is_empty() {
90 info.push_str("\n📊 CodeGraph:\n");
91 for tool in code_tools.iter() {
92 let def = tool.definition();
93 info.push_str(&format!(" {} - {}\n", def.name,
94 truncate_description(&def.description, 35)));
95 }
96 }
97
98 if !web_tools.is_empty() {
99 info.push_str("\n🌐 网络:\n");
100 for tool in web_tools.iter() {
101 let def = tool.definition();
102 info.push_str(&format!(" {} - {}\n", def.name,
103 truncate_description(&def.description, 35)));
104 }
105 }
106
107 if !workflow_tools.is_empty() {
108 info.push_str("\n🔄 工作流:\n");
109 for tool in workflow_tools.iter().take(10) {
110 let def = tool.definition();
111 info.push_str(&format!(" {} - {}\n", def.name,
112 truncate_description(&def.description, 35)));
113 }
114 }
115
116 if !mcp_tools.is_empty() {
117 info.push_str("\n🔌 MCP:\n");
118 for tool in mcp_tools.iter().take(15) {
119 let def = tool.definition();
120 info.push_str(&format!(" {} - {}\n", def.name,
121 truncate_description(&def.description, 35)));
122 }
123 if mcp_tools.len() > 15 {
124 info.push_str(&format!(" (+ {} 个更多)\n", mcp_tools.len() - 15));
125 }
126 }
127
128 if !other_tools.is_empty() {
129 info.push_str("\n🔧 其他:\n");
130 for tool in other_tools.iter().take(10) {
131 let def = tool.definition();
132 info.push_str(&format!(" {} - {}\n", def.name,
133 truncate_description(&def.description, 35)));
134 }
135 if other_tools.len() > 10 {
136 info.push_str(&format!(" (+ {} 个更多)\n", other_tools.len() - 10));
137 }
138 }
139
140 let _ = ctx.event_tx.send(crate::AgentEvent::progress(info, None)).await;
141 false
142 })
143 }
144}
145
146fn truncate_description(desc: &str, max_len: usize) -> String {
147 let first_line = desc.lines().next().unwrap_or(desc);
148 let chars: Vec<char> = first_line.chars().collect();
149 if chars.len() > max_len {
150 chars[..max_len.saturating_sub(3)].iter().collect::<String>() + "..."
151 } else {
152 first_line.to_string()
153 }
154}