diff --git a/Cargo.lock b/Cargo.lock index 09c665d4..025aafb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,6 +1221,27 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9b18233253483ce2f65329a24072ec414db782531bdbb7d0bbc4bd2ce6b7e21" +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom 7.1.3", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1525,6 +1546,7 @@ dependencies = [ "clap", "clap_complete", "clap_mangen", + "color-print", "cortex-app-server", "cortex-common", "cortex-engine", diff --git a/cortex-cli/Cargo.toml b/cortex-cli/Cargo.toml index d01efa0c..72444c7d 100644 --- a/cortex-cli/Cargo.toml +++ b/cortex-cli/Cargo.toml @@ -39,6 +39,9 @@ cortex-share = { path = "../cortex-share" } clap = { workspace = true } clap_complete = { workspace = true } clap_mangen = { workspace = true } + +# For colored help output +color-print = "0.3" tokio = { workspace = true, features = ["full"] } anyhow = { workspace = true } tracing = { workspace = true } diff --git a/cortex-cli/src/main.rs b/cortex-cli/src/main.rs index 9fd9098b..073dac22 100644 --- a/cortex-cli/src/main.rs +++ b/cortex-cli/src/main.rs @@ -10,11 +10,32 @@ //! - Shell completions use anyhow::{Result, bail}; +use clap::builder::styling::{AnsiColor, Effects, Styles}; use clap::{Args, CommandFactory, Parser, Subcommand}; use clap_complete::{Shell, generate}; use std::io; use std::path::PathBuf; +/// Cortex CLI styled help theme. +/// Uses a modern color scheme with cyan accents for a beautiful terminal experience. +fn get_styles() -> Styles { + Styles::styled() + // Headers (USAGE, COMMANDS, OPTIONS) - Bold cyan + .header(AnsiColor::Cyan.on_default() | Effects::BOLD) + // Usage line - Green + .usage(AnsiColor::Green.on_default() | Effects::BOLD) + // Literals (command names, flag names) - Bold green + .literal(AnsiColor::Green.on_default() | Effects::BOLD) + // Placeholders (, [ARGS]) - Yellow + .placeholder(AnsiColor::Yellow.on_default()) + // Errors - Bold red + .error(AnsiColor::Red.on_default() | Effects::BOLD) + // Valid values - Cyan + .valid(AnsiColor::Cyan.on_default()) + // Invalid values - Yellow + .invalid(AnsiColor::Yellow.on_default()) +} + use cortex_cli::acp_cmd::AcpCli; use cortex_cli::agent_cmd::AgentCli; use cortex_cli::debug_cmd::DebugCli; @@ -82,29 +103,48 @@ impl LogLevel { } /// After-help section with environment variables documentation. -const AFTER_HELP: &str = "\ -ENVIRONMENT VARIABLES: - CORTEX_HOME Override config directory (default: ~/.config/cortex) - CORTEX_API_KEY API key (alternative to --with-api-key) - CORTEX_MODEL Default model (alternative to --model) - CORTEX_LOG_LEVEL Log verbosity (error, warn, info, debug, trace) - NO_COLOR Disable colored output (set to '1' or 'true') - VISUAL Editor for /edit command (fallback: EDITOR) - EDITOR Fallback editor if VISUAL is not set - -CONFIGURATION: - Config file: ~/.config/cortex/config.toml - Sessions: ~/.local/share/cortex/sessions/ - Logs: ~/.cache/cortex/logs/ - Agents: ~/.cortex/agents/ (personal), .cortex/agents/ (project) - -EXAMPLES: - cortex Start interactive TUI - cortex \"fix the bug\" Start TUI with initial prompt - cortex run \"explain this\" Non-interactive single request - cortex exec \"run tests\" Headless execution for CI/CD - cortex resume --last Continue most recent session -"; +const AFTER_HELP: &str = color_print::cstr!( + r#" +ENVIRONMENT VARIABLES: + CORTEX_HOME Override config directory (default: ~/.config/cortex) + CORTEX_API_KEY API key (alternative to --with-api-key) + CORTEX_MODEL Default model (alternative to --model) + CORTEX_LOG_LEVEL Log verbosity (error, warn, info, debug, trace) + NO_COLOR Disable colored output (set to '1' or 'true') + VISUAL Editor for /edit command (fallback: EDITOR) + EDITOR Fallback editor if VISUAL is not set + +PATHS: + Config file ~/.config/cortex/config.toml + Sessions ~/.local/share/cortex/sessions/ + Logs ~/.cache/cortex/logs/ + Agents ~/.cortex/agents/ (personal), .cortex/agents/ (project) + +QUICK START: + cortex Start interactive TUI + cortex "fix the bug" Start TUI with initial prompt + cortex run "explain this" Non-interactive single request + cortex exec "run tests" Headless execution for CI/CD + cortex resume --last Continue most recent session + +LEARN MORE: + Documentation: https://docs.cortex.foundation + Report issues: https://github.com/CortexLM/cortex-cli/issues +"# +); + +/// Before-help section with ASCII art banner. +const BEFORE_HELP: &str = color_print::cstr!( + r#" + ██████╗ ██████╗ ██████╗ ████████╗███████╗██╗ ██╗ + ██╔════╝██╔═══██╗██╔══██╗╚══██╔══╝██╔════╝╚██╗██╔╝ + ██║ ██║ ██║██████╔╝ ██║ █████╗ ╚███╔╝ + ██║ ██║ ██║██╔══██╗ ██║ ██╔══╝ ██╔██╗ + ╚██████╗╚██████╔╝██║ ██║ ██║ ███████╗██╔╝ ██╗ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + AI-Powered Coding Agent +"# +); /// Cortex CLI - AI Coding Agent /// @@ -114,8 +154,10 @@ EXAMPLES: #[command(author, version)] #[command(about = "Cortex - AI Coding Agent", long_about = None)] #[command( + styles = get_styles(), subcommand_negates_reqs = true, override_usage = "cortex [OPTIONS] [PROMPT]\n cortex [OPTIONS] [ARGS]", + before_help = BEFORE_HELP, after_help = AFTER_HELP )] struct Cli { @@ -132,8 +174,11 @@ struct Cli { /// Arguments for interactive mode. #[derive(Args, Debug, Default)] struct InteractiveArgs { - /// Model to use - #[arg(short, long)] + // ═══════════════════════════════════════════════════════════════════════════ + // Model Configuration + // ═══════════════════════════════════════════════════════════════════════════ + /// Model to use (e.g., claude-sonnet-4-20250514, gpt-4o, gemini-2.0-flash) + #[arg(short, long, help_heading = "Model Configuration")] model: Option, /// Use open-source/local LLM providers instead of cloud APIs. @@ -143,15 +188,22 @@ struct InteractiveArgs { /// llamacpp - llama.cpp server /// vllm - vLLM inference server /// openrouter - OpenRouter API (cloud, supports many OSS models) - #[arg(long = "oss", default_value_t = false)] + #[arg( + long = "oss", + default_value_t = false, + help_heading = "Model Configuration" + )] oss: bool, /// Configuration profile from config.toml - #[arg(long = "profile", short = 'p')] + #[arg(long = "profile", short = 'p', help_heading = "Model Configuration")] config_profile: Option, + // ═══════════════════════════════════════════════════════════════════════════ + // Security & Sandbox + // ═══════════════════════════════════════════════════════════════════════════ /// Select the sandbox policy for shell commands - #[arg(long = "sandbox", short = 's')] + #[arg(long = "sandbox", short = 's', help_heading = "Security")] sandbox_mode: Option, /// Set the approval policy for tool executions. @@ -160,7 +212,12 @@ struct InteractiveArgs { /// medium - Ask for approval only for risky operations /// low - Rarely ask for approval (only for dangerous operations) /// yolo - Never ask for approval (DANGEROUS: use with caution) - #[arg(long = "ask-for-approval", short = 'a', value_name = "POLICY")] + #[arg( + long = "ask-for-approval", + short = 'a', + value_name = "POLICY", + help_heading = "Security" + )] approval_policy: Option, /// Enable fully automatic mode with sandboxed execution. @@ -168,7 +225,7 @@ struct InteractiveArgs { /// The sandbox ensures operations are confined to the workspace directory. /// Best suited for automated CI/CD pipelines and non-destructive scripted workflows. /// For interactive use, consider --ask-for-approval=medium instead. - #[arg(long = "full-auto", default_value_t = false)] + #[arg(long = "full-auto", default_value_t = false, help_heading = "Security")] full_auto: bool, /// Skip all confirmation prompts and execute commands without sandboxing. DANGEROUS! @@ -176,14 +233,34 @@ struct InteractiveArgs { long = "dangerously-bypass-approvals-and-sandbox", alias = "yolo", default_value_t = false, - conflicts_with_all = ["approval_policy", "full_auto"] + conflicts_with_all = ["approval_policy", "full_auto"], + help_heading = "Security" )] dangerously_bypass_approvals_and_sandbox: bool, + // ═══════════════════════════════════════════════════════════════════════════ + // Workspace & Files + // ═══════════════════════════════════════════════════════════════════════════ /// Tell the agent to use the specified directory as its working root - #[arg(long = "cd", short = 'C', value_name = "DIR")] + #[arg( + long = "cd", + short = 'C', + value_name = "DIR", + help_heading = "Workspace" + )] cwd: Option, + /// Additional directories that should be writable + #[arg(long = "add-dir", value_name = "DIR", help_heading = "Workspace")] + add_dir: Vec, + + /// Image files to attach to the initial prompt + #[arg(long = "image", short = 'i', value_delimiter = ',', num_args = 1.., help_heading = "Workspace")] + images: Vec, + + // ═══════════════════════════════════════════════════════════════════════════ + // Features + // ═══════════════════════════════════════════════════════════════════════════ /// Enable web search capability for the agent. /// When enabled, the agent can: /// - Search the web for up-to-date information and documentation @@ -191,20 +268,21 @@ struct InteractiveArgs { /// - Look up library/API documentation /// - Research error messages and solutions /// Requires network access. Search results are automatically filtered for relevance. - #[arg(long = "search", default_value_t = false)] + #[arg(long = "search", default_value_t = false, help_heading = "Features")] web_search: bool, - /// Additional directories that should be writable - #[arg(long = "add-dir", value_name = "DIR")] - add_dir: Vec, - - /// Image files to attach to the initial prompt - #[arg(long = "image", short = 'i', value_delimiter = ',', num_args = 1..)] - images: Vec, - + // ═══════════════════════════════════════════════════════════════════════════ + // Debugging & Output + // ═══════════════════════════════════════════════════════════════════════════ /// Set log verbosity level (error, warn, info, debug, trace) /// Can also be set via CORTEX_LOG_LEVEL environment variable - #[arg(long = "log-level", short = 'L', value_enum, default_value = "info")] + #[arg( + long = "log-level", + short = 'L', + value_enum, + default_value = "info", + help_heading = "Debugging" + )] log_level: LogLevel, /// Enable debug logging (deprecated: use --log-level debug instead) @@ -216,91 +294,167 @@ struct InteractiveArgs { prompt: Vec, } +// Command category display names for styled help output +const CAT_EXECUTION: &str = "Execution Modes"; +const CAT_SESSION: &str = "Session Management"; +const CAT_AUTH: &str = "Authentication"; +const CAT_EXTENSION: &str = "Extensibility"; +const CAT_CONFIG: &str = "Configuration"; +const CAT_UTILITIES: &str = "Utilities"; +const CAT_ADVANCED: &str = "Advanced"; + #[derive(Subcommand)] enum Commands { + // ═══════════════════════════════════════════════════════════════════════════ + // Execution Modes + // ═══════════════════════════════════════════════════════════════════════════ /// Run Cortex non-interactively with advanced options - #[command(visible_alias = "r")] + #[command(visible_alias = "r", display_order = 1)] + #[command(next_help_heading = CAT_EXECUTION)] Run(RunCli), - /// Execute a single command in non-interactive (headless) mode. - /// Designed for CI/CD pipelines, shell scripts, and batch processing. - /// Supports autonomy levels (--auto), output formats, and multi-turn conversations. - #[command(visible_alias = "e")] + /// Execute in headless mode (for CI/CD, scripts, automation) + #[command(visible_alias = "e", display_order = 2)] + #[command(next_help_heading = CAT_EXECUTION)] Exec(ExecCli), - /// Manage login - Login(LoginCommand), - - /// Remove stored authentication credentials - Logout(LogoutCommand), - - /// Manage MCP servers - Mcp(McpCli), - - /// Manage agents (list, create, show) - Agent(AgentCli), - - /// Run the MCP server (stdio transport) - McpServer, - - /// Generate shell completion scripts - Completion(CompletionCommand), - - /// Run commands within a Cortex-provided sandbox - #[command(visible_alias = "sb")] - Sandbox(SandboxArgs), - + // ═══════════════════════════════════════════════════════════════════════════ + // Session Management + // ═══════════════════════════════════════════════════════════════════════════ /// Resume a previous interactive session + #[command(display_order = 10)] + #[command(next_help_heading = CAT_SESSION)] Resume(ResumeCommand), /// List previous sessions + #[command(display_order = 11)] + #[command(next_help_heading = CAT_SESSION)] Sessions(SessionsCommand), /// Export a session to JSON format + #[command(display_order = 12)] + #[command(next_help_heading = CAT_SESSION)] Export(ExportCommand), /// Import a session from JSON file or URL + #[command(display_order = 13)] + #[command(next_help_heading = CAT_SESSION)] Import(ImportCommand), - /// Show configuration - Config(ConfigCommand), + // ═══════════════════════════════════════════════════════════════════════════ + // Authentication + // ═══════════════════════════════════════════════════════════════════════════ + /// Authenticate with Cortex API + #[command(display_order = 20)] + #[command(next_help_heading = CAT_AUTH)] + Login(LoginCommand), - /// Inspect feature flags - Features(FeaturesCommand), + /// Remove stored authentication credentials + #[command(display_order = 21)] + #[command(next_help_heading = CAT_AUTH)] + Logout(LogoutCommand), - /// Run the HTTP API server (for desktop/web integration) - Serve(ServeCommand), + // ═══════════════════════════════════════════════════════════════════════════ + // Extensibility (Agents, MCP, Plugins) + // ═══════════════════════════════════════════════════════════════════════════ + /// Manage agents (list, create, show) + #[command(display_order = 30)] + #[command(next_help_heading = CAT_EXTENSION)] + Agent(AgentCli), - /// List available models - Models(ModelsCli), + /// Manage MCP (Model Context Protocol) servers + #[command(display_order = 31)] + #[command(next_help_heading = CAT_EXTENSION)] + Mcp(McpCli), - /// Check for and install updates - Upgrade(UpgradeCli), + /// Run the MCP server (stdio transport) + #[command(display_order = 32, hide = true)] + #[command(next_help_heading = CAT_EXTENSION)] + McpServer, - /// Uninstall Cortex CLI - Uninstall(UninstallCli), + /// Start ACP server for IDE integration (e.g., Zed) + #[command(display_order = 33)] + #[command(next_help_heading = CAT_EXTENSION)] + Acp(AcpCli), - /// Show usage statistics - Stats(StatsCli), + // ═══════════════════════════════════════════════════════════════════════════ + // Configuration + // ═══════════════════════════════════════════════════════════════════════════ + /// Show or edit configuration + #[command(display_order = 40)] + #[command(next_help_heading = CAT_CONFIG)] + Config(ConfigCommand), + + /// List available models + #[command(display_order = 41)] + #[command(next_help_heading = CAT_CONFIG)] + Models(ModelsCli), + + /// Inspect feature flags + #[command(display_order = 42)] + #[command(next_help_heading = CAT_CONFIG)] + Features(FeaturesCommand), - /// GitHub Actions integration - #[command(visible_alias = "gh")] + // ═══════════════════════════════════════════════════════════════════════════ + // Utilities + // ═══════════════════════════════════════════════════════════════════════════ + /// GitHub integration (actions, workflows) + #[command(visible_alias = "gh", display_order = 50)] + #[command(next_help_heading = CAT_UTILITIES)] Github(GitHubCli), /// Checkout a pull request + #[command(display_order = 51)] + #[command(next_help_heading = CAT_UTILITIES)] Pr(PrCli), /// Scrape web content to markdown/text/html + #[command(display_order = 52)] + #[command(next_help_heading = CAT_UTILITIES)] Scrape(ScrapeCommand), - /// Start ACP server for IDE integration (e.g., Zed) - Acp(AcpCli), + /// Show usage statistics + #[command(display_order = 53)] + #[command(next_help_heading = CAT_UTILITIES)] + Stats(StatsCli), - /// Debug and diagnostic commands - Debug(DebugCli), + /// Generate shell completion scripts + #[command(display_order = 54)] + #[command(next_help_heading = CAT_UTILITIES)] + Completion(CompletionCommand), + + // ═══════════════════════════════════════════════════════════════════════════ + // Advanced + // ═══════════════════════════════════════════════════════════════════════════ + /// Run commands within a Cortex-provided sandbox + #[command(visible_alias = "sb", display_order = 60)] + #[command(next_help_heading = CAT_ADVANCED)] + Sandbox(SandboxArgs), + + /// Run the HTTP API server (for desktop/web integration) + #[command(display_order = 61)] + #[command(next_help_heading = CAT_ADVANCED)] + Serve(ServeCommand), /// Discover Cortex servers on the local network + #[command(display_order = 62)] + #[command(next_help_heading = CAT_ADVANCED)] Servers(ServersCommand), + + /// Check for and install updates + #[command(display_order = 70)] + #[command(next_help_heading = CAT_ADVANCED)] + Upgrade(UpgradeCli), + + /// Uninstall Cortex CLI + #[command(display_order = 71)] + #[command(next_help_heading = CAT_ADVANCED)] + Uninstall(UninstallCli), + + /// Debug and diagnostic commands + #[command(display_order = 99)] + #[command(next_help_heading = CAT_ADVANCED)] + Debug(DebugCli), } // Note: ExecCommand has been replaced by the comprehensive ExecCli from exec_cmd module.