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::approval::RiskLevel;
11use crate::tools::{Tool, ToolDefinition};
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"]
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
71/// Tool for finding callers of a symbol.
72pub 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
129/// Tool for finding callees of a symbol.
130pub 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
187/// Tool for checking CodeGraph status.
188pub 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
225/// Tool for syncing CodeGraph index.
226pub 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
264/// Create CodeGraph tools for a project path.
265pub 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
275/// Create CodeGraph tools with automatic project root detection.
276pub 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
281/// Check if CodeGraph tools should be injected.
282pub 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
287/// Create CodeGraph tools if installed.
288pub 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}