Skip to main content

mermaid_cli/cli/
commands.rs

1use anyhow::Result;
2
3use crate::{
4    app::{get_config_dir, init_config, load_config},
5    models::ModelFactory,
6    ollama::{is_installed as is_ollama_installed, list_models_async as get_ollama_models},
7};
8
9use super::Commands;
10
11/// Handle CLI subcommands
12/// Returns Ok(true) if the command was handled and we should exit
13/// Returns Ok(false) if we should continue to the main application
14pub async fn handle_command(command: &Commands) -> Result<bool> {
15    match command {
16        Commands::Init => {
17            println!("Initializing Mermaid configuration...");
18            init_config()?;
19            println!("Configuration initialized successfully!");
20            Ok(true)
21        },
22        Commands::List => {
23            list_models().await?;
24            Ok(true)
25        },
26        Commands::Version => {
27            show_version();
28            Ok(true)
29        },
30        Commands::Status => {
31            show_status().await?;
32            Ok(true)
33        },
34        Commands::Add { name } => {
35            crate::mcp::add_server(name).await?;
36            Ok(true)
37        },
38        Commands::Remove { name } => {
39            crate::mcp::remove_server(name).await?;
40            Ok(true)
41        },
42        Commands::Mcp => {
43            show_mcp_servers();
44            Ok(true)
45        },
46        Commands::Chat => Ok(false),       // Continue to chat interface
47        Commands::Run { .. } => Ok(false), // Handled by main.rs
48    }
49}
50
51/// List available models across all backends
52pub async fn list_models() -> Result<()> {
53    let models = ModelFactory::list_all_models().await?;
54
55    if models.is_empty() {
56        println!("No models found across any backends");
57    } else {
58        println!("Available models:");
59        for model in models {
60            println!("  - {}", model);
61        }
62    }
63    Ok(())
64}
65
66/// Show version information
67pub fn show_version() {
68    println!("Mermaid v{}", env!("CARGO_PKG_VERSION"));
69    println!("   An open-source, model-agnostic AI pair programmer");
70}
71
72/// Show configured MCP servers
73fn show_mcp_servers() {
74    let config = load_config().unwrap_or_default();
75
76    if config.mcp_servers.is_empty() {
77        println!("No MCP servers configured.\n");
78        println!("Add one with: mermaid add <name>");
79        println!("Examples:");
80        println!("  mermaid add context7     # Library documentation");
81        println!("  mermaid add github       # GitHub API integration");
82        println!("  mermaid add playwright   # Browser automation");
83        println!("  mermaid add memory       # Persistent knowledge graph");
84        return;
85    }
86
87    println!("Configured MCP servers:\n");
88    for (name, server_cfg) in &config.mcp_servers {
89        let package = server_cfg
90            .args
91            .iter()
92            .find(|a| !a.starts_with('-'))
93            .unwrap_or(&server_cfg.command);
94        let env_keys: Vec<&String> = server_cfg.env.keys().collect();
95        let env_display = if env_keys.is_empty() {
96            String::new()
97        } else {
98            format!(" (env: {})", env_keys.iter().map(|k| k.as_str()).collect::<Vec<_>>().join(", "))
99        };
100        println!("  {} — {}{}", name, package, env_display);
101    }
102    println!("\nManage with: mermaid add <name> / mermaid remove <name>");
103}
104
105/// Show status of all dependencies
106async fn show_status() -> Result<()> {
107    println!("Mermaid Status:");
108    println!();
109
110    // Check available backends (use user's config for host/port)
111    let status_config = load_config().unwrap_or_default();
112    let factory = ModelFactory::from_config(&status_config);
113    let backends = factory.available_providers_pub().await;
114    if backends.is_empty() {
115        println!("  [WARNING] Backends: None available");
116    } else {
117        println!("  [OK] Backends: {}", backends.join(", "));
118    }
119
120    // Check Ollama
121    if is_ollama_installed() {
122        let models = get_ollama_models().await.unwrap_or_default();
123        if models.is_empty() {
124            println!("  [WARNING] Ollama: Installed (no models)");
125        } else {
126            println!("  [OK] Ollama: Running ({} models installed)", models.len());
127            for model in models.iter().take(3) {
128                println!("      - {}", model);
129            }
130            if models.len() > 3 {
131                println!("      ... and {} more", models.len() - 3);
132            }
133        }
134    } else {
135        println!("  [ERROR] Ollama: Not installed");
136    }
137
138    // Check configuration (uses platform-specific path via ProjectDirs)
139    if let Ok(config_dir) = get_config_dir() {
140        let config_path = config_dir.join("config.toml");
141        if config_path.exists() {
142            println!("  [OK] Configuration: {}", config_path.display());
143        } else {
144            println!("  [WARNING] Configuration: Not found (using defaults)");
145        }
146    }
147
148    // MCP Servers
149    if let Ok(cfg) = load_config() {
150        if cfg.mcp_servers.is_empty() {
151            println!("  [INFO] MCP Servers: None configured (use 'mermaid add <name>')");
152        } else {
153            println!(
154                "  [OK] MCP Servers: {} configured",
155                cfg.mcp_servers.len()
156            );
157            for (name, server_cfg) in &cfg.mcp_servers {
158                println!(
159                    "      - {} ({})",
160                    name,
161                    server_cfg.args.get(1).unwrap_or(&server_cfg.command)
162                );
163            }
164        }
165    }
166
167    // Environment variables (for API providers)
168    println!("\n  Environment:");
169    if std::env::var("OPENROUTER_API_KEY").is_ok() {
170        println!("    - OPENROUTER_API_KEY: Set");
171    }
172    if std::env::var("OLLAMA_API_KEY").is_ok() {
173        println!("    - OLLAMA_API_KEY: Set (for Ollama Cloud)");
174    }
175
176    println!();
177    Ok(())
178}