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    /// Update Mermaid to the latest release
100    Update {
101        /// Only report whether an update is available; don't install it
102        #[arg(long)]
103        check: bool,
104        /// Reinstall even if already on the latest version
105        #[arg(long)]
106        force: bool,
107    },
108    /// Check status of dependencies and backends
109    Status,
110    /// Check first-run readiness and explain what Mermaid can do now
111    Doctor {
112        /// Output format (text, json, markdown)
113        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
114        format: OutputFormat,
115    },
116    /// Run fast deterministic Mermaid self-tests
117    SelfTest {
118        /// Output format (text, json, markdown)
119        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
120        format: OutputFormat,
121        /// Keep the temporary self-test workspace after the run
122        #[arg(long)]
123        keep_workspace: bool,
124    },
125    /// List durable runtime tasks
126    Tasks {
127        /// Maximum number of tasks to show
128        #[arg(short, long, default_value_t = 20)]
129        limit: usize,
130    },
131    /// Show one durable runtime task and its timeline
132    Task {
133        /// Task id
134        id: String,
135    },
136    /// List Mermaid-managed background processes
137    Processes {
138        /// Maximum number of processes to show
139        #[arg(short, long, default_value_t = 20)]
140        limit: usize,
141    },
142    /// Print a managed process log
143    Logs {
144        /// Process id from `mermaid processes`
145        id: String,
146    },
147    /// Stop a managed process
148    Stop {
149        /// Process id from `mermaid processes`
150        id: String,
151    },
152    /// Restart a managed process
153    Restart {
154        /// Process id from `mermaid processes`
155        id: String,
156    },
157    /// Open a URL, file, or managed process URL
158    Open {
159        /// URL, path, or process id
160        target: String,
161    },
162    /// Show listening TCP ports
163    Ports,
164    /// List pending approvals
165    Approvals,
166    /// Approve a pending approval record
167    Approve {
168        /// Approval id
169        id: String,
170    },
171    /// Deny a pending approval record
172    Deny {
173        /// Approval id
174        id: String,
175    },
176    /// List recent persisted tool runs
177    ToolRuns {
178        /// Maximum number of tool runs to show
179        #[arg(short, long, default_value_t = 20)]
180        limit: usize,
181    },
182    /// List checkpoints
183    Checkpoints {
184        /// Maximum number of checkpoints to show
185        #[arg(short, long, default_value_t = 20)]
186        limit: usize,
187    },
188    /// Restore a checkpoint by id
189    Restore {
190        /// Checkpoint id
191        id: String,
192    },
193    /// Manage Mermaid plugin bundles
194    Plugin {
195        #[command(subcommand)]
196        command: PluginCommand,
197    },
198    /// Manage Mermaid's Linux background service
199    Daemon {
200        #[command(subcommand)]
201        command: DaemonCommand,
202    },
203    /// Create a remote pairing token
204    Pair {
205        /// Human label for the remote client
206        #[arg(long)]
207        label: Option<String>,
208    },
209    /// Internal self-QA commands. Hidden from normal help output.
210    #[command(hide = true)]
211    Qa {
212        #[command(subcommand)]
213        command: QaCommand,
214    },
215    /// Add an MCP server (e.g., mermaid add context7)
216    Add {
217        /// MCP server name (e.g., context7, github, filesystem)
218        name: String,
219    },
220    /// Remove a configured MCP server
221    Remove {
222        /// MCP server name to remove
223        name: String,
224    },
225    /// List configured MCP servers
226    Mcp,
227    /// Create a pull/merge request from the current branch via the host CLI
228    /// (`gh` for GitHub, `glab` for GitLab)
229    Pr {
230        #[command(subcommand)]
231        command: PrCommand,
232    },
233    /// Configure Ollama Cloud API key (interactive prompt). Run this
234    /// from your shell before starting mermaid — it reads stdin and
235    /// doesn't work from inside the TUI.
236    CloudSetup,
237    /// Run a single prompt non-interactively
238    Run {
239        /// The prompt to execute
240        prompt: String,
241
242        /// Output format (text, json, markdown)
243        #[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
244        format: OutputFormat,
245
246        /// Maximum tokens to generate
247        #[arg(long)]
248        max_tokens: Option<usize>,
249
250        /// Don't execute agent actions (dry run)
251        #[arg(long)]
252        no_execute: bool,
253    },
254}
255
256#[derive(Subcommand, Debug)]
257pub enum PluginCommand {
258    /// Install a plugin from a local path
259    Install {
260        /// Path containing plugin.toml
261        path: PathBuf,
262    },
263    /// List installed plugins
264    List,
265    /// Enable an installed plugin
266    Enable {
267        /// Plugin id or name
268        id: String,
269    },
270    /// Disable an installed plugin
271    Disable {
272        /// Plugin id or name
273        id: String,
274    },
275    /// Validate a plugin manifest without installing
276    Audit {
277        /// Path containing plugin.toml
278        path: PathBuf,
279    },
280}
281
282/// Which Git hosting provider's CLI to drive.
283#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
284pub enum GitHost {
285    /// GitHub, via the `gh` CLI.
286    Github,
287    /// GitLab, via the `glab` CLI.
288    Gitlab,
289}
290
291#[derive(Subcommand, Debug)]
292pub enum PrCommand {
293    /// Create a PR/MR from the current branch. Wraps `gh pr create` /
294    /// `glab mr create`, reusing their existing authentication.
295    Create {
296        /// PR/MR title. Omitted → filled from the branch's commits.
297        #[arg(short, long)]
298        title: Option<String>,
299        /// PR/MR body text.
300        #[arg(short, long)]
301        body: Option<String>,
302        /// Read the body from a file (e.g. a saved review summary).
303        #[arg(long, value_name = "FILE", conflicts_with = "body")]
304        summary: Option<PathBuf>,
305        /// Base branch to merge into (defaults to the host's default branch).
306        #[arg(long)]
307        base: Option<String>,
308        /// Open as a draft.
309        #[arg(long)]
310        draft: bool,
311        /// Open the creation page in a browser instead of creating directly.
312        #[arg(long)]
313        web: bool,
314        /// Force a provider instead of auto-detecting from the `origin` remote.
315        #[arg(long, value_enum)]
316        provider: Option<GitHost>,
317    },
318}
319
320#[derive(Subcommand, Debug)]
321pub enum DaemonCommand {
322    /// Install the systemd user service for this user
323    Install {
324        /// Start and enable the service after writing the unit
325        #[arg(long)]
326        start: bool,
327        /// Overwrite an existing Mermaid service unit
328        #[arg(long)]
329        force: bool,
330    },
331    /// Remove the systemd user service for this user
332    Uninstall,
333    /// Start the background user service
334    Start,
335    /// Stop the background user service
336    Stop,
337    /// Restart the background user service
338    Restart,
339    /// Show background service status
340    Status,
341    /// Show background service logs
342    Logs {
343        /// Follow log output
344        #[arg(short, long)]
345        follow: bool,
346        /// Number of log lines to show before following/exiting
347        #[arg(short = 'n', long, default_value_t = 100)]
348        lines: usize,
349    },
350    /// Print the generated service unit without installing it
351    PrintUnit,
352}
353
354#[derive(Subcommand, Debug)]
355pub enum QaCommand {
356    /// Deterministically exercise context compaction without a real model.
357    CompactSmoke {
358        /// Number of synthetic user/assistant turns to seed
359        #[arg(long, default_value_t = 6)]
360        turns: usize,
361        /// Output format
362        #[arg(short, long, value_enum, default_value_t = OutputFormat::Json)]
363        format: OutputFormat,
364    },
365}
366
367#[derive(Debug, Clone, Copy, ValueEnum)]
368pub enum OutputFormat {
369    /// Plain text output
370    Text,
371    /// JSON structured output
372    Json,
373    /// Markdown formatted output
374    Markdown,
375}