matrixcode_core/tools/codegraph/
tools.rs1use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::{Value, json};
6use std::path::Path;
7use std::sync::Arc;
8
9use super::manager::CodeGraphManager;
10use crate::tools::{Tool, ToolDefinition};
11use crate::approval::RiskLevel;
12
13pub struct CodeGraphSearchTool {
15 manager: Arc<CodeGraphManager>,
16}
17
18impl CodeGraphSearchTool {
19 pub fn new(project_path: &Path) -> Self {
20 Self {
21 manager: Arc::new(CodeGraphManager::new(project_path)),
22 }
23 }
24}
25
26#[async_trait]
27impl Tool for CodeGraphSearchTool {
28 fn definition(&self) -> ToolDefinition {
29 ToolDefinition {
30 name: "code_search".to_string(),
31 description: "[优先] [优先工具] 搜索代码符号(函数、类、方法、变量)。查找代码定义时必须优先使用此工具,比 grep 快 10-100 倍。返回符号位置、签名、文档。grep 仅用于搜索字符串内容(如错误消息)。".to_string(),
32 parameters: json!({
33 "type": "object",
34 "properties": {
35 "pattern": {
36 "type": "string",
37 "description": "���号名称搜索模式(支持模糊匹配)"
38 },
39 "limit": {
40 "type": "integer",
41 "description": "返回结果数量限制(默认 20)",
42 "default": 20
43 }
44 },
45 "required": ["pattern"]
46 }),
47 is_priority: true,
48 }
49 }
50
51 async fn execute(&self, args: Value) -> Result<String> {
52 let pattern = args["pattern"].as_str()
53 .ok_or_else(|| anyhow::anyhow!("Missing pattern parameter"))?;
54 let limit = args["limit"].as_u64().unwrap_or(20) as usize;
55
56 let nodes = self.manager.search(pattern, limit)?;
57
58 Ok(serde_json::to_string(&json!({
59 "nodes": nodes,
60 "query": pattern,
61 "total_count": nodes.len()
62 }))?)
63 }
64
65 fn risk_level(&self) -> RiskLevel {
66 RiskLevel::Safe
67 }
68}
69
70pub struct CodeGraphCallersTool {
72 manager: Arc<CodeGraphManager>,
73}
74
75impl CodeGraphCallersTool {
76 pub fn new(project_path: &Path) -> Self {
77 Self {
78 manager: Arc::new(CodeGraphManager::new(project_path)),
79 }
80 }
81}
82
83#[async_trait]
84impl Tool for CodeGraphCallersTool {
85 fn definition(&self) -> ToolDefinition {
86 ToolDefinition {
87 name: "code_callers".to_string(),
88 description: "[优先] [优先工具] 查找调用指定符号的所有函数/方法。分析调用关系时必须优先使用,比 grep 追溯更准确。grep 仅用于搜索字符串内容。".to_string(),
89 parameters: json!({
90 "type": "object",
91 "properties": {
92 "symbol": {
93 "type": "string",
94 "description": "符号 ID 或名称"
95 },
96 "limit": {
97 "type": "integer",
98 "description": "返回结果数量限制(默认 10)",
99 "default": 10
100 }
101 },
102 "required": ["symbol"]
103 }),
104 is_priority: true,
105 }
106 }
107
108 async fn execute(&self, args: Value) -> Result<String> {
109 let symbol = args["symbol"].as_str()
110 .ok_or_else(|| anyhow::anyhow!("Missing symbol parameter"))?;
111 let limit = args["limit"].as_u64().unwrap_or(10) as usize;
112
113 let nodes = self.manager.callers(symbol, limit)?;
114
115 Ok(serde_json::to_string(&json!({
116 "callers": nodes,
117 "symbol": symbol,
118 "total_count": nodes.len()
119 }))?)
120 }
121
122 fn risk_level(&self) -> RiskLevel {
123 RiskLevel::Safe
124 }
125}
126
127pub struct CodeGraphCalleesTool {
129 manager: Arc<CodeGraphManager>,
130}
131
132impl CodeGraphCalleesTool {
133 pub fn new(project_path: &Path) -> Self {
134 Self {
135 manager: Arc::new(CodeGraphManager::new(project_path)),
136 }
137 }
138}
139
140#[async_trait]
141impl Tool for CodeGraphCalleesTool {
142 fn definition(&self) -> ToolDefinition {
143 ToolDefinition {
144 name: "code_callees".to_string(),
145 description: "[优先] [优先工具] 查找指定符号调用的所有函数/方法。分析执行流程时必须优先使用,比 grep 追踪更准确。grep 仅用于搜索字符串内容。".to_string(),
146 parameters: json!({
147 "type": "object",
148 "properties": {
149 "symbol": {
150 "type": "string",
151 "description": "符号 ID 或名称"
152 },
153 "limit": {
154 "type": "integer",
155 "description": "返回结果数量限制(默认 10)",
156 "default": 10
157 }
158 },
159 "required": ["symbol"]
160 }),
161 is_priority: true,
162 }
163 }
164
165 async fn execute(&self, args: Value) -> Result<String> {
166 let symbol = args["symbol"].as_str()
167 .ok_or_else(|| anyhow::anyhow!("Missing symbol parameter"))?;
168 let limit = args["limit"].as_u64().unwrap_or(10) as usize;
169
170 let nodes = self.manager.callees(symbol, limit)?;
171
172 Ok(serde_json::to_string(&json!({
173 "callees": nodes,
174 "symbol": symbol,
175 "total_count": nodes.len()
176 }))?)
177 }
178
179 fn risk_level(&self) -> RiskLevel {
180 RiskLevel::Safe
181 }
182}
183
184pub struct CodeGraphStatusTool {
186 manager: Arc<CodeGraphManager>,
187}
188
189impl CodeGraphStatusTool {
190 pub fn new(project_path: &Path) -> Self {
191 Self {
192 manager: Arc::new(CodeGraphManager::new(project_path)),
193 }
194 }
195}
196
197#[async_trait]
198impl Tool for CodeGraphStatusTool {
199 fn definition(&self) -> ToolDefinition {
200 ToolDefinition {
201 name: "code_status".to_string(),
202 description: "检查 CodeGraph 索引状态。返回文件数、节点数、边数、支持的语言等信息。".to_string(),
203 parameters: json!({
204 "type": "object",
205 "properties": {}
206 }),
207 is_priority: false,
208 }
209 }
210
211 async fn execute(&self, _args: Value) -> Result<String> {
212 let status = self.manager.status()?;
213 Ok(serde_json::to_string(&status)?)
214 }
215
216 fn risk_level(&self) -> RiskLevel {
217 RiskLevel::Safe
218 }
219}
220
221pub struct CodeGraphSyncTool {
223 manager: Arc<CodeGraphManager>,
224}
225
226impl CodeGraphSyncTool {
227 pub fn new(project_path: &Path) -> Self {
228 Self {
229 manager: Arc::new(CodeGraphManager::new(project_path)),
230 }
231 }
232}
233
234#[async_trait]
235impl Tool for CodeGraphSyncTool {
236 fn definition(&self) -> ToolDefinition {
237 ToolDefinition {
238 name: "code_sync".to_string(),
239 description: "手动同步 CodeGraph 索引。当代码库有变化但自动同步未触发时使用,确保搜索结果是最新的。".to_string(),
240 parameters: json!({
241 "type": "object",
242 "properties": {}
243 }),
244 is_priority: false,
245 }
246 }
247
248 async fn execute(&self, _args: Value) -> Result<String> {
249 self.manager.sync().await?;
250 Ok(serde_json::to_string(&json!({"success": true, "message": "CodeGraph index synced"}))?)
251 }
252
253 fn risk_level(&self) -> RiskLevel {
254 RiskLevel::Safe
255 }
256}
257
258pub fn codegraph_tools(project_path: &Path) -> Vec<Box<dyn Tool>> {
260 vec![
261 Box::new(CodeGraphSearchTool::new(project_path)),
262 Box::new(CodeGraphCallersTool::new(project_path)),
263 Box::new(CodeGraphCalleesTool::new(project_path)),
264 Box::new(CodeGraphStatusTool::new(project_path)),
265 Box::new(CodeGraphSyncTool::new(project_path)),
266 ]
267}
268
269pub fn codegraph_tools_with_auto_detect(start_path: &Path) -> Vec<Box<dyn Tool>> {
271 let project_path = super::project::find_project_root(start_path);
272 codegraph_tools(&project_path)
273}
274
275pub fn should_inject_codegraph_tools(start_path: &Path) -> bool {
277 super::install::is_codegraph_installed() &&
278 CodeGraphManager::with_auto_detect(start_path).is_initialized()
279}
280
281pub fn codegraph_tools_if_installed(start_path: &Path) -> Vec<Box<dyn Tool>> {
283 if should_inject_codegraph_tools(start_path) {
284 codegraph_tools_with_auto_detect(start_path)
285 } else {
286 vec![]
287 }
288}