mermaid-cli 0.10.2

Open-source AI pair programmer with agentic capabilities. Local-first with Ollama, native tool calling, and beautiful TUI.
Documentation
use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;

use crate::models::ReasoningLevel;

#[derive(Parser, Debug)]
#[command(name = "mermaid")]
#[command(version)]
#[command(about = "An open-source, model-agnostic AI pair programmer", long_about = None)]
#[command(after_help = TOP_LEVEL_HELP_AFTER)]
pub struct Cli {
    /// Model to use (e.g., qwen3-coder:30b, ollama/llama3)
    #[arg(short, long)]
    pub model: Option<String>,

    /// Reasoning depth (none, minimal, low, medium, high, max).
    /// Overrides the persisted default for this session; the slash
    /// command `/reasoning <level>` and Alt+T can change it at runtime.
    #[arg(long)]
    pub reasoning: Option<ReasoningLevel>,

    /// Project directory (defaults to current directory)
    #[arg(short, long)]
    pub path: Option<PathBuf>,

    /// Verbose output
    #[arg(short, long)]
    pub verbose: bool,

    /// Show session picker to choose a previous conversation
    #[arg(long, conflicts_with = "continue_session")]
    pub sessions: bool,

    /// Resume the last conversation instead of starting fresh
    #[arg(long = "continue", conflicts_with = "sessions")]
    pub continue_session: bool,

    /// Append every reducer `Msg` to a JSONL file at this path for
    /// debugging / post-mortem replay. Interactive mode only.
    #[arg(long, value_name = "FILE")]
    pub record: Option<PathBuf>,

    /// Replace Mermaid's default system prompt for this invocation
    #[arg(long, global = true, conflicts_with = "system_prompt_file")]
    pub system_prompt: Option<String>,

    /// Replace Mermaid's default system prompt with the contents of a file
    #[arg(
        long,
        value_name = "FILE",
        global = true,
        conflicts_with = "system_prompt"
    )]
    pub system_prompt_file: Option<PathBuf>,

    /// Append extra instructions after Mermaid's system prompt for this invocation
    #[arg(long, global = true)]
    pub append_system_prompt: Option<String>,

    /// Append extra instructions from a file after Mermaid's system prompt
    #[arg(long, value_name = "FILE", global = true)]
    pub append_system_prompt_file: Option<PathBuf>,

    #[command(subcommand)]
    pub command: Option<Commands>,
}

const TOP_LEVEL_HELP_AFTER: &str = "\
Common first run:
  mermaid doctor                         Check model, tools, safety, and project readiness
  mermaid                                Start the full-screen terminal coding agent
  mermaid run \"inspect this repo\"        Run one prompt headlessly
  mermaid self-test                      Run fast deterministic Mermaid self-tests

Command groups:
  Everyday: chat, run, doctor, status, list, self-test
  Model/context: models, model-info, --model, --reasoning, --system-prompt*
  Safety/recovery: approvals, approve, deny, checkpoints, restore
  Integrations: add, remove, mcp, cloud-setup, plugin, pr
  Advanced runtime: daemon, tasks, task, processes, logs, stop, restart, ports, pair";

#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Initialize configuration
    Init,
    /// List available models
    List,
    /// List model/provider capability records
    Models,
    /// Show static and cached capability info for a model id
    ModelInfo {
        /// Model id, e.g. openai/gpt-5.2
        model: String,
    },
    /// Start a chat session (default)
    Chat,
    /// Show version information
    Version,
    /// Check status of dependencies and backends
    Status,
    /// Check first-run readiness and explain what Mermaid can do now
    Doctor {
        /// Output format (text, json, markdown)
        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
        format: OutputFormat,
    },
    /// Run fast deterministic Mermaid self-tests
    SelfTest {
        /// Output format (text, json, markdown)
        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
        format: OutputFormat,
        /// Keep the temporary self-test workspace after the run
        #[arg(long)]
        keep_workspace: bool,
    },
    /// List durable runtime tasks
    Tasks {
        /// Maximum number of tasks to show
        #[arg(short, long, default_value_t = 20)]
        limit: usize,
    },
    /// Show one durable runtime task and its timeline
    Task {
        /// Task id
        id: String,
    },
    /// List Mermaid-managed background processes
    Processes {
        /// Maximum number of processes to show
        #[arg(short, long, default_value_t = 20)]
        limit: usize,
    },
    /// Print a managed process log
    Logs {
        /// Process id from `mermaid processes`
        id: String,
    },
    /// Stop a managed process
    Stop {
        /// Process id from `mermaid processes`
        id: String,
    },
    /// Restart a managed process
    Restart {
        /// Process id from `mermaid processes`
        id: String,
    },
    /// Open a URL, file, or managed process URL
    Open {
        /// URL, path, or process id
        target: String,
    },
    /// Show listening TCP ports
    Ports,
    /// List pending approvals
    Approvals,
    /// Approve a pending approval record
    Approve {
        /// Approval id
        id: String,
    },
    /// Deny a pending approval record
    Deny {
        /// Approval id
        id: String,
    },
    /// List recent persisted tool runs
    ToolRuns {
        /// Maximum number of tool runs to show
        #[arg(short, long, default_value_t = 20)]
        limit: usize,
    },
    /// List checkpoints
    Checkpoints {
        /// Maximum number of checkpoints to show
        #[arg(short, long, default_value_t = 20)]
        limit: usize,
    },
    /// Restore a checkpoint by id
    Restore {
        /// Checkpoint id
        id: String,
    },
    /// Manage Mermaid plugin bundles
    Plugin {
        #[command(subcommand)]
        command: PluginCommand,
    },
    /// Manage Mermaid's Linux background service
    Daemon {
        #[command(subcommand)]
        command: DaemonCommand,
    },
    /// Create a remote pairing token
    Pair {
        /// Human label for the remote client
        #[arg(long)]
        label: Option<String>,
    },
    /// Internal self-QA commands. Hidden from normal help output.
    #[command(hide = true)]
    Qa {
        #[command(subcommand)]
        command: QaCommand,
    },
    /// Add an MCP server (e.g., mermaid add context7)
    Add {
        /// MCP server name (e.g., context7, github, filesystem)
        name: String,
    },
    /// Remove a configured MCP server
    Remove {
        /// MCP server name to remove
        name: String,
    },
    /// List configured MCP servers
    Mcp,
    /// Create a pull/merge request from the current branch via the host CLI
    /// (`gh` for GitHub, `glab` for GitLab)
    Pr {
        #[command(subcommand)]
        command: PrCommand,
    },
    /// Configure Ollama Cloud API key (interactive prompt). Run this
    /// from your shell before starting mermaid — it reads stdin and
    /// doesn't work from inside the TUI.
    CloudSetup,
    /// Run a single prompt non-interactively
    Run {
        /// The prompt to execute
        prompt: String,

        /// Output format (text, json, markdown)
        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
        format: OutputFormat,

        /// Maximum tokens to generate
        #[arg(long)]
        max_tokens: Option<usize>,

        /// Don't execute agent actions (dry run)
        #[arg(long)]
        no_execute: bool,
    },
}

