Skip to main content

matrixcode_core/tools/codegraph/
tools.rs

1//! Tool implementations for CodeGraph.
2
3use 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
13/// Tool for searching symbols in CodeGraph index.
14pub 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
70/// Tool for finding callers of a symbol.
71pub 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
127/// Tool for finding callees of a symbol.
128pub 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
184/// Tool for checking CodeGraph status.
185pub 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
221/// Tool for syncing CodeGraph index.
222pub 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
258/// Create CodeGraph tools for a project path.
259pub 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
269/// Create CodeGraph tools with automatic project root detection.
270pub 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
275/// Check if CodeGraph tools should be injected.
276pub 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
281/// Create CodeGraph tools if installed.
282pub 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}