Skip to main content

matrixcode_core/command/handlers/
system.rs

1//! /system 命令处理器
2
3use std::future::Future;
4use std::pin::Pin;
5
6use crate::command::{BackendContext, Command};
7use crate::workflow::WorkflowRegistry;
8
9pub struct System;
10
11impl Command for System {
12    fn name(&self) -> &'static str {
13        "system"
14    }
15
16    fn help(&self) -> Option<&'static str> {
17        Some("显示系统信息和完整的系统提示词")
18    }
19
20    fn execute<'a>(
21        &'a self,
22        ctx: &'a mut BackendContext<'_>,
23    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
24        Box::pin(async move {
25            let msg = ctx.message.trim();
26
27            // 解析子命令
28            if msg == "/system" || msg == "/system status" {
29                self.show_full(ctx).await
30            } else if msg == "/system prompt" {
31                self.show_prompt(ctx).await
32            } else if msg == "/system tools" {
33                self.show_tools(ctx).await
34            } else if msg == "/system stats" {
35                self.show_stats(ctx).await
36            } else if msg == "/system skills" {
37                self.show_skills(ctx).await
38            } else if msg == "/system workflows" {
39                self.show_workflows(ctx).await
40            } else if msg == "/system config" {
41                self.show_config(ctx).await
42            } else if msg == "/system env" {
43                self.show_env(ctx).await
44            } else if msg == "/system help" {
45                self.show_help(ctx).await
46            } else {
47                let _ = ctx.event_tx.send(crate::AgentEvent::progress(
48                    "用法:/system [status|prompt|tools|stats|skills|workflows|config|env|help]".to_string(),
49                    None,
50                )).await;
51                false
52            }
53        })
54    }
55}
56
57impl System {
58    /// 显示完整信息
59    async fn show_full(&self, ctx: &mut BackendContext<'_>) -> bool {
60        let mut output = String::new();
61
62        // 1. 系统基本信息
63        output.push_str("🖥️  **系统环境**\n");
64        output.push_str(&format!(
65            "  • 操作系统: {} ({})\n",
66            std::env::consts::OS,
67            std::env::consts::ARCH
68        ));
69        output.push_str(&format!(
70            "  • 当前目录: {:?}\n",
71            ctx.project_path.unwrap_or(&std::path::PathBuf::new())
72        ));
73        output.push_str(&format!("  • 当前模型: {}\n\n", ctx.model));
74
75        // 2. Token 使用统计
76        let (input_tokens, output_tokens) = ctx.agent.get_token_counts();
77        output.push_str("📊 **Token 统计**\n");
78        output.push_str(&format!("  • 输入: {} tokens\n", input_tokens));
79        output.push_str(&format!("  • 输出: {} tokens\n", output_tokens));
80        output.push_str(&format!(
81            "  • 总计: {} tokens\n\n",
82            input_tokens + output_tokens
83        ));
84
85        // 3. 工具列表
86        let tools = ctx.agent.get_tools();
87        output.push_str(&format!("🔧 **可用工具** (共 {} 个)\n", tools.len()));
88        for (idx, tool) in tools.iter().enumerate() {
89            if idx < 10 || tools.len() <= 15 {
90                output.push_str(&format!("  • {}\n", tool.definition().name));
91            } else if idx == 10 {
92                output.push_str(&format!("  ... 还有 {} 个工具\n", tools.len() - 10));
93                break;
94            }
95        }
96        output.push('\n');
97
98        // 4. 技能列表
99        if !ctx.skills.is_empty() {
100            output.push_str(&format!("📚 **可用技能** (共 {} 个)\n", ctx.skills.len()));
101            for skill in ctx.skills.iter() {
102                output.push_str(&format!("  • {}: {}\n", skill.name, skill.description));
103            }
104            output.push('\n');
105        }
106
107        // 5. 工作流列表
108        if let Some(project_path) = ctx.project_path {
109            let registry = WorkflowRegistry::new(Some(project_path));
110            if !registry.is_empty() {
111                output.push_str(&format!(
112                    "⚡ **可用工作流** (共 {} 个)\n",
113                    registry.list().len()
114                ));
115                for info in registry.list().iter().take(5) {
116                    output.push_str(&format!("  • {}: ", info.id));
117                    if let Some(ref desc) = info.description {
118                        output.push_str(desc);
119                    } else {
120                        output.push_str(&info.name);
121                    }
122                    output.push('\n');
123                }
124                if registry.list().len() > 5 {
125                    output.push_str(&format!(
126                        "  ... 还有 {} 个工作流\n",
127                        registry.list().len() - 5
128                    ));
129                }
130                output.push('\n');
131            }
132        }
133
134        // 6. 完整 System Prompt
135        output.push_str("📜 **完整 System Prompt**\n");
136        output.push_str(&format!(
137            "长度: {} 字符\n\n",
138            ctx.agent.get_system_prompt().len()
139        ));
140        output.push_str("```\n");
141        output.push_str(ctx.agent.get_system_prompt());
142        output.push_str("\n```\n");
143
144        let _ = ctx
145            .event_tx
146            .send(crate::AgentEvent::progress(output, None))
147            .await;
148        false
149    }
150
151    /// 仅显示 System Prompt
152    async fn show_prompt(&self, ctx: &mut BackendContext<'_>) -> bool {
153        let prompt = ctx.agent.get_system_prompt();
154        let mut output = String::new();
155
156        output.push_str("📜 **System Prompt**\n");
157        output.push_str(&format!("长度: {} 字符\n\n", prompt.len()));
158        output.push_str("```\n");
159        output.push_str(prompt);
160        output.push_str("\n```\n");
161
162        let _ = ctx
163            .event_tx
164            .send(crate::AgentEvent::progress(output, None))
165            .await;
166        false
167    }
168
169    /// 仅显示工具列表
170    async fn show_tools(&self, ctx: &mut BackendContext<'_>) -> bool {
171        let tools = ctx.agent.get_tools();
172        let mut output = String::new();
173
174        output.push_str(&format!("🔧 **可用工具** (共 {} 个)\n\n", tools.len()));
175
176        for (idx, tool) in tools.iter().enumerate() {
177            let def = tool.definition();
178            output.push_str(&format!("{}. **{}**\n", idx + 1, def.name));
179            output.push_str(&format!("   {}\n\n", def.description));
180        }
181
182        let _ = ctx
183            .event_tx
184            .send(crate::AgentEvent::progress(output, None))
185            .await;
186        false
187    }
188
189    /// 仅显示统计信息
190    async fn show_stats(&self, ctx: &mut BackendContext<'_>) -> bool {
191        let mut output = String::new();
192
193        // 系统信息
194        output.push_str("🖥️  **系统环境**\n");
195        output.push_str(&format!(
196            "  • 操作系统: {} ({})\n",
197            std::env::consts::OS,
198            std::env::consts::ARCH
199        ));
200        output.push_str(&format!(
201            "  • 当前目录: {:?}\n",
202            ctx.project_path.unwrap_or(&std::path::PathBuf::new())
203        ));
204        output.push_str(&format!("  • 当前模型: {}\n\n", ctx.model));
205
206        // Token 统计
207        let (input_tokens, output_tokens) = ctx.agent.get_token_counts();
208        output.push_str("📊 **Token 统计**\n");
209        output.push_str(&format!("  • 输入: {} tokens\n", input_tokens));
210        output.push_str(&format!("  • 输出: {} tokens\n", output_tokens));
211        output.push_str(&format!(
212            "  • 总计: {} tokens\n\n",
213            input_tokens + output_tokens
214        ));
215
216        // 工具统计
217        let tools = ctx.agent.get_tools();
218        output.push_str("🔧 **资源统计**\n");
219        output.push_str(&format!("  • 可用工具: {} 个\n", tools.len()));
220        output.push_str(&format!("  • 可用技能: {} 个\n", ctx.skills.len()));
221
222        // 工作流统计
223        if let Some(project_path) = ctx.project_path {
224            let registry = WorkflowRegistry::new(Some(project_path));
225            output.push_str(&format!("  • 可用工作流: {} 个\n", registry.list().len()));
226        } else {
227            output.push_str("  • 可用工作流: 0 个\n");
228        }
229
230        output.push_str(&format!(
231            "  • System Prompt 长度: {} 字符\n",
232            ctx.agent.get_system_prompt().len()
233        ));
234
235        let _ = ctx
236            .event_tx
237            .send(crate::AgentEvent::progress(output, None))
238            .await;
239        false
240    }
241
242    /// 仅显示技能列表
243    async fn show_skills(&self, ctx: &mut BackendContext<'_>) -> bool {
244        let mut output = String::new();
245
246        if ctx.skills.is_empty() {
247            output.push_str("📚 **可用技能**: 无\n");
248        } else {
249            output.push_str(&format!("📚 **可用技能** (共 {} 个)\n\n", ctx.skills.len()));
250            for (idx, skill) in ctx.skills.iter().enumerate() {
251                output.push_str(&format!("{}. **{}**\n", idx + 1, skill.name));
252                output.push_str(&format!("   {}\n\n", skill.description));
253            }
254        }
255
256        let _ = ctx
257            .event_tx
258            .send(crate::AgentEvent::progress(output, None))
259            .await;
260        false
261    }
262
263    /// 显示工作流列表
264    async fn show_workflows(&self, ctx: &mut BackendContext<'_>) -> bool {
265        let mut output = String::new();
266
267        if let Some(project_path) = ctx.project_path {
268            let registry = WorkflowRegistry::new(Some(project_path));
269            let workflows = registry.list();
270
271            if workflows.is_empty() {
272                output.push_str("⚡ **可用工作流**: 无\n");
273                output.push_str("\n提示:在项目 .matrix/workflows/ 目录下创建 workflow.yaml 文件来添加自定义工作流。\n");
274            } else {
275                output.push_str(&format!(
276                    "⚡ **可用工作流** (共 {} 个)\n\n",
277                    workflows.len()
278                ));
279                for (idx, info) in workflows.iter().enumerate() {
280                    output.push_str(&format!("{}. **{}** (`{}`)\n", idx + 1, info.name, info.id));
281                    if let Some(ref desc) = info.description {
282                        output.push_str(&format!("   {}\n", desc));
283                    }
284                    if !info.required_inputs.is_empty() {
285                        output.push_str(&format!(
286                            "   需要输入: {}\n",
287                            info.required_inputs.join(", ")
288                        ));
289                    }
290                    output.push('\n');
291                }
292                output.push_str(
293                    "调用方式: 使用 workflow_run 工具,传入 workflow_id 和可选的 inputs 参数。\n",
294                );
295            }
296        } else {
297            output.push_str("⚡ **可用工作流**: 无项目路径\n");
298            output.push_str("\n提示:需要在项目目录下才能查看工作流。\n");
299        }
300
301        let _ = ctx
302            .event_tx
303            .send(crate::AgentEvent::progress(output, None))
304            .await;
305        false
306    }
307
308    /// 显示配置信息(隐藏敏感信息)
309    async fn show_config(&self, ctx: &mut BackendContext<'_>) -> bool {
310        let mut output = String::new();
311
312        output.push_str("⚙️  **配置信息**\n\n");
313
314        // Provider 信息
315        if let Some(ref provider) = ctx.config.provider {
316            output.push_str(&format!("  • Provider: {}\n", provider));
317        } else {
318            output.push_str("  • Provider: anthropic (默认)\n");
319        }
320
321        // Model 信息
322        if let Some(ref model) = ctx.config.model {
323            output.push_str(&format!("  • Model: {}\n", model));
324        } else {
325            output.push_str(&format!("  • Model: {} (默认)\n", ctx.model));
326        }
327
328        // Base URL
329        if let Some(ref base_url) = ctx.config.base_url {
330            output.push_str(&format!("  • Base URL: {}\n", base_url));
331        }
332
333        // API Key(隐藏)
334        if ctx.config.api_key.is_some() {
335            output.push_str("  • API Key: 已设置(已隐藏)\n");
336        } else {
337            output.push_str("  • API Key: 未设置\n");
338        }
339
340        // 其他配置
341        output.push_str(&format!("  • Extended Thinking: {}\n", ctx.config.think));
342        output.push_str(&format!(
343            "  • Markdown Rendering: {}\n",
344            ctx.config.markdown
345        ));
346        output.push_str(&format!("  • Max Tokens: {}\n", ctx.config.max_tokens));
347
348        if let Some(ref context_size) = ctx.config.context_size {
349            output.push_str(&format!("  • Context Size: {}\n", context_size));
350        }
351
352        // Approve Mode
353        if let Some(ref approve_mode) = ctx.config.approve_mode {
354            output.push_str(&format!("  • Approve Mode: {}\n", approve_mode));
355        }
356
357        // Multi-model
358        if let Some(ref multi_model) = ctx.config.multi_model {
359            output.push_str(&format!("  • Multi-Model: {}\n", multi_model));
360        }
361
362        if let Some(ref plan_model) = ctx.config.plan_model {
363            output.push_str(&format!("  • Plan Model: {}\n", plan_model));
364        }
365
366        if let Some(ref compress_model) = ctx.config.compress_model {
367            output.push_str(&format!("  • Compress Model: {}\n", compress_model));
368        }
369
370        if let Some(ref fast_model) = ctx.config.fast_model {
371            output.push_str(&format!("  • Fast Model: {}\n", fast_model));
372        }
373
374        // MCP Servers
375        if let Some(ref mcp_servers) = ctx.config.mcp_servers {
376            if !mcp_servers.is_empty() {
377                output.push_str(&format!("\n  • MCP Servers: {} 个\n", mcp_servers.len()));
378                for (name, _config) in mcp_servers.iter().take(5) {
379                    output.push_str(&format!("    - {}\n", name));
380                }
381                if mcp_servers.len() > 5 {
382                    output.push_str(&format!("    ... 还有 {} 个\n", mcp_servers.len() - 5));
383                }
384            }
385        }
386
387        // Config 路径
388        output.push_str("\n📁 **配置文件路径**\n");
389        if let Some(path) = crate::Config::matrix_config_path() {
390            output.push_str(&format!("  • Matrix Config: {:?}\n", path));
391        }
392        if let Some(path) = crate::Config::claude_settings_path() {
393            output.push_str(&format!("  • Claude Settings: {:?}\n", path));
394        }
395
396        let _ = ctx
397            .event_tx
398            .send(crate::AgentEvent::progress(output, None))
399            .await;
400        false
401    }
402
403    /// 显示环境变量(隐藏敏感信息)
404    async fn show_env(&self, ctx: &mut BackendContext<'_>) -> bool {
405        let mut output = String::new();
406
407        output.push_str("🌍 **环境变量**\n\n");
408
409        // API 相关环境变量
410        let api_key = std::env::var("API_KEY").ok();
411        let anthropic_token = std::env::var("ANTHROPIC_AUTH_TOKEN").ok();
412        let base_url = std::env::var("BASE_URL").ok();
413        let anthropic_base = std::env::var("ANTHROPIC_BASE_URL").ok();
414        let model = std::env::var("MODEL").ok();
415        let anthropic_model = std::env::var("ANTHROPIC_MODEL").ok();
416
417        // API Key(隐藏)
418        if api_key.is_some() || anthropic_token.is_some() {
419            output.push_str("  • API_KEY / ANTHROPIC_AUTH_TOKEN: 已设置(已隐藏)\n");
420        } else {
421            output.push_str("  • API_KEY / ANTHROPIC_AUTH_TOKEN: 未设置\n");
422        }
423
424        // Base URL
425        if let Some(url) = base_url {
426            output.push_str(&format!("  • BASE_URL: {}\n", url));
427        } else if let Some(url) = anthropic_base {
428            output.push_str(&format!("  • ANTHROPIC_BASE_URL: {}\n", url));
429        } else {
430            output.push_str("  • BASE_URL: 未设置(使用默认)\n");
431        }
432
433        // Model
434        if let Some(m) = model {
435            output.push_str(&format!("  • MODEL: {}\n", m));
436        } else if let Some(m) = anthropic_model {
437            output.push_str(&format!("  • ANTHROPIC_MODEL: {}\n", m));
438        } else {
439            output.push_str("  • MODEL: 未设置(使用配置或默认)\n");
440        }
441
442        // 其他常用环境变量
443        let provider = std::env::var("PROVIDER").ok();
444        if let Some(p) = provider {
445            output.push_str(&format!("  • PROVIDER: {}\n", p));
446        }
447
448        let max_tokens = std::env::var("MAX_TOKENS").ok();
449        if let Some(t) = max_tokens {
450            output.push_str(&format!("  • MAX_TOKENS: {}\n", t));
451        }
452
453        let think = std::env::var("THINK").ok();
454        if let Some(t) = think {
455            output.push_str(&format!("  • THINK: {}\n", t));
456        }
457
458        // 系统环境变量
459        output.push_str("\n💻 **系统环境**\n");
460        if let Ok(home) = std::env::var("HOME") {
461            output.push_str(&format!("  • HOME: {}\n", home));
462        } else if let Ok(userprofile) = std::env::var("USERPROFILE") {
463            output.push_str(&format!("  • USERPROFILE: {}\n", userprofile));
464        }
465
466        if let Ok(pwd) = std::env::var("PWD") {
467            output.push_str(&format!("  • PWD: {}\n", pwd));
468        }
469
470        if let Ok(path) = std::env::var("PATH") {
471            // 截断过长的 PATH
472            if path.len() > 100 {
473                output.push_str(&format!("  • PATH: {}... (已截断)\n", &path[..100]));
474            } else {
475                output.push_str(&format!("  • PATH: {}\n", path));
476            }
477        }
478
479        // 优先级说明
480        output.push_str("\n📋 **配置优先级** (从高到低)\n");
481        output.push_str("  1. 环境变量 (API_KEY, BASE_URL, MODEL 等)\n");
482        output.push_str("  2. ~/.matrix/config.json (MatrixCode 配置)\n");
483        output.push_str("  3. ~/.claude/settings.json (Claude Code 配置)\n");
484        output.push_str("  4. 默认值\n");
485
486        let _ = ctx
487            .event_tx
488            .send(crate::AgentEvent::progress(output, None))
489            .await;
490        false
491    }
492
493    /// 显示帮助
494    async fn show_help(&self, ctx: &mut BackendContext<'_>) -> bool {
495        let help = r#"🖥️  **/system 命令帮助**
496
497用法:
498  /system           显示完整系统信息(默认)
499  /system status    显示完整系统信息
500  /system prompt    仅显示 System Prompt
501  /system tools      仅显示工具列表(含描述)
502  /system stats      仅显示统计信息
503  /system skills     仅显示技能列表
504  /system workflows  仅显示工作流列表
505  /system config     仅显示配置信息(隐藏敏感信息)
506  /system env        仅显示环境变量(隐藏敏感信息)
507  /system help       显示此帮助信息
508
509说明:
510  - prompt: 查看发送给 LLM 的完整系统提示词
511  - tools: 查看所有可用工具及其描述
512  - stats: 查看运行时统计(Token、工具数、工作流数等)
513  - skills: 查看当前加载的技能
514  - workflows: 查看项目中的自动化工作流
515  - config: 查看当前配置(API Key 等敏感信息已隐藏)
516  - env: 查看环境变量(API Key 等敏感信息已隐藏)
517
518示例:
519  /system prompt    # 调试 Prompt 问题
520  /system config    # 检查配置是否正确
521  /system workflows # 查看可用的工作流
522"#;
523
524        let _ = ctx
525            .event_tx
526            .send(crate::AgentEvent::progress(help.to_string(), None))
527            .await;
528        false
529    }
530}