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
302#[derive(Subcommand)]
303pub enum AgentAction {
304    Create { name: String },
305    List,
306    Edit { name: String },
307    Rm { name: String },
308    Run { name: String, task: String },
309    Mention { name: String, message: String },
310}
311
312#[derive(Subcommand)]
313pub enum AuthAction {
314    Add {
315        provider: String,
316    },
317    List,
318    Rm {
319        provider: String,
320    },
321    /// Authenticate a provider via OAuth device flow (github/google/microsoft).
322    Login {
323        provider: String,
324        /// OAuth client id (or set <PROVIDER>_CLIENT_ID env var)
325        #[arg(long)]
326        client_id: Option<String>,
327    },
328}
329
330#[derive(Subcommand)]
331pub enum SkillsAction {
332    List,
333    View {
334        name: String,
335    },
336    Create {
337        name: String,
338    },
339    Install {
340        source: String,
341    },
342    Update {
343        name: String,
344    },
345    Prune,
346    /// Remove a skill by name (e.g. to delete junk auto-learned skills)
347    Rm {
348        name: String,
349    },
350}
351
352#[derive(Subcommand)]
353pub enum PluginsAction {
354    List,
355    Install {
356        source: String,
357        #[arg(long)]
358        allow: bool,
359    },
360    Rm {
361        name: String,
362    },
363}
364
365#[derive(Subcommand)]
366pub enum GithubAction {
367    /// Review a pull request: fetch diff via `gh`, run a read-only review prompt
368    Review {
369        /// PR number
370        pr: u64,
371        /// Print the review plan without invoking the model or posting comments
372        #[arg(long)]
373        dry_run: bool,
374        /// Override the model id
375        #[arg(long)]
376        model: Option<String>,
377        /// Restrict tool allow-list (comma-separated). Empty = inherit config.
378        #[arg(long)]
379        allowed_tools: Option<String>,
380    },
381    /// Show CI status for the current branch (via `gh run list`)
382    Status,
383    /// Fetch CI logs for a workflow run id (via `gh run view --log`)
384    Logs { run_id: String },
385}
386
387#[derive(Subcommand)]
388pub enum SecurityAction {
389    /// Run a full security audit
390    Audit {
391        /// Emit JSON instead of human-readable summary
392        #[arg(long)]
393        json: bool,
394    },
395}
396
397#[derive(Subcommand)]
398pub enum ToolsAction {
399    List {
400        #[arg(long)]
401        surface: Option<String>,
402    },
403    Enable {
404        tool: String,
405    },
406    Disable {
407        tool: String,
408    },
409}
410
411#[derive(Subcommand)]
412pub enum McpAction {
413    Add {
414        server: String,
415
416        /// Command to launch the MCP server
417        #[arg(long)]
418        command: Option<String>,
419
420        /// Command arguments, either repeated or space-delimited
421        #[arg(long, value_delimiter = ' ', allow_hyphen_values = true)]
422        args: Vec<String>,
423
424        /// Transport backend: stdio, sse, or url
425        #[arg(long)]
426        transport: Option<String>,
427    },
428    List,
429    Rm {
430        server: String,
431    },
432}
433
434#[derive(Subcommand)]
435pub enum CheckpointAction {
436    /// List all checkpoints
437    List,
438    /// Show diff between HEAD and a checkpoint
439    Diff {
440        /// Checkpoint ID
441        id: String,
442    },
443    /// Delete checkpoints older than N days (default: 30)
444    Prune {
445        /// Remove checkpoints older than this many days
446        #[arg(long, default_value = "30")]
447        older_than_days: u64,
448    },
449}
450
451#[derive(Subcommand)]
452pub enum GatewayAction {
453    Start,
454    Status,
455    Health,
456    Abort { run: String },
457    Stop,
458}
459
460#[derive(Subcommand)]
461pub enum SessionAction {
462    List,
463    Export {
464        id: String,
465        path: Option<PathBuf>,
466    },
467    Cleanup {
468        #[arg(long, default_value_t = 30)]
469        older_than_days: u64,
470    },
471}
472
473#[derive(Subcommand)]
474pub enum ProfileAction {
475    Create { name: String },
476    List,
477    Use { name: String },
478}
479
480#[derive(Subcommand)]
481pub enum ImportSource {
482    Openclaw { path: Option<PathBuf> },
483}
484
485#[derive(Subcommand)]
486pub enum MemoryAction {
487    List,
488    Forget {
489        id: String,
490    },
491    Add {
492        key: String,
493        value: String,
494    },
495    Replace {
496        id: String,
497        key: String,
498        value: String,
499    },
500    Recall {
501        query: String,
502        #[arg(long, default_value_t = 10)]
503        limit: usize,
504    },
505    Consolidate,
506    Docs,
507    Search {
508        query: String,
509        #[arg(long, default_value_t = 10)]
510        limit: usize,
511    },
512    Scroll {
513        session: String,
514        #[arg(long, default_value_t = 0)]
515        around: usize,
516        #[arg(long, default_value_t = 3)]
517        before: usize,
518        #[arg(long, default_value_t = 3)]
519        after: usize,
520    },
521    Graph {
522        #[command(subcommand)]
523        action: GraphAction,
524    },
525}
526
527#[derive(Subcommand)]
528pub enum GraphAction {
529    UpsertNode {
530        id: String,
531        label: String,
532        #[arg(long, default_value = "entity")]
533        kind: String,
534        #[arg(long, default_value = "{}")]
535        properties: String,
536    },
537    UpsertEdge {
538        from_id: String,
539        relation: String,
540        to_id: String,
541        #[arg(long)]
542        id: Option<String>,
543        #[arg(long, default_value_t = 1.0)]
544        weight: f64,
545        #[arg(long, default_value = "{}")]
546        properties: String,
547    },
548    Get {
549        id: String,
550    },
551    Neighbors {
552        id: String,
553        #[arg(long, default_value = "both")]
554        direction: String,
555        #[arg(long, default_value_t = 20)]
556        limit: usize,
557    },
558    Search {
559        query: String,
560        #[arg(long, default_value_t = 20)]
561        limit: usize,
562    },
563    Export,
564    DeleteNode {
565        id: String,
566    },
567    DeleteEdge {
568        id: String,
569    },
570    SyncNeo4j,
571}
572
573#[derive(Subcommand)]
574pub enum PermissionAction {
575    /// Show current permission mode and rules
576    List,
577    /// Set permission mode (read-only|plan|supervised|trusted|autonomous|emergency-stop)
578    Set { mode: String },
579    /// Add an explicitly allowed tool pattern
580    AllowTool { tool: String },
581    /// Add a tool pattern that always asks for approval
582    AskTool { tool: String },
583    /// Add an explicitly denied tool pattern
584    DenyTool { tool: String },
585    /// Add an allowed path boundary
586    AllowPath { path: PathBuf },
587    /// Add a denied path boundary
588    DenyPath { path: PathBuf },
589}
590
591#[derive(Subcommand)]
592pub enum RouteAction {
593    /// Pin the intelligent auto-router to a specific provider for all tiers.
594    /// Example: sparrow route set opencode-go
595    Set {
596        /// Provider ID to use for all task tiers (e.g. opencode-go, anthropic, nvidia)
597        provider: String,
598    },
599    /// Clear the pinned provider — let the multi-tier policy decide per task.
600    Clear,
601    /// Show the current routing config (preferred provider + per-tier policy).
602    Show,
603}
604
605#[derive(Subcommand)]
606pub enum HookAction {
607    /// Install pre-commit security hook
608    Install,
609    /// Scan staged files (or all files with --all) for secrets
610    Scan {
611        /// Scan entire working tree instead of just staged files
612        #[arg(long)]
613        all: bool,
614    },
615}