Skip to main content

mermaid_cli/cli/
args.rs

1use clap::{Parser, Subcommand, ValueEnum};
2use std::path::PathBuf;
3
4use crate::models::ReasoningLevel;
5
6#[derive(Parser, Debug)]
7#[command(name = "mermaid")]
8#[command(version)]
9#[command(about = "An open-source, model-agnostic AI pair programmer", long_about = None)]
10#[command(after_help = TOP_LEVEL_HELP_AFTER)]
11pub struct Cli {
12    /// Model to use (e.g., qwen3-coder:30b, ollama/llama3)
13    #[arg(short, long)]
14    pub model: Option<String>,
15
16    /// Reasoning depth (none, minimal, low, medium, high, max).
17    /// Overrides the persisted default for this session; the slash
18    /// command `/reasoning <level>` and Alt+T can change it at runtime.
19    #[arg(long)]
20    pub reasoning: Option<ReasoningLevel>,
21
22    /// Project directory (defaults to current directory)
23    #[arg(short, long)]
24    pub path: Option<PathBuf>,
25
26    /// Verbose output
27    #[arg(short, long)]
28    pub verbose: bool,
29
30    /// Show session picker to choose a previous conversation
31    #[arg(long, conflicts_with = "continue_session")]
32    pub sessions: bool,
33
34    /// Resume the last conversation instead of starting fresh
35    #[arg(long = "continue", conflicts_with = "sessions")]
36    pub continue_session: bool,
37
38    /// Append every reducer `Msg` to a JSONL file at this path for
39    /// debugging / post-mortem replay. Interactive mode only.
40    #[arg(long, value_name = "FILE")]
41    pub record: Option<PathBuf>,
42
43    /// Replace Mermaid's default system prompt for this invocation
44    #[arg(long, global = true, conflicts_with = "system_prompt_file")]
45    pub system_prompt: Option<String>,
46
47    /// Replace Mermaid's default system prompt with the contents of a file
48    #[arg(
49        long,
50        value_name = "FILE",
51        global = true,
52        conflicts_with = "system_prompt"
53    )]
54    pub system_prompt_file: Option<PathBuf>,
55
56    /// Append extra instructions after Mermaid's system prompt for this invocation
57    #[arg(long, global = true)]
58    pub append_system_prompt: Option<String>,
59
60    /// Append extra instructions from a file after Mermaid's system prompt
61    #[arg(long, value_name = "FILE", global = true)]
62    pub append_system_prompt_file: Option<PathBuf>,
63
64    #[command(subcommand)]
65    pub command: Option<Commands>,
66}
67
68const TOP_LEVEL_HELP_AFTER: &str = "\
69Common first run:
70  mermaid doctor                         Check model, tools, safety, and project readiness
71  mermaid                                Start the full-screen terminal coding agent
72  mermaid run \"inspect this repo\"        Run one prompt headlessly
73  mermaid self-test                      Run fast deterministic Mermaid self-tests
74
75Command groups:
76  Everyday: chat, run, doctor, status, list, self-test
77  Model/context: models, model-info, --model, --reasoning, --system-prompt*
78  Safety/recovery: approvals, approve, deny, checkpoints, restore
79  Integrations: add, remove, mcp, cloud-setup, plugin, pr
80  Advanced runtime: daemon, tasks, task, processes, logs, stop, restart, ports, pair";
81
82#[derive(Subcommand, Debug)]
83pub enum Commands {
84    /// Initialize configuration
85    Init,
86    /// List available models
87    List,
88    /// List model/provider capability records
89    Models,
90    /// Show static and cached capability info for a model id
91    ModelInfo {
92        /// Model id, e.g. openai/gpt-5.2
93        model: String,
94    },
95    /// Start a chat session (default)
96    Chat,
97    /// Show version information
98    Version,
99    /// Check status of dependencies and backends
100    Status,
101    /// Check first-run readiness and explain what Mermaid can do now
102    Doctor {
103        /// Output format (text, json, markdown)
104        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
105        format: OutputFormat,
106    },
107    /// Run fast deterministic Mermaid self-tests
108    SelfTest {
109        /// Output format (text, json, markdown)
110        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
111        format: OutputFormat,
112        /// Keep the temporary self-test workspace after the run
113        #[arg(long)]
114        keep_workspace: bool,
115    },
116    /// List durable runtime tasks
117    Tasks {
118        /// Maximum number of tasks to show
119        #[arg(short, long, default_value_t = 20)]
120        limit: usize,
121    },
122    /// Show one durable runtime task and its timeline
123    Task {
124        /// Task id
125        id: String,
126    },
127    /// List Mermaid-managed background processes
128    Processes {
129        /// Maximum number of processes to show
130        #[arg(short, long, default_value_t = 20)]
131        limit: usize,
132    },
133    /// Print a managed process log
134    Logs {
135        /// Process id from `mermaid processes`
136        id: String,
137    },
138    /// Stop a managed process
139    Stop {
140        /// Process id from `mermaid processes`
141        id: String,
142    },
143    /// Restart a managed process
144    Restart {
145        /// Process id from `mermaid processes`
146        id: String,
147    },
148    /// Open a URL, file, or managed process URL
149    Open {
150        /// URL, path, or process id
151        target: String,
152    },
153    /// Show listening TCP ports
154    Ports,
155    /// List pending approvals
156    Approvals,
157    /// Approve a pending approval record
158    Approve {
159        /// Approval id
160        id: String,
161    },
162    /// Deny a pending approval record
163    Deny {
164        /// Approval id
165        id: String,
166    },
167    /// List recent persisted tool runs
168    ToolRuns {
169        /// Maximum number of tool runs to show
170        #[arg(short, long, default_value_t = 20)]
171        limit: usize,
172    },
173    /// List checkpoints
174    Checkpoints {
175        /// Maximum number of checkpoints to show
176        #[arg(short, long, default_value_t = 20)]
177        limit: usize,
178    },
179    /// Restore a checkpoint by id
180    Restore {
181        /// Checkpoint id
182        id: String,
183    },
184    /// Manage Mermaid plugin bundles
185    Plugin {
186        #[command(subcommand)]
187        command: PluginCommand,
188    },
189    /// Manage Mermaid's Linux background service
190    Daemon {
191        #[command(subcommand)]
192        command: DaemonCommand,
193    },
194    /// Create a remote pairing token
195    Pair {
196        /// Human label for the remote client
197        #[arg(long)]
198        label: Option<String>,
199    },
200    /// Internal self-QA commands. Hidden from normal help output.
201    #[command(hide = true)]
202    Qa {
203        #[command(subcommand)]
204        command: QaCommand,
205    },
206    /// Add an MCP server (e.g., mermaid add context7)
207    Add {
208        /// MCP server name (e.g., context7, github, filesystem)
209        name: String,
210    },
211    /// Remove a configured MCP server
212    Remove {
213        /// MCP server name to remove
214        name: String,
215    },
216    /// List configured MCP servers
217    Mcp,
218    /// Create a pull/merge request from the current branch via the host CLI
219    /// (`gh` for GitHub, `glab` for GitLab)
220    Pr {
221        #[command(subcommand)]
222        command: PrCommand,
223    },
224    /// Configure Ollama Cloud API key (interactive prompt). Run this
225    /// from your shell before starting mermaid — it reads stdin and
226    /// doesn't work from inside the TUI.
227    CloudSetup,
228    /// Run a single prompt non-interactively
229    Run {
230        /// The prompt to execute
231        prompt: String,
232
233        /// Output format (text, json, markdown)
234        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
235        format: OutputFormat,
236
237        /// Maximum tokens to generate
238        #[arg(long)]
239        max_tokens: Option<usize>,
240
241        /// Don't execute agent actions (dry run)
242        #[arg(long)]
243        no_execute: bool,
244    },
245}
246
247#[derive(Subcommand, Debug)]
248pub enum PluginCommand {
249    /// Install a plugin from a local path
250    Install {
251        /// Path containing plugin.toml
252        path: PathBuf,
253    },
254    /// List installed plugins
255    List,
256    /// Enable an installed plugin
257    Enable {
258        /// Plugin id or name
259        id: String,
260    },
261    /// Disable an installed plugin
262    Disable {
263        /// Plugin id or name
264        id: String,
265    },
266    /// Validate a plugin manifest without installing
267    Audit {
268        /// Path containing plugin.toml
269        path: PathBuf,
270    },
271}
272
273/// Which Git hosting provider's CLI to drive.
274#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
275pub enum GitHost {
276    /// GitHub, via the `gh` CLI.
277    Github,
278    /// GitLab, via the `glab` CLI.
279    Gitlab,
280}
281
282#[derive(Subcommand, Debug)]
283pub enum PrCommand {
284    /// Create a PR/MR from the current branch. Wraps `gh pr create` /
285    /// `glab mr create`, reusing their existing authentication.
286    Create {
287        /// PR/MR title. Omitted → filled from the branch's commits.
288        #[arg(short, long)]
289        title: Option<String>,
290        /// PR/MR body text.
291        #[arg(short, long)]
292        body: Option<String>,
293        /// Read the body from a file (e.g. a saved review summary).
294        #[arg(long, value_name = "FILE", conflicts_with = "body")]
295        summary: Option<PathBuf>,
296        /// Base branch to merge into (defaults to the host's default branch).
297        #[arg(long)]
298        base: Option<String>,
299        /// Open as a draft.
300        #[arg(long)]
301        draft: bool,
302        /// Open the creation page in a browser instead of creating directly.
303        #[arg(long)]
304        web: bool,
305        /// Force a provider instead of auto-detecting from the `origin` remote.
306        #[arg(long, value_enum)]
307        provider: Option<GitHost>,
308    },
309}
310
311#[derive(Subcommand, Debug)]
312pub enum DaemonCommand {
313    /// Install the systemd user service for this user
314    Install {
315        /// Start and enable the service after writing the unit
316        #[arg(long)]
317        start: bool,
318        /// Overwrite an existing Mermaid service unit
319        #[arg(long)]
320        force: bool,
321    },
322    /// Remove the systemd user service for this user
323    Uninstall,
324    /// Start the background user service
325    Start,
326    /// Stop the background user service
327    Stop,
328    /// Restart the background user service
329    Restart,
330    /// Show background service status
331    Status,
332    /// Show background service logs
333    Logs {
334        /// Follow log output
335        #[arg(short, long)]
336        follow: bool,
337        /// Number of log lines to show before following/exiting
338        #[arg(short = 'n', long, default_value_t = 100)]
339        lines: usize,
340    },
341    /// Print the generated service unit without installing it
342    PrintUnit,
343}
344
345#[derive(Subcommand, Debug)]
346pub enum QaCommand {
347    /// Deterministically exercise context compaction without a real model.
348    CompactSmoke {
349        /// Number of synthetic user/assistant turns to seed
350        #[arg(long, default_value_t = 6)]
351        turns: usize,
352        /// Output format
353        #[arg(short, long, value_enum, default_value_t = OutputFormat::Json)]
354        format: OutputFormat,
355    },
356}
357
358#[derive(Debug, Clone, Copy, ValueEnum)]
359pub enum OutputFormat {
360    /// Plain text output
361    Text,
362    /// JSON structured output
363    Json,
364    /// Markdown formatted output
365    Markdown,
366}