matrixcode_core/command/handlers/
overview.rs1use std::future::Future;
4use std::pin::Pin;
5
6use crate::command::{Command, BackendContext};
7
8pub struct Overview;
9
10impl Command for Overview {
11 fn name(&self) -> &'static str {
12 "overview"
13 }
14
15 fn help(&self) -> Option<&'static str> {
16 Some("显示项目概览")
17 }
18
19 fn execute<'a>(&'a self, ctx: &'a mut BackendContext<'_>)
20 -> Pin<Box<dyn Future<Output = bool> + Send + 'a>>
21 {
22 Box::pin(async move {
23 let Some(project_path) = ctx.project_path else {
25 let _ = ctx.event_tx.send(crate::AgentEvent::progress(
26 "未指定项目路径".to_string(),
27 None,
28 )).await;
29 return false;
30 };
31
32 let readme_path = project_path.join("README.md");
34 let mut info = "📂 项目概览:\n\n".to_string();
35
36 if readme_path.exists() {
38 if let Ok(content) = tokio::fs::read_to_string(&readme_path).await {
39 let lines: Vec<&str> = content.lines().take(20).collect();
40 info.push_str("README:\n");
41 info.push_str(&lines.join("\n"));
42 info.push_str("\n\n");
43 }
44 }
45
46 info.push_str("目录结构:\n");
48 if let Ok(mut entries) = tokio::fs::read_dir(project_path).await {
49 let mut dirs = Vec::new();
50 let mut files = Vec::new();
51
52 while let Ok(Some(entry)) = entries.next_entry().await {
53 let name = entry.file_name().to_string_lossy().to_string();
54 if name.starts_with('.') || name == "target" || name == "node_modules" {
55 continue;
56 }
57 if entry.file_type().await.map(|t| t.is_dir()).unwrap_or(false) {
58 dirs.push(name);
59 } else {
60 files.push(name);
61 }
62 }
63
64 dirs.sort();
65 files.sort();
66
67 for dir in dirs.iter().take(10) {
68 info.push_str(&format!(" 📁 {}/\n", dir));
69 }
70 for file in files.iter().take(10) {
71 info.push_str(&format!(" 📄 {}\n", file));
72 }
73
74 if dirs.len() > 10 || files.len() > 10 {
75 info.push_str(&format!(" ... 还有 {} 个目录, {} 个文件\n",
76 dirs.len().saturating_sub(10),
77 files.len().saturating_sub(10)));
78 }
79 }
80
81 let _ = ctx.event_tx.send(crate::AgentEvent::progress(info, None)).await;
82 false
83 })
84 }
85}