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