Skip to main content

sparrow/cli/
mod.rs

1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(name = "sparrow", about = "one cli · grows with you", version)]
6pub struct Cli {
7    #[command(subcommand)]
8    pub command: Option<Commands>,
9
10    /// Launch terminal TUI (native)
11    #[arg(long)]
12    pub tui: bool,
13
14    /// Launch webview console (HTTP + WebSocket)
15    #[arg(long)]
16    pub web: bool,
17
18    /// JSON output (NDJSON event stream)
19    #[arg(long)]
20    pub json: bool,
21
22    /// Override autonomy level
23    #[arg(long)]
24    pub autonomy: Option<String>,
25
26    /// Force a specific model
27    #[arg(long)]
28    pub model: Option<String>,
29
30    /// Prefer local/offline models
31    #[arg(long)]
32    pub local: bool,
33
34    /// Session budget cap (USD)
35    #[arg(long)]
36    pub budget: Option<f64>,
37
38    /// Sandbox backend
39    #[arg(long)]
40    pub sandbox: Option<String>,
41
42    /// Profile name
43    #[arg(long)]
44    pub profile: Option<String>,
45
46    /// Disable checkpointing
47    #[arg(long)]
48    pub no_checkpoint: bool,
49
50    /// Run as a named agent
51    #[arg(long)]
52    pub agent: Option<String>,
53}
54
55#[derive(Subcommand)]
56pub enum Commands {
57    /// Run a single agentic task
58    Run {
59        /// Task description
60        task: String,
61
62        /// Emit NDJSON event stream (same as the global --json flag, but may
63        /// follow the task: `sparrow run "..." --json`)
64        #[arg(long)]
65        json: bool,
66    },
67
68    /// Create a read-only execution plan for a task
69    Plan {
70        /// Task description
71        task: String,
72
73        /// Emit JSON instead of Markdown
74        #[arg(long)]
75        json: bool,
76    },
77
78    /// Interactive multi-turn chat
79    Chat,
80
81    /// Launch TUI
82    Tui,
83
84    /// Launch first-run setup, then the WebView cockpit
85    Launch {
86        /// TCP port for the WebView HTTP/WS server
87        #[arg(long, default_value = "9339")]
88        port: u16,
89
90        /// Launch the terminal TUI instead of the WebView cockpit
91        #[arg(long)]
92        tui: bool,
93    },
94
95    /// Launch webview console (HTTP + WebSocket)
96    Console {
97        /// TCP port for the webview HTTP/WS server
98        #[arg(long, default_value = "9339")]
99        port: u16,
100    },
101
102    /// Run headless Sparrow runtime daemon
103    Daemon,
104
105    /// Manage persistent agents
106    Agent {
107        #[command(subcommand)]
108        action: AgentAction,
109    },
110
111    /// Run swarm: planner → coder → verifier
112    Swarm {
113        /// Task or plan file
114        task: String,
115    },
116
117    /// Schedule periodic jobs
118    Schedule {
119        /// Task description
120        task: String,
121
122        /// Cron expression
123        #[arg(long)]
124        cron: String,
125
126        /// Autonomy level for scheduled jobs
127        #[arg(long)]
128        autonomy: Option<String>,
129
130        /// Report to surfaces
131        #[arg(long)]
132        report: Vec<String>,
133    },
134
135    /// Manage model routing
136    Model {
137        /// Set active route
138        #[arg(long)]
139        set: Option<String>,
140
141        /// List available models
142        #[arg(long)]
143        list: bool,
144    },
145
146    /// Configure intelligent auto-routing provider
147    Route {
148        #[command(subcommand)]
149        action: RouteAction,
150    },
151
152    /// Manage provider credentials
153    Auth {
154        #[command(subcommand)]
155        action: AuthAction,
156    },
157
158    /// Manage skill library
159    Skills {
160        #[command(subcommand)]
161        action: SkillsAction,
162    },
163
164    /// Manage local Sparrow plugins
165    Plugins {
166        #[command(subcommand)]
167        action: PluginsAction,
168    },
169
170    /// Inspect and gate toolsets
171    Tools {
172        #[command(subcommand)]
173        action: ToolsAction,
174    },
175
176    /// Security audit of config, permissions, plugins, hooks, secrets
177    Security {
178        #[command(subcommand)]
179        action: SecurityAction,
180    },
181
182    /// GitHub Action / remote PR workflow
183    Github {
184        #[command(subcommand)]
185        action: GithubAction,
186    },
187
188    /// Compact context and write a durable handoff doc
189    Compact {
190        /// Task description (recorded in the handoff)
191        #[arg(long)]
192        task: Option<String>,
193        /// Output path (default: .sparrow/handoff/<timestamp>.md)
194        #[arg(long)]
195        out: Option<PathBuf>,
196        /// Emit JSON instead of Markdown to stdout (the file is always Markdown)
197        #[arg(long)]
198        json: bool,
199    },
200
201    /// Manage MCP connectors
202    Mcp {
203        #[command(subcommand)]
204        action: McpAction,
205    },
206
207    /// List checkpoints
208    Checkpoint {
209        #[command(subcommand)]
210        action: CheckpointAction,
211    },
212
213    /// Rewind to a checkpoint
214    Rewind {
215        /// Checkpoint ID or number
216        id: String,
217    },
218
219    /// Replay a transcript
220    Replay {
221        /// Run ID to replay
222        run_id: String,
223        /// Open an interactive TUI scrubber (←/→ to step through events)
224        #[arg(long)]
225        scrub: bool,
226    },
227
228    /// Start/stop gateway daemon
229    Gateway {
230        #[command(subcommand)]
231        action: GatewayAction,
232    },
233
234    /// Manage saved sessions
235    Sessions {
236        #[command(subcommand)]
237        action: SessionAction,
238    },
239
240    /// Interactive tutorial
241    Learn,
242
243    /// Initialize a project with .sparrow/ config
244    Init,
245
246    /// Show live status (active runs, budget, session)
247    Status,
248
249    /// Manage persistent memory
250    Memory {
251        #[command(subcommand)]
252        action: MemoryAction,
253    },
254
255    /// Inspect and update permission policy
256    Permissions {
257        #[command(subcommand)]
258        action: PermissionAction,
259    },
260
261    /// Profile management
262    Profile {
263        #[command(subcommand)]
264        action: ProfileAction,
265    },
266
267    /// Migrate from OpenClaw
268    Import {
269        #[command(subcommand)]
270        source: ImportSource,
271    },
272
273    /// Edit configuration
274    Config {
275        /// Open config.toml in editor
276        #[arg(short)]
277        edit: bool,
278    },
279
280    /// Self-update
281    Update,
282
283    /// Run diagnostics
284    Doctor,
285
286    /// (Re)run conversational setup
287    Setup,
288
289    /// Run a self-contained demo (snake game)
290    Demo,
291
292    /// Share latest session as GitHub Gist
293    Share,
294
295    /// Install or scan security pre-commit hooks
296    Hook {
297        #[command(subcommand)]
298        action: HookAction,
299    },
300
301    /// Voice commands (speak, transcribe, providers)
302    Voice {
303        #[command(subcommand)]
304        action: VoiceAction,
305    },
306}
307
308#[derive(Subcommand)]
309pub enum AgentAction {
310    Create { name: String },
311    List,
312    Edit { name: String },
313    Rm { name: String },
314    Run { name: String, task: String },
315    Mention { name: String, message: String },
316}
317
318#[derive(Subcommand)]
319pub enum AuthAction {
320    Add {
321        provider: String,
322    },
323    List,
324    Rm {
325        provider: String,
326    },
327    /// Authenticate a provider via OAuth device flow (github/google/microsoft).
328    Login {
329        provider: String,
330        /// OAuth client id (or set <PROVIDER>_CLIENT_ID env var)
331        #[arg(long)]
332        client_id: Option<String>,
333    },
334}
335
336#[derive(Subcommand)]
337pub enum SkillsAction {
338    List,
339    View {
340        name: String,
341    },
342    Create {
343        name: String,
344    },
345    Install {
346        source: String,
347    },
348    Update {
349        name: String,
350    },
351    Prune,
352    /// Remove a skill by name (e.g. to delete junk auto-learned skills)
353    Rm {
354        name: String,
355    },
356}
357
358#[derive(Subcommand)]
359pub enum PluginsAction {
360    List,
361    Install {
362        source: String,
363        #[arg(long)]
364        allow: bool,
365    },
366    Rm {
367        name: String,
368    },
369}
370
371#[derive(Subcommand)]
372pub enum GithubAction {
373    /// Review a pull request: fetch diff via `gh`, run a read-only review prompt
374    Review {
375        /// PR number
376        pr: u64,
377        /// Print the review plan without invoking the model or posting comments
378        #[arg(long)]
379        dry_run: bool,
380        /// Override the model id
381        #[arg(long)]
382        model: Option<String>,
383        /// Restrict tool allow-list (comma-separated). Empty = inherit config.
384        #[arg(long)]
385        allowed_tools: Option<String>,
386    },
387    /// Show CI status for the current branch (via `gh run list`)
388    Status,
389    /// Fetch CI logs for a workflow run id (via `gh run view --log`)
390    Logs { run_id: String },
391}
392
393#[derive(Subcommand)]
394pub enum SecurityAction {
395    /// Run a full security audit
396    Audit {
397        /// Emit JSON instead of human-readable summary
398        #[arg(long)]
399        json: bool,
400    },
401}
402
403#[derive(Subcommand)]
404pub enum ToolsAction {
405    List {
406        #[arg(long)]
407        surface: Option<String>,
408    },
409    Enable {
410        tool: String,
411    },
412    Disable {
413        tool: String,
414    },
415}
416
417#[derive(Subcommand)]
418pub enum McpAction {
419    Add {
420        server: String,
421
422        /// Command to launch the MCP server
423        #[arg(long)]
424        command: Option<String>,
425
426        /// Command arguments, either repeated or space-delimited
427        #[arg(long, value_delimiter = ' ', allow_hyphen_values = true)]
428        args: Vec<String>,
429
430        /// Transport backend: stdio, sse, or url
431        #[arg(long)]
432        transport: Option<String>,
433    },
434    List,
435    Rm {
436        server: String,
437    },
438}
439
440#[derive(Subcommand)]
441pub enum CheckpointAction {
442    /// List all checkpoints
443    List,
444    /// Show diff between HEAD and a checkpoint
445    Diff {
446        /// Checkpoint ID
447        id: String,
448    },
449    /// Delete checkpoints older than N days (default: 30)
450    Prune {
451        /// Remove checkpoints older than this many days
452        #[arg(long, default_value = "30")]
453        older_than_days: u64,
454    },
455}
456
457#[derive(Subcommand)]
458pub enum GatewayAction {
459    Start,
460    Status,
461    Health,
462    Abort { run: String },
463    Stop,
464}
465
466#[derive(Subcommand)]
467pub enum SessionAction {
468    List,
469    Export {
470        id: String,
471        path: Option<PathBuf>,
472    },
473    Cleanup {
474        #[arg(long, default_value_t = 30)]
475        older_than_days: u64,
476    },
477}
478
479#[derive(Subcommand)]
480pub enum ProfileAction {
481    Create { name: String },
482    List,
483    Use { name: String },
484}
485
486#[derive(Subcommand)]
487pub enum ImportSource {
488    Openclaw { path: Option<PathBuf> },
489}
490
491#[derive(Subcommand)]
492pub enum MemoryAction {
493    List,
494    Forget {
495        id: String,
496    },
497    Add {
498        key: String,
499        value: String,
500    },
501    Replace {
502        id: String,
503        key: String,
504        value: String,
505    },
506    Recall {
507        query: String,
508        #[arg(long, default_value_t = 10)]
509        limit: usize,
510    },
511    Consolidate,
512    Docs,
513    Search {
514        query: String,
515        #[arg(long, default_value_t = 10)]
516        limit: usize,
517    },
518    Scroll {
519        session: String,
520        #[arg(long, default_value_t = 0)]
521        around: usize,
522        #[arg(long, default_value_t = 3)]
523        before: usize,
524        #[arg(long, default_value_t = 3)]
525        after: usize,
526    },
527    Graph {
528        #[command(subcommand)]
529        action: GraphAction,
530    },
531}
532
533#[derive(Subcommand)]
534pub enum GraphAction {
535    UpsertNode {
536        id: String,
537        label: String,
538        #[arg(long, default_value = "entity")]
539        kind: String,
540        #[arg(long, default_value = "{}")]
541        properties: String,
542    },
543    UpsertEdge {
544        from_id: String,
545        relation: String,
546        to_id: String,
547        #[arg(long)]
548        id: Option<String>,
549        #[arg(long, default_value_t = 1.0)]
550        weight: f64,
551        #[arg(long, default_value = "{}")]
552        properties: String,
553    },
554    Get {
555        id: String,
556    },
557    Neighbors {
558        id: String,
559        #[arg(long, default_value = "both")]
560        direction: String,
561        #[arg(long, default_value_t = 20)]
562        limit: usize,
563    },
564    Search {
565        query: String,
566        #[arg(long, default_value_t = 20)]
567        limit: usize,
568    },
569    Export,
570    DeleteNode {
571        id: String,
572    },
573    DeleteEdge {
574        id: String,
575    },
576    SyncNeo4j,
577}
578
579#[derive(Subcommand)]
580pub enum PermissionAction {
581    /// Show current permission mode and rules
582    List,
583    /// Set permission mode (read-only|plan|supervised|trusted|autonomous|emergency-stop)
584    Set { mode: String },
585    /// Add an explicitly allowed tool pattern
586    AllowTool { tool: String },
587    /// Add a tool pattern that always asks for approval
588    AskTool { tool: String },
589    /// Add an explicitly denied tool pattern
590    DenyTool { tool: String },
591    /// Add an allowed path boundary
592    AllowPath { path: PathBuf },
593    /// Add a denied path boundary
594    DenyPath { path: PathBuf },
595}
596
597#[derive(Subcommand)]
598pub enum RouteAction {
599    /// Pin the intelligent auto-router to a specific provider for all tiers.
600    /// Example: sparrow route set opencode-go
601    Set {
602        /// Provider ID to use for all task tiers (e.g. opencode-go, anthropic, nvidia)
603        provider: String,
604    },
605    /// Clear the pinned provider — let the multi-tier policy decide per task.
606    Clear,
607    /// Show the current routing config (preferred provider + per-tier policy).
608    Show,
609}
610
611#[derive(Subcommand)]
612pub enum HookAction {
613    /// Install pre-commit security hook
614    Install,
615    /// Scan staged files (or all files with --all) for secrets
616    Scan {
617        /// Scan entire working tree instead of just staged files
618        #[arg(long)]
619        all: bool,
620    },
621}
622
623#[derive(Subcommand)]
624pub enum VoiceAction {
625    /// Convert text to speech
626    Speak { text: String },
627    /// Transcribe audio file
628    Transcribe { file: String },
629    /// List available voice providers
630    Providers,
631}