返回 DeepSeek-TUI-2026
project.rs
根目录 / crates / tui / src / tools / project.rs
1 //! Project mapping tool for understanding codebase structure.
2
3 use crate::utils::{is_key_file, project_tree, summarize_project};
4 use anyhow::Result;
5 use async_trait::async_trait;
6 use serde::Serialize;
7 use serde_json::{Value, json};
8
9 use super::spec::{
10 ApprovalRequirement, ToolCapability, ToolContext, ToolError, ToolResult, ToolSpec, optional_u64,
11 };
12
13 pub struct ProjectMapTool;
14
15 #[derive(Debug, Serialize)]
16 struct ProjectMap {
17 tree: String,
18 summary: String,
19 key_files: Vec<String>,
20 }
21
22 #[async_trait]
23 impl ToolSpec for ProjectMapTool {
24 fn name(&self) -> &'static str {
25 "project_map"
26 }
27
28 fn description(&self) -> &'static str {
29 "Get a high-level map of the project structure, including key files and a tree view."
30 }
31
32 fn input_schema(&self) -> Value {
33 json!({
34 "type": "object",
35 "properties": {
36 "max_depth": {
37 "type": "integer",
38 "description": "Maximum depth for the tree view (default: 3)."
39 }
40 }
41 })
42 }
43
44 fn capabilities(&self) -> Vec<ToolCapability> {
45 vec![ToolCapability::ReadOnly, ToolCapability::Sandboxable]
46 }
47
48 fn approval_requirement(&self) -> ApprovalRequirement {
49 ApprovalRequirement::Auto
50 }
51
52 async fn execute(&self, input: Value, context: &ToolContext) -> Result<ToolResult, ToolError> {
53 let max_depth = optional_u64(&input, "max_depth", 3) as usize;
54 let map = generate_project_map(&context.workspace, max_depth)?;
55 ToolResult::json(&map).map_err(|e| ToolError::execution_failed(e.to_string()))
56 }
57 }
58
59 fn generate_project_map(root: &std::path::Path, max_depth: usize) -> Result<ProjectMap, ToolError> {
60 let tree = project_tree(root, max_depth);
61 let summary = summarize_project(root);
62
63 // For key_files, we can just do a quick scan since summarize_project doesn't return them directly anymore
64 let mut key_files = Vec::new();
65 let mut builder = ignore::WalkBuilder::new(root);
66 builder.hidden(false).follow_links(true).max_depth(Some(2));
67 let walker = builder.build();
68
69 for entry in walker.flatten() {
70 if is_key_file(entry.path())
71 && let Ok(rel) = entry.path().strip_prefix(root)
72 {
73 key_files.push(rel.to_string_lossy().to_string());
74 }
75 }
76
77 Ok(ProjectMap {
78 tree,
79 summary,
80 key_files,
81 })
82 }
83
83 lines RUST