#[derive(Subcommand, Debug)]
pub enum PluginCommand {
    /// Install a plugin from a local path
    Install {
        /// Path containing plugin.toml
        path: PathBuf,
    },
    /// List installed plugins
    List,
    /// Enable an installed plugin
    Enable {
        /// Plugin id or name
        id: String,
    },
    /// Disable an installed plugin
    Disable {
        /// Plugin id or name
        id: String,
    },
    /// Validate a plugin manifest without installing
    Audit {
        /// Path containing plugin.toml
        path: PathBuf,
    },
}

/// Which Git hosting provider's CLI to drive.
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum GitHost {
    /// GitHub, via the `gh` CLI.
    Github,
    /// GitLab, via the `glab` CLI.
    Gitlab,
}

#[derive(Subcommand, Debug)]
pub enum PrCommand {
    /// Create a PR/MR from the current branch. Wraps `gh pr create` /
    /// `glab mr create`, reusing their existing authentication.
    Create {
        /// PR/MR title. Omitted → filled from the branch's commits.
        #[arg(short, long)]
        title: Option<String>,
        /// PR/MR body text.
        #[arg(short, long)]
        body: Option<String>,
        /// Read the body from a file (e.g. a saved review summary).
        #[arg(long, value_name = "FILE", conflicts_with = "body")]
        summary: Option<PathBuf>,
        /// Base branch to merge into (defaults to the host's default branch).
        #[arg(long)]
        base: Option<String>,
        /// Open as a draft.
        #[arg(long)]
        draft: bool,
        /// Open the creation page in a browser instead of creating directly.
        #[arg(long)]
        web: bool,
        /// Force a provider instead of auto-detecting from the `origin` remote.
        #[arg(long, value_enum)]
        provider: Option<GitHost>,
    },
}

#[derive(Subcommand, Debug)]
pub enum DaemonCommand {
    /// Install the systemd user service for this user
    Install {
        /// Start and enable the service after writing the unit
        #[arg(long)]
        start: bool,
        /// Overwrite an existing Mermaid service unit
        #[arg(long)]
        force: bool,
    },
    /// Remove the systemd user service for this user
    Uninstall,
    /// Start the background user service
    Start,
    /// Stop the background user service
    Stop,
    /// Restart the background user service
    Restart,
    /// Show background service status
    Status,
    /// Show background service logs
    Logs {
        /// Follow log output
        #[arg(short, long)]
        follow: bool,
        /// Number of log lines to show before following/exiting
        #[arg(short = 'n', long, default_value_t = 100)]
        lines: usize,
    },
    /// Print the generated service unit without installing it
    PrintUnit,
}

#[derive(Subcommand, Debug)]
pub enum QaCommand {
    /// Deterministically exercise context compaction without a real model.
    CompactSmoke {
        /// Number of synthetic user/assistant turns to seed
        #[arg(long, default_value_t = 6)]
        turns: usize,
        /// Output format
        #[arg(short, long, value_enum, default_value_t = OutputFormat::Json)]
        format: OutputFormat,
    },
}

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum OutputFormat {
    /// Plain text output
    Text,
    /// JSON structured output
    Json,
    /// Markdown formatted output
    Markdown,
}