Skip to main content

splice/cli/
commands.rs

1//! CLI command definitions (clap subcommand enums).
2
3use super::*;
4
5/// Available Splice commands.
6#[derive(clap::Subcommand, Debug)]
7pub enum Commands {
8    /// Delete a symbol by removing its definition.
9    #[command(display_order = 200)]
10    Delete {
11        /// Path to the source file containing the symbol.
12        #[arg(short, long)]
13        file: std::path::PathBuf,
14
15        /// Symbol name to delete.
16        #[arg(short, long)]
17        symbol: String,
18
19        /// Optional symbol kind filter.
20        #[arg(short, long)]
21        kind: Option<SymbolKind>,
22
23        /// Optional validation mode (off, os, path).
24        #[arg(long, value_name = "MODE")]
25        analyzer: Option<AnalyzerMode>,
26
27        /// Path to rust-analyzer binary (used with --analyzer path).
28        #[arg(long, value_name = "PATH")]
29        analyzer_binary: Option<std::path::PathBuf>,
30
31        /// Optional language (auto-detect from extension by default).
32        #[arg(long, value_name = "LANG")]
33        language: Option<Language>,
34
35        /// Number of context lines after the match.
36        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
37        context_after: usize,
38
39        /// Number of context lines before the match.
40        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
41        context_before: usize,
42
43        /// Number of context lines before and after the match (default: 3).
44        #[arg(short = 'C', long, value_name = "N", default_value = "3")]
45        context: usize,
46
47        /// Create a backup before deleting.
48        #[arg(long)]
49        create_backup: bool,
50
51        /// Include relationship information in output.
52        #[arg(long)]
53        relationships: bool,
54
55        /// Preview deletion without applying changes.
56        #[arg(short = 'n', long = "dry-run")]
57        dry_run: bool,
58
59        /// Number of context lines in unified diff (default: 3).
60        #[arg(short = 'U', long, value_name = "N", default_value = "3")]
61        unified: usize,
62
63        /// Optional operation ID for auditing (auto-generated UUID if not provided).
64        #[arg(long)]
65        operation_id: Option<String>,
66
67        /// Optional JSON metadata to attach to this operation.
68        #[arg(long)]
69        metadata: Option<String>,
70
71        /// Capture graph snapshot before deleting.
72        #[arg(long)]
73        snapshot_before: bool,
74    },
75
76    /// Apply a patch to a symbol's span.
77    #[command(display_order = 201)]
78    Patch {
79        /// Path to the source file containing the symbol.
80        #[arg(short = 'f', long, required_unless_present = "batch")]
81        file: Option<std::path::PathBuf>,
82
83        /// Symbol name to patch.
84        #[arg(short = 's', long, required_unless_present = "batch")]
85        symbol: Option<String>,
86
87        /// Optional symbol kind filter.
88        #[arg(short, long, conflicts_with = "batch")]
89        kind: Option<SymbolKind>,
90
91        /// Optional validation mode (off, os, path).
92        #[arg(long, value_name = "MODE")]
93        analyzer: Option<AnalyzerMode>,
94
95        /// Path to rust-analyzer binary (used with --analyzer path).
96        #[arg(long, value_name = "PATH")]
97        analyzer_binary: Option<std::path::PathBuf>,
98
99        /// Path to file containing replacement content.
100        #[arg(
101            short = 'w',
102            long = "with",
103            value_name = "FILE",
104            required_unless_present = "batch"
105        )]
106        with_: Option<std::path::PathBuf>,
107
108        /// Optional language (auto-detect from extension by default).
109        #[arg(long, value_name = "LANG")]
110        language: Option<Language>,
111
112        /// JSON file describing batch replacements.
113        #[arg(long, value_name = "FILE")]
114        batch: Option<std::path::PathBuf>,
115
116        /// Number of context lines after the match.
117        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
118        context_after: usize,
119
120        /// Number of context lines before the match.
121        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
122        context_before: usize,
123
124        /// Number of context lines before and after the match (default: 3).
125        #[arg(short = 'C', long, value_name = "N", default_value = "3")]
126        context_both: usize,
127
128        /// Preview changes without applying (alias: --dry-run, -n).
129        #[arg(
130            short = 'n',
131            long = "dry-run",
132            alias = "preview",
133            conflicts_with = "batch"
134        )]
135        preview: bool,
136
137        /// Number of context lines in unified diff (default: 3).
138        #[arg(short = 'U', long, value_name = "N", default_value = "3")]
139        unified: usize,
140
141        /// Create a backup before patching.
142        #[arg(long)]
143        create_backup: bool,
144
145        /// Include relationship information in output.
146        #[arg(long)]
147        relationships: bool,
148
149        /// Optional operation ID for auditing (auto-generated UUID if not provided).
150        #[arg(long)]
151        operation_id: Option<String>,
152
153        /// Optional JSON metadata to attach to this operation.
154        #[arg(long)]
155        metadata: Option<String>,
156
157        /// Path to codegraph database (required for symbol resolution).
158        #[arg(short = 'd', long, value_name = "FILE")]
159        db: Option<std::path::PathBuf>,
160
161        /// Capture graph snapshot before patching.
162        #[arg(long)]
163        snapshot_before: bool,
164
165        /// Generate DOT graph output for visualization (requires --preview)
166        #[arg(long, requires = "preview")]
167        impact_graph: bool,
168    },
169
170    /// Execute a multi-step refactoring plan.
171    Plan {
172        /// Path to the plan.json file.
173        #[arg(short, long)]
174        file: std::path::PathBuf,
175
176        /// Optional operation ID for auditing (auto-generated UUID if not provided).
177        #[arg(long)]
178        operation_id: Option<String>,
179
180        /// Optional JSON metadata to attach to this operation.
181        #[arg(long)]
182        metadata: Option<String>,
183    },
184
185    /// Undo a previous operation by restoring from a backup manifest.
186    Undo {
187        /// Path to the backup manifest file.
188        #[arg(short, long)]
189        manifest: std::path::PathBuf,
190    },
191
192    /// Apply a pattern replacement to multiple files.
193    ApplyFiles {
194        /// Glob pattern for matching files (e.g., "tests/**/*.rs" or "src/**/*.py").
195        #[arg(short, long)]
196        glob: String,
197
198        /// Text pattern to find.
199        #[arg(short, long)]
200        find: String,
201
202        /// Replacement text.
203        #[arg(short, long)]
204        replace: String,
205
206        /// Optional language (auto-detect from extension by default).
207        #[arg(long, value_name = "LANG")]
208        language: Option<Language>,
209
210        /// Number of context lines after the match.
211        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
212        context_after: usize,
213
214        /// Number of context lines before the match.
215        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
216        context_before: usize,
217
218        /// Number of context lines before and after the match (default: 3).
219        #[arg(short = 'C', long, value_name = "N", default_value = "3")]
220        context_both: usize,
221
222        /// Skip validation gates (default: false).
223        #[arg(long)]
224        no_validate: bool,
225
226        /// Create a backup before applying.
227        #[arg(long)]
228        create_backup: bool,
229
230        /// Optional operation ID for auditing (auto-generated UUID if not provided).
231        #[arg(long)]
232        operation_id: Option<String>,
233
234        /// Optional JSON metadata to attach to this operation.
235        #[arg(long)]
236        metadata: Option<String>,
237
238        /// Preview changes without applying — prints what would change and exits.
239        #[arg(short = 'n', long = "dry-run", visible_alias = "preview")]
240        dry_run: bool,
241    },
242
243    /// Query symbols by labels (uses Magellan integration).
244    #[command(display_order = 104)]
245    Query {
246        /// Path to the Magellan database.
247        #[arg(short, long)]
248        db: std::path::PathBuf,
249
250        /// Labels to query (can be specified multiple times).
251        /// Examples: rust, python, fn, struct, class, method, etc.
252        #[arg(short, long)]
253        label: Vec<String>,
254
255        /// Filter results by file path (optional).
256        /// Can be a glob pattern: "src/main.rs", "src/**/*.rs", etc.
257        #[arg(long)]
258        file: Option<String>,
259
260        /// Number of context lines after the match.
261        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
262        context_after: usize,
263
264        /// Number of context lines before the match.
265        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
266        context_before: usize,
267
268        /// Number of context lines before and after the match (default: 3).
269        #[arg(short = 'C', long, value_name = "N", default_value = "3")]
270        context_both: usize,
271
272        /// List all available labels.
273        #[arg(long)]
274        list: bool,
275
276        /// Count entities with specified label(s).
277        #[arg(long)]
278        count: bool,
279
280        /// Show source code for each result.
281        #[arg(long)]
282        show_code: bool,
283
284        /// Include relationship information in output.
285        #[arg(long)]
286        relationships: bool,
287
288        /// Expand symbol to full body.
289        #[arg(long)]
290        expand: bool,
291
292        /// Expansion level (0=none, 1=body, 2=containing block).
293        #[arg(long = "expand-level", value_name = "N", default_value = "1")]
294        expand_level: usize,
295    },
296
297    /// Get code chunks from the database (uses Magellan integration).
298    #[command(display_order = 105)]
299    Get {
300        /// Path to the Magellan database.
301        #[arg(short, long)]
302        db: std::path::PathBuf,
303
304        /// Path to the source file.
305        #[arg(short, long)]
306        file: std::path::PathBuf,
307
308        /// Start byte offset.
309        #[arg(long)]
310        start: usize,
311
312        /// End byte offset.
313        #[arg(long)]
314        end: usize,
315
316        /// Number of context lines after the match.
317        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
318        context_after: usize,
319
320        /// Number of context lines before the match.
321        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
322        context_before: usize,
323
324        /// Number of context lines before and after the match (default: 3).
325        #[arg(short = 'C', long, value_name = "N", default_value = "3")]
326        context_both: usize,
327
328        /// Include relationship information in output.
329        #[arg(long)]
330        relationships: bool,
331
332        /// Expand symbol to full body.
333        #[arg(long)]
334        expand: bool,
335
336        /// Expansion level (0=none, 1=body, 2=containing block).
337        #[arg(long = "expand-level", value_name = "N", default_value = "1")]
338        expand_level: usize,
339    },
340
341    /// Query execution log.
342    #[command(display_order = 300)]
343    Log {
344        /// Filter by operation type (patch, delete, batch, plan, apply-files, query).
345        #[arg(short, long)]
346        operation_type: Option<String>,
347
348        /// Filter by status (ok, error, partial).
349        #[arg(short, long)]
350        status: Option<String>,
351
352        /// Show operations after this date (ISO 8601 or Unix timestamp).
353        #[arg(long)]
354        after: Option<String>,
355
356        /// Show operations before this date (ISO 8601 or Unix timestamp).
357        #[arg(long)]
358        before: Option<String>,
359
360        /// Maximum number of results.
361        #[arg(short, long, default_value = "20")]
362        limit: usize,
363
364        /// Skip first N results.
365        #[arg(long, default_value = "0")]
366        offset: usize,
367
368        /// Get specific execution by ID.
369        #[arg(short, long)]
370        execution_id: Option<String>,
371
372        /// Output as JSON.
373        #[arg(short, long)]
374        json: bool,
375
376        /// Show statistics only.
377        #[arg(long)]
378        stats: bool,
379    },
380
381    /// Explain an error code with detailed documentation.
382    #[command(display_order = 400)]
383    Explain {
384        /// Error code to explain (e.g., SPL-E001, SPL-E002)
385        #[arg(short, long, value_name = "CODE")]
386        code: String,
387    },
388
389    /// Search for code patterns in files.
390    #[command(display_order = 401)]
391    Search {
392        /// Text pattern to search for.
393        #[arg(short, long)]
394        pattern: String,
395
396        /// Files or directories to search (defaults to current directory).
397        #[arg(long, value_name = "PATH", default_value = ".")]
398        path: std::path::PathBuf,
399
400        /// Optional language filter (auto-detect if not specified).
401        #[arg(long, value_name = "LANG")]
402        language: Option<Language>,
403
404        /// Glob pattern for file filtering (e.g., "src/**/*.rs", "tests/**/*.py").
405        /// If not specified, searches all supported file types in path.
406        #[arg(short = 'g', long, value_name = "GLOB")]
407        glob: Option<String>,
408
409        /// Number of context lines after the match.
410        #[arg(short = 'A', long, value_name = "N", default_value = "0")]
411        context_after: usize,
412
413        /// Number of context lines before the match.
414        #[arg(short = 'B', long, value_name = "N", default_value = "0")]
415        context_before: usize,
416
417        /// Number of context lines before and after the match (default: 2).
418        #[arg(short = 'C', long, value_name = "N", default_value = "2")]
419        context_both: usize,
420
421        /// Apply replacement to all matches (atomic with rollback on failure).
422        #[arg(long, requires = "replace")]
423        apply: bool,
424
425        /// Replacement text (required with --apply).
426        #[arg(short = 'r', long, value_name = "TEXT")]
427        replace: Option<String>,
428
429        /// Output results as JSON.
430        #[arg(long)]
431        json: bool,
432    },
433
434    /// Show database statistics (files, symbols, refs, calls, chunks)
435    ///
436    /// Use --detect-backend to check which backend format the database uses.
437    #[command(display_order = 100)]
438    Status {
439        /// Path to the Magellan database
440        #[arg(short, long)]
441        db: std::path::PathBuf,
442
443        /// Detect and report the backend format (sqlite only)
444        #[arg(long, default_value = "false")]
445        detect_backend: bool,
446    },
447
448    /// Find symbols by name or 16-character symbol ID
449    #[command(display_order = 101)]
450    Find {
451        /// Path to the Magellan database
452        #[arg(short, long)]
453        db: std::path::PathBuf,
454
455        /// Symbol name to search
456        #[arg(short, long, conflicts_with = "symbol_id")]
457        name: Option<String>,
458
459        /// 16-character hex symbol ID
460        #[arg(long, conflicts_with = "name")]
461        symbol_id: Option<String>,
462
463        /// Return all matches (default: first match only)
464        #[arg(short, long)]
465        ambiguous: bool,
466
467        /// Output format (human, json, pretty)
468        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
469        output: OutputFormat,
470    },
471
472    /// Show call relationships for a symbol
473    #[command(display_order = 102)]
474    Refs {
475        /// Path to the Magellan database
476        #[arg(short, long)]
477        db: std::path::PathBuf,
478
479        /// Symbol name
480        #[arg(short, long)]
481        name: String,
482
483        /// File path containing the symbol
484        #[arg(short, long)]
485        path: std::path::PathBuf,
486
487        /// Direction: in (callers), out (callees), both (default)
488        #[arg(long, value_enum, default_value_t = CallDirection::Both)]
489        direction: CallDirection,
490
491        /// Output format (human, json, pretty)
492        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
493        output: OutputFormat,
494
495        /// Generate DOT graph output for visualization
496        #[arg(long)]
497        impact_graph: bool,
498    },
499
500    /// List all indexed files
501    #[command(display_order = 103)]
502    Files {
503        /// Path to the Magellan database
504        #[arg(short, long)]
505        db: std::path::PathBuf,
506
507        /// Include symbol count per file
508        #[arg(long)]
509        symbols: bool,
510
511        /// Output format (human, json, pretty)
512        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
513        output: OutputFormat,
514    },
515
516    /// Export graph data in JSON, JSONL, or CSV format
517    #[command(display_order = 106)]
518    Export {
519        /// Path to the Magellan database
520        #[arg(short, long)]
521        db: std::path::PathBuf,
522
523        /// Export format (json, jsonl, csv)
524        #[arg(short, long, value_enum, default_value_t = ExportFormat::Json)]
525        format: ExportFormat,
526
527        /// Output file path (writes to stdout if not specified)
528        #[arg(long)]
529        file: Option<std::path::PathBuf>,
530    },
531
532    /// Migrate Magellan database to latest schema version
533    #[command(display_order = 107)]
534    MigrateDb {
535        /// Path to the Magellan database
536        #[arg(short, long = "db", default_value = ".magellan/magellan.db")]
537        db_path: std::path::PathBuf,
538
539        /// Create backup before migrating
540        #[arg(long, default_value = "true")]
541        backup: bool,
542
543        /// Check migration status without migrating
544        #[arg(long)]
545        dry_run: bool,
546    },
547
548    /// Rename a symbol across all files using byte-accurate references
549    #[command(display_order = 110)]
550    Rename {
551        /// Symbol ID (32-char BLAKE3 or 16-char SHA-256)
552        #[arg(short, long, conflicts_with = "name")]
553        symbol: Option<String>,
554
555        /// Symbol name (requires --file)
556        #[arg(long, conflicts_with = "symbol")]
557        name: Option<String>,
558
559        /// File path for symbol name resolution (required with --name)
560        #[arg(short, long)]
561        file: Option<std::path::PathBuf>,
562
563        /// New name for the symbol
564        #[arg(short, long)]
565        to: String,
566
567        /// Path to Magellan database (default: .magellan/magellan.db)
568        #[arg(short, long, default_value = ".magellan/magellan.db")]
569        db: std::path::PathBuf,
570
571        /// Preview changes without applying
572        #[arg(short = 'n', long = "dry-run")]
573        preview: bool,
574
575        /// Generate proof file (requires --dry-run)
576        #[arg(long)]
577        proof: bool,
578
579        /// Override backup directory (default: .splice/backups/)
580        #[arg(long)]
581        backup_dir: Option<std::path::PathBuf>,
582
583        /// Skip backup creation
584        #[arg(long)]
585        no_backup: bool,
586        /// Create backup before rename (default: true for safety, use --no-backup to skip)
587        #[arg(long, default_value = "true")]
588        create_backup: bool,
589
590        /// Capture graph snapshot before renaming.
591        #[arg(long)]
592        snapshot_before: bool,
593
594        /// Generate DOT graph output for visualization (requires --preview)
595        #[arg(long, requires = "preview")]
596        impact_graph: bool,
597    },
598
599    /// Show reachability analysis for a symbol (caller/callee chains)
600    #[command(display_order = 111)]
601    Reachable {
602        /// Symbol name to analyze
603        #[arg(short, long)]
604        symbol: String,
605
606        /// File path containing the symbol
607        #[arg(short, long)]
608        path: std::path::PathBuf,
609
610        /// Path to Magellan database (default: .magellan/magellan.db)
611        #[arg(short, long, default_value = ".magellan/magellan.db")]
612        db: std::path::PathBuf,
613
614        /// Analysis direction: forward (callees), reverse (callers), both
615        #[arg(long, value_enum, default_value_t = ReachabilityDirection::Forward)]
616        direction: ReachabilityDirection,
617
618        /// Maximum depth to traverse (default: 10)
619        #[arg(long, default_value = "10")]
620        max_depth: usize,
621
622        /// Output format (human, json, pretty)
623        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
624        output: OutputFormat,
625
626        /// Generate DOT graph output for visualization
627        #[arg(long)]
628        impact_graph: bool,
629    },
630
631    /// Detect dead code (unreachable symbols) from entry points
632    #[command(display_order = 112)]
633    DeadCode {
634        /// Entry point symbol name (e.g., "main", "MyApp::run")
635        #[arg(short, long)]
636        entry: String,
637
638        /// File path containing the entry point symbol
639        #[arg(short, long)]
640        path: std::path::PathBuf,
641
642        /// Path to Magellan database (default: .magellan/magellan.db)
643        #[arg(short, long, default_value = ".magellan/magellan.db")]
644        db: std::path::PathBuf,
645
646        /// Exclude public symbols from dead code list
647        #[arg(long)]
648        exclude_public: bool,
649
650        /// Group results by file (default: true for human output)
651        #[arg(long, default_value = "true")]
652        group_by_file: bool,
653
654        /// Output format (human, json, pretty)
655        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
656        output: OutputFormat,
657    },
658
659    /// Detect cycles in the call graph
660    #[command(display_order = 113)]
661    Cycles {
662        /// Path to Magellan database (default: .magellan/magellan.db)
663        #[arg(short, long, default_value = ".magellan/magellan.db")]
664        db: std::path::PathBuf,
665
666        /// Optional: find cycles containing this specific symbol
667        #[arg(short, long)]
668        symbol: Option<String>,
669
670        /// Optional: file path for symbol resolution (required with --symbol)
671        #[arg(short, long)]
672        path: Option<std::path::PathBuf>,
673
674        /// Maximum number of cycles to return (default: 100)
675        #[arg(short, long, default_value = "100")]
676        max_cycles: usize,
677
678        /// Show cycle members (default: true)
679        #[arg(long, default_value = "true")]
680        show_members: bool,
681
682        /// Output format (human, json, pretty)
683        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
684        output: OutputFormat,
685    },
686
687    /// Analyze condensation graph (SCCs collapsed to DAG)
688    #[command(display_order = 114)]
689    Condense {
690        /// Path to Magellan database (default: .magellan/magellan.db)
691        #[arg(short, long, default_value = ".magellan/magellan.db")]
692        db: std::path::PathBuf,
693
694        /// Show SCC members (default: true for human output)
695        #[arg(long, default_value = "true")]
696        show_members: bool,
697
698        /// Show topological levels
699        #[arg(long)]
700        show_levels: bool,
701
702        /// Output format (human, json, pretty)
703        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
704        output: OutputFormat,
705    },
706
707    /// Perform program slicing (forward/backward impact analysis)
708    #[command(display_order = 115)]
709    Slice {
710        /// Target symbol to slice from
711        #[arg(short, long)]
712        target: String,
713
714        /// File path containing the target symbol
715        #[arg(short, long)]
716        path: std::path::PathBuf,
717
718        /// Path to Magellan database (default: .magellan/magellan.db)
719        #[arg(short, long, default_value = ".magellan/magellan.db")]
720        db: std::path::PathBuf,
721
722        /// Slice direction: forward (what this affects) or backward (what affects this)
723        #[arg(long, value_enum, default_value_t = SliceDirection::Forward)]
724        direction: SliceDirection,
725
726        /// Maximum depth to traverse (default: unlimited)
727        #[arg(long)]
728        max_depth: Option<usize>,
729
730        /// Output format (human, json, pretty)
731        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
732        output: OutputFormat,
733    },
734
735    /// Validate proof checksums for refactoring audit trail
736    #[command(display_order = 116)]
737    ValidateProof {
738        /// Path to the proof JSON file
739        #[arg(short, long)]
740        proof: std::path::PathBuf,
741
742        /// Output format (human, json, pretty)
743        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
744        output: OutputFormat,
745    },
746
747    /// Compare two snapshots and report differences
748    #[command(display_order = 117)]
749    Verify {
750        /// Path to the "before" snapshot file
751        #[arg(short = 'b', long)]
752        before: std::path::PathBuf,
753
754        /// Path to the "after" snapshot file
755        #[arg(short = 'a', long)]
756        after: std::path::PathBuf,
757
758        /// Show detailed symbol-by-symbol differences
759        #[arg(long)]
760        detailed: bool,
761
762        /// Output format (human, json, pretty)
763        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
764        output: OutputFormat,
765    },
766
767    /// Execute batch operations from YAML spec
768    #[command(display_order = 250)]
769    Batch {
770        /// Path to the batch specification YAML file
771        #[arg(short = 'f', long)]
772        spec: std::path::PathBuf,
773
774        /// Database path for snapshot/impact analysis (required for rollback)
775        #[arg(short = 'd', long)]
776        db: Option<std::path::PathBuf>,
777
778        /// Preview changes without applying (alias: --dry-run, -n)
779        #[arg(short = 'n', long = "dry-run")]
780        dry_run: bool,
781
782        /// Continue on error instead of stopping
783        #[arg(long = "continue-on-error")]
784        continue_on_error: bool,
785
786        /// Rollback mode: auto, never, always
787        #[arg(long, value_enum, default_value_t = CliRollbackMode::Auto)]
788        rollback: CliRollbackMode,
789
790        /// Optional validation mode (off, os, path).
791        #[arg(long, value_name = "MODE")]
792        analyzer: Option<AnalyzerMode>,
793
794        /// Path to rust-analyzer binary (used with --analyzer path).
795        #[arg(long, value_name = "PATH")]
796        analyzer_binary: Option<std::path::PathBuf>,
797    },
798
799    /// Create a new file with validation
800    #[command(display_order = 105)]
801    Create {
802        /// Path to the file to create
803        #[arg(short, long)]
804        file: std::path::PathBuf,
805
806        /// Validate only (don't write file)
807        #[arg(short = 'V', long)]
808        validate_only: bool,
809
810        /// Add module declaration to parent module
811        #[arg(short = 'm', long)]
812        with_mod: bool,
813
814        /// Workspace directory (default: current directory)
815        #[arg(short, long, default_value = ".")]
816        workspace: std::path::PathBuf,
817    },
818
819    /// Get grounded code completions using Magellan database
820    #[command(display_order = 119)]
821    Complete {
822        /// Path to the source file
823        #[arg(short, long)]
824        file: std::path::PathBuf,
825
826        /// Line number (1-based)
827        #[arg(short, long)]
828        line: usize,
829
830        /// Column number (1-based)
831        #[arg(short, long)]
832        column: usize,
833
834        /// Maximum number of suggestions
835        #[arg(short, long, default_value = "10")]
836        max_results: usize,
837
838        /// Path to Magellan database
839        #[arg(short, long, default_value = ".magellan/splice.db")]
840        db: std::path::PathBuf,
841    },
842
843    /// Manage code graph snapshots
844    #[command(display_order = 120, subcommand)]
845    Snapshots(SnapshotsCommands),
846}
847
848/// Snapshot management subcommands.
849#[derive(clap::Subcommand, Debug, Clone)]
850pub enum SnapshotsCommands {
851    /// List all snapshots
852    List {
853        /// Filter by operation type (patch, delete, rename)
854        #[arg(short, long)]
855        operation: Option<String>,
856
857        /// Maximum number of snapshots to show
858        #[arg(short = 'n', long)]
859        limit: Option<usize>,
860
861        /// Show total disk usage
862        #[arg(long)]
863        disk_usage: bool,
864
865        /// Output format (human, json, pretty)
866        #[arg(short, long, value_enum, default_value_t = OutputFormat::Human)]
867        output: OutputFormat,
868    },
869
870    /// Delete a specific snapshot
871    Delete {
872        /// Snapshot ID (timestamp or filename)
873        #[arg(short, long)]
874        id: String,
875
876        /// Skip confirmation prompt
877        #[arg(long)]
878        force: bool,
879    },
880
881    /// Clean up old snapshots (keep N most recent)
882    Cleanup {
883        /// Number of recent snapshots to keep (default: 10)
884        #[arg(short = 'k', long, default_value = "10")]
885        keep: usize,
886
887        /// Show what would be deleted without deleting
888        #[arg(long)]
889        dry_run: bool,
890
891        /// Confirm deletion of more than 50 snapshots. Without this flag,
892        /// a bulk-delete refuses to run and asks for either `--yes` or `--dry-run`.
893        #[arg(long)]
894        yes: bool,
895    },
896}