Skip to main content

matrixcode_core/command/handlers/
tools.rs

1//! /tools 命令处理器
2
3use 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            // 按类别分组
27            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}