sc/cli/mod.rs
1//! CLI definitions using clap.
2
3use clap::{Args, Parser, Subcommand, ValueEnum};
4use std::path::PathBuf;
5
6/// Output format for list/query commands.
7#[derive(ValueEnum, Clone, Debug, Default, PartialEq, Eq)]
8pub enum OutputFormat {
9 /// Human-readable table (default)
10 #[default]
11 Table,
12 /// JSON (same as --json)
13 Json,
14 /// Comma-separated values
15 Csv,
16}
17
18pub mod commands;
19
20/// SaveContext CLI - The OS for AI coding agents
21#[derive(Parser, Debug)]
22#[command(name = "sc", author, version, about, long_about = None)]
23pub struct Cli {
24 #[command(subcommand)]
25 pub command: Commands,
26
27 /// Database path (default: ~/.savecontext/data/savecontext.db)
28 #[arg(long, global = true, env = "SC_DB")]
29 pub db: Option<PathBuf>,
30
31 /// Actor name for audit trail
32 #[arg(long, global = true, env = "SC_ACTOR")]
33 pub actor: Option<String>,
34
35 /// Active session ID (passed by MCP server)
36 #[arg(long, global = true, env = "SC_SESSION")]
37 pub session: Option<String>,
38
39 /// Output as JSON (for agent integration)
40 #[arg(long, alias = "robot", global = true)]
41 pub json: bool,
42
43 /// Output format (table, json, csv)
44 #[arg(long, value_enum, global = true, default_value_t)]
45 pub format: OutputFormat,
46
47 /// Output only the ID/key (for agent scripting)
48 #[arg(long, global = true)]
49 pub silent: bool,
50
51 /// Preview changes without writing to the database
52 #[arg(long, global = true)]
53 pub dry_run: bool,
54
55 /// Increase logging verbosity (-v, -vv)
56 #[arg(short, long, action = clap::ArgAction::Count, global = true)]
57 pub verbose: u8,
58
59 /// Quiet mode (no output except errors)
60 #[arg(short, long, global = true)]
61 pub quiet: bool,
62
63 /// Disable colored output
64 #[arg(long, global = true)]
65 pub no_color: bool,
66}
67
68#[derive(Subcommand, Debug)]
69pub enum Commands {
70 /// Initialize a SaveContext workspace
71 Init {
72 /// Use global location (~/.savecontext/)
73 #[arg(long)]
74 global: bool,
75
76 /// Overwrite existing database
77 #[arg(long)]
78 force: bool,
79 },
80
81 /// Print version information
82 Version,
83
84 /// Session management
85 Session {
86 #[command(subcommand)]
87 command: SessionCommands,
88 },
89
90 /// Show current session status
91 Status,
92
93 /// Save a context item
94 Save(SaveArgs),
95
96 /// Get/search context items
97 Get(GetArgs),
98
99 /// Delete a context item
100 Delete {
101 /// Key of the item to delete
102 key: String,
103 },
104
105 /// Update a context item
106 Update(UpdateArgs),
107
108 /// Tag context items
109 Tag {
110 #[command(subcommand)]
111 command: TagCommands,
112 },
113
114 /// Issue management
115 Issue {
116 #[command(subcommand)]
117 command: IssueCommands,
118 },
119
120 /// Checkpoint management
121 Checkpoint {
122 #[command(subcommand)]
123 command: CheckpointCommands,
124 },
125
126 /// Project memory (persistent across sessions)
127 Memory {
128 #[command(subcommand)]
129 command: MemoryCommands,
130 },
131
132 /// Sync with JSONL files
133 Sync {
134 #[command(subcommand)]
135 command: SyncCommands,
136 },
137
138 /// Project management
139 Project {
140 #[command(subcommand)]
141 command: ProjectCommands,
142 },
143
144 /// Plan management (PRDs, specs, feature docs)
145 Plan {
146 #[command(subcommand)]
147 command: PlanCommands,
148 },
149
150 /// Prepare context for compaction (auto-checkpoint + summary)
151 Compaction,
152
153 /// Generate context primer for agent injection
154 Prime {
155 /// Include Claude Code transcript summaries
156 #[arg(long)]
157 transcript: bool,
158
159 /// Maximum transcript entries to include
160 #[arg(long, default_value = "5")]
161 transcript_limit: usize,
162
163 /// Compact output for agent system prompt injection
164 #[arg(long)]
165 compact: bool,
166
167 /// Smart relevance-ranked context selection
168 #[arg(long)]
169 smart: bool,
170
171 /// Token budget for smart mode (default: 4000)
172 #[arg(long, default_value = "4000")]
173 budget: usize,
174
175 /// Topic query for semantic boosting in smart mode
176 #[arg(long)]
177 query: Option<String>,
178
179 /// Temporal decay half-life in days for smart mode (default: 14)
180 #[arg(long, default_value = "14")]
181 decay_days: u32,
182 },
183
184 /// Generate shell completions
185 Completions {
186 /// Shell to generate completions for
187 #[arg(value_enum)]
188 shell: Shell,
189 },
190
191 /// Embedding configuration and management
192 Embeddings {
193 #[command(subcommand)]
194 command: EmbeddingsCommands,
195 },
196}
197
198/// Supported shells for completions.
199#[derive(clap::ValueEnum, Clone, Debug)]
200pub enum Shell {
201 Bash,
202 Zsh,
203 Fish,
204 PowerShell,
205 Elvish,
206}
207
208// ============================================================================
209// Session Commands
210// ============================================================================
211
212#[derive(Subcommand, Debug)]
213pub enum SessionCommands {
214 /// Start a new session
215 Start {
216 /// Session name
217 name: String,
218
219 /// Session description
220 #[arg(short, long)]
221 description: Option<String>,
222
223 /// Project path (defaults to current directory)
224 #[arg(short, long)]
225 project: Option<String>,
226
227 /// Channel name (auto-derived from git branch if not provided)
228 #[arg(long)]
229 channel: Option<String>,
230
231 /// Force create a new session instead of resuming existing one
232 #[arg(long)]
233 force_new: bool,
234 },
235
236 /// End current session
237 End,
238
239 /// Pause current session
240 Pause,
241
242 /// Resume a paused session
243 Resume {
244 /// Session ID to resume
245 id: String,
246 },
247
248 /// List sessions
249 List {
250 /// Filter by status (active, paused, completed, all)
251 #[arg(short, long, default_value = "active")]
252 status: String,
253
254 /// Maximum sessions to return
255 #[arg(short, long, default_value = "10")]
256 limit: usize,
257
258 /// Search sessions by name or description
259 #[arg(long)]
260 search: Option<String>,
261
262 /// Filter by project path
263 #[arg(short, long)]
264 project: Option<String>,
265
266 /// Show sessions from all projects (ignore project filter)
267 #[arg(long)]
268 all_projects: bool,
269
270 /// Include completed sessions (when status is not 'all' or 'completed')
271 #[arg(long)]
272 include_completed: bool,
273 },
274
275 /// Switch to a different session
276 Switch {
277 /// Session ID to switch to
278 id: String,
279 },
280
281 /// Rename current session
282 Rename {
283 /// New session name
284 name: String,
285 },
286
287 /// Delete a session permanently
288 Delete {
289 /// Session ID
290 id: String,
291
292 /// Skip confirmation and delete
293 #[arg(short, long)]
294 force: bool,
295 },
296
297 /// Add a project path to a session
298 AddPath {
299 /// Session ID (uses current active session if not specified)
300 #[arg(short, long)]
301 id: Option<String>,
302
303 /// Project path to add (defaults to current directory)
304 path: Option<String>,
305 },
306
307 /// Remove a project path from a session
308 RemovePath {
309 /// Session ID (uses current active session if not specified)
310 #[arg(short, long)]
311 id: Option<String>,
312
313 /// Project path to remove
314 path: String,
315 },
316}
317
318// ============================================================================
319// Context Item Commands (Save/Get)
320// ============================================================================
321
322#[derive(Args, Debug)]
323pub struct SaveArgs {
324 /// Unique key for this context item
325 pub key: String,
326
327 /// Value to save
328 pub value: String,
329
330 /// Category (reminder, decision, progress, note)
331 #[arg(short, long, default_value = "note")]
332 pub category: String,
333
334 /// Priority (high, normal, low)
335 #[arg(short, long, default_value = "normal")]
336 pub priority: String,
337}
338
339#[derive(Args, Debug, Default)]
340pub struct GetArgs {
341 /// Search query (smart semantic search when embeddings enabled, keyword fallback)
342 #[arg(short = 's', long)]
343 pub query: Option<String>,
344
345 /// Get by exact key
346 #[arg(short, long)]
347 pub key: Option<String>,
348
349 /// Filter by category
350 #[arg(short, long)]
351 pub category: Option<String>,
352
353 /// Filter by priority
354 #[arg(short = 'P', long)]
355 pub priority: Option<String>,
356
357 /// Search across all sessions (not just current)
358 #[arg(long)]
359 pub search_all_sessions: bool,
360
361 /// Semantic search threshold (0.0-1.0, lower = more results)
362 #[arg(long)]
363 pub threshold: Option<f64>,
364
365 /// Semantic search mode (fast, quality, tiered)
366 ///
367 /// - fast: Instant results using Model2Vec (lower accuracy)
368 /// - quality: Slower but more accurate results using Ollama/HuggingFace
369 /// - tiered: Fast candidates, quality re-ranking (default)
370 #[arg(long, value_parser = parse_search_mode)]
371 pub search_mode: Option<crate::embeddings::SearchMode>,
372
373 /// Pagination offset
374 #[arg(long)]
375 pub offset: Option<usize>,
376
377 /// Maximum items to return
378 #[arg(short, long, default_value = "50")]
379 pub limit: usize,
380}
381
382/// Parse search mode from string
383fn parse_search_mode(s: &str) -> std::result::Result<crate::embeddings::SearchMode, String> {
384 s.parse()
385}
386
387#[derive(Args, Debug)]
388pub struct UpdateArgs {
389 /// Key of the item to update
390 pub key: String,
391
392 /// New value
393 #[arg(long)]
394 pub value: Option<String>,
395
396 /// New category (reminder, decision, progress, note)
397 #[arg(short, long)]
398 pub category: Option<String>,
399
400 /// New priority (high, normal, low)
401 #[arg(short, long)]
402 pub priority: Option<String>,
403
404 /// New channel
405 #[arg(long)]
406 pub channel: Option<String>,
407}
408
409#[derive(Subcommand, Debug)]
410pub enum TagCommands {
411 /// Add tags to context items
412 Add {
413 /// Key of the item to tag
414 key: String,
415
416 /// Tags to add (comma-separated or multiple --tag flags)
417 #[arg(short, long, value_delimiter = ',', required = true)]
418 tags: Vec<String>,
419 },
420
421 /// Remove tags from context items
422 Remove {
423 /// Key of the item to untag
424 key: String,
425
426 /// Tags to remove (comma-separated or multiple --tag flags)
427 #[arg(short, long, value_delimiter = ',', required = true)]
428 tags: Vec<String>,
429 },
430}
431
432// ============================================================================
433// Issue Commands
434// ============================================================================
435
436#[derive(Subcommand, Debug)]
437pub enum IssueCommands {
438 /// Create a new issue
439 Create(IssueCreateArgs),
440
441 /// List issues
442 List(IssueListArgs),
443
444 /// Show issue details
445 Show {
446 /// Issue ID (short or full)
447 id: String,
448 },
449
450 /// Update an issue
451 Update(IssueUpdateArgs),
452
453 /// Mark issue(s) as complete
454 Complete {
455 /// Issue IDs (one or more)
456 ids: Vec<String>,
457
458 /// Reason for closing
459 #[arg(short = 'r', long)]
460 reason: Option<String>,
461 },
462
463 /// Claim issue(s) (assign to self)
464 Claim {
465 /// Issue IDs (one or more)
466 ids: Vec<String>,
467 },
468
469 /// Release issue(s)
470 Release {
471 /// Issue IDs (one or more)
472 ids: Vec<String>,
473 },
474
475 /// Delete issue(s)
476 Delete {
477 /// Issue IDs (one or more)
478 ids: Vec<String>,
479 },
480
481 /// Manage issue labels
482 Label {
483 #[command(subcommand)]
484 command: IssueLabelCommands,
485 },
486
487 /// Manage issue dependencies
488 Dep {
489 #[command(subcommand)]
490 command: IssueDepCommands,
491 },
492
493 /// Clone an issue
494 Clone {
495 /// Issue ID to clone
496 id: String,
497
498 /// New title (defaults to "Copy of <original>")
499 #[arg(short, long)]
500 title: Option<String>,
501 },
502
503 /// Mark issue as duplicate of another
504 Duplicate {
505 /// Issue ID to mark as duplicate
506 id: String,
507
508 /// Issue ID this is a duplicate of
509 #[arg(long)]
510 of: String,
511 },
512
513 /// List issues ready to work on
514 Ready {
515 /// Maximum issues to return
516 #[arg(short, long, default_value = "10")]
517 limit: usize,
518 },
519
520 /// Get next block of issues and claim them
521 NextBlock {
522 /// Number of issues to claim
523 #[arg(short, long, default_value = "3")]
524 count: usize,
525 },
526
527 /// Create multiple issues at once with dependencies
528 Batch {
529 /// JSON input containing issues array, dependencies, and optional planId
530 #[arg(long)]
531 json_input: String,
532 },
533
534 /// Count issues grouped by a field
535 Count {
536 /// Group by: status, type, priority, assignee
537 #[arg(short, long, default_value = "status")]
538 group_by: String,
539 },
540
541 /// List stale issues (not updated recently)
542 Stale {
543 /// Issues not updated in this many days
544 #[arg(short, long, default_value = "7")]
545 days: u64,
546
547 /// Maximum issues to return
548 #[arg(short, long, default_value = "50")]
549 limit: usize,
550 },
551
552 /// List blocked issues with their blockers
553 Blocked {
554 /// Maximum issues to return
555 #[arg(short, long, default_value = "50")]
556 limit: usize,
557 },
558}
559
560#[derive(Subcommand, Debug)]
561pub enum IssueLabelCommands {
562 /// Add labels to an issue
563 Add {
564 /// Issue ID
565 id: String,
566
567 /// Labels to add (comma-separated)
568 #[arg(short, long, value_delimiter = ',', required = true)]
569 labels: Vec<String>,
570 },
571
572 /// Remove labels from an issue
573 Remove {
574 /// Issue ID
575 id: String,
576
577 /// Labels to remove (comma-separated)
578 #[arg(short, long, value_delimiter = ',', required = true)]
579 labels: Vec<String>,
580 },
581}
582
583#[derive(Subcommand, Debug)]
584pub enum IssueDepCommands {
585 /// Add a dependency to an issue
586 Add {
587 /// Issue ID
588 id: String,
589
590 /// ID of issue this depends on
591 #[arg(long)]
592 depends_on: String,
593
594 /// Dependency type (blocks, related, parent-child, discovered-from)
595 #[arg(short = 't', long, default_value = "blocks")]
596 dep_type: String,
597 },
598
599 /// Remove a dependency from an issue
600 Remove {
601 /// Issue ID
602 id: String,
603
604 /// ID of issue to remove dependency on
605 #[arg(long)]
606 depends_on: String,
607 },
608
609 /// Show dependency tree for an issue or all epics
610 Tree {
611 /// Root issue ID (omit for all epics)
612 id: Option<String>,
613 },
614}
615
616#[derive(Args, Debug)]
617pub struct IssueCreateArgs {
618 /// Issue title
619 pub title: String,
620
621 /// Issue description
622 #[arg(short, long)]
623 pub description: Option<String>,
624
625 /// Implementation details or notes
626 #[arg(long)]
627 pub details: Option<String>,
628
629 /// Issue type (task, bug, feature, epic, chore)
630 #[arg(short = 't', long, default_value = "task")]
631 pub issue_type: String,
632
633 /// Priority (0=lowest to 4=critical)
634 #[arg(short, long, default_value = "2")]
635 pub priority: i32,
636
637 /// Parent issue ID (for subtasks)
638 #[arg(long)]
639 pub parent: Option<String>,
640
641 /// Link issue to a Plan (PRD/spec)
642 #[arg(long)]
643 pub plan_id: Option<String>,
644
645 /// Labels (-l bug -l security or -l bug,security)
646 #[arg(short, long, value_delimiter = ',')]
647 pub labels: Option<Vec<String>>,
648
649 /// Import issues from a JSONL file (one JSON object per line)
650 #[arg(short, long)]
651 pub file: Option<PathBuf>,
652}
653
654#[derive(Args, Debug, Default)]
655pub struct IssueListArgs {
656 /// Filter by specific issue ID (short or full)
657 #[arg(long)]
658 pub id: Option<String>,
659
660 /// Filter by status (backlog, open, in_progress, blocked, closed, deferred, all)
661 #[arg(short, long, default_value = "open")]
662 pub status: String,
663
664 /// Filter by exact priority (0-4)
665 #[arg(short, long)]
666 pub priority: Option<i32>,
667
668 /// Filter by minimum priority
669 #[arg(long)]
670 pub priority_min: Option<i32>,
671
672 /// Filter by maximum priority
673 #[arg(long)]
674 pub priority_max: Option<i32>,
675
676 /// Filter by type
677 #[arg(short = 't', long)]
678 pub issue_type: Option<String>,
679
680 /// Filter by labels (all must match, comma-separated)
681 #[arg(long, value_delimiter = ',')]
682 pub labels: Option<Vec<String>>,
683
684 /// Filter by labels (any must match, comma-separated)
685 #[arg(long, value_delimiter = ',')]
686 pub labels_any: Option<Vec<String>>,
687
688 /// Filter by parent issue ID
689 #[arg(long)]
690 pub parent: Option<String>,
691
692 /// Filter by plan ID
693 #[arg(long)]
694 pub plan: Option<String>,
695
696 /// Filter issues with subtasks
697 #[arg(long)]
698 pub has_subtasks: bool,
699
700 /// Filter issues without subtasks
701 #[arg(long)]
702 pub no_subtasks: bool,
703
704 /// Filter issues with dependencies
705 #[arg(long)]
706 pub has_deps: bool,
707
708 /// Filter issues without dependencies
709 #[arg(long)]
710 pub no_deps: bool,
711
712 /// Sort by field (priority, createdAt, updatedAt)
713 #[arg(long, default_value = "createdAt")]
714 pub sort: String,
715
716 /// Sort order (asc, desc)
717 #[arg(long, default_value = "desc")]
718 pub order: String,
719
720 /// Filter by issues created in last N days
721 #[arg(long)]
722 pub created_days: Option<i64>,
723
724 /// Filter by issues created in last N hours
725 #[arg(long)]
726 pub created_hours: Option<i64>,
727
728 /// Filter by issues updated in last N days
729 #[arg(long)]
730 pub updated_days: Option<i64>,
731
732 /// Filter by issues updated in last N hours
733 #[arg(long)]
734 pub updated_hours: Option<i64>,
735
736 /// Search in title/description
737 #[arg(long)]
738 pub search: Option<String>,
739
740 /// Filter by assignee
741 #[arg(long)]
742 pub assignee: Option<String>,
743
744 /// Search across all projects
745 #[arg(long)]
746 pub all_projects: bool,
747
748 /// Maximum issues to return
749 #[arg(short, long, default_value = "50")]
750 pub limit: usize,
751}
752
753#[derive(Args, Debug)]
754pub struct IssueUpdateArgs {
755 /// Issue ID
756 pub id: String,
757
758 /// New title
759 #[arg(long)]
760 pub title: Option<String>,
761
762 /// New description
763 #[arg(short, long)]
764 pub description: Option<String>,
765
766 /// New details
767 #[arg(long)]
768 pub details: Option<String>,
769
770 /// New status
771 #[arg(short, long)]
772 pub status: Option<String>,
773
774 /// New priority
775 #[arg(short, long)]
776 pub priority: Option<i32>,
777
778 /// New type
779 #[arg(short = 't', long)]
780 pub issue_type: Option<String>,
781
782 /// New parent issue ID
783 #[arg(long)]
784 pub parent: Option<String>,
785
786 /// New plan ID
787 #[arg(long)]
788 pub plan: Option<String>,
789}
790
791// ============================================================================
792// Checkpoint Commands
793// ============================================================================
794
795#[derive(Subcommand, Debug)]
796pub enum CheckpointCommands {
797 /// Create a checkpoint
798 Create {
799 /// Checkpoint name
800 name: String,
801
802 /// Description
803 #[arg(short, long)]
804 description: Option<String>,
805
806 /// Include git status
807 #[arg(long)]
808 include_git: bool,
809 },
810
811 /// List checkpoints
812 List {
813 /// Search checkpoints by name or description
814 #[arg(short, long)]
815 search: Option<String>,
816
817 /// Filter by session ID
818 #[arg(long)]
819 session: Option<String>,
820
821 /// Filter by project path
822 #[arg(long)]
823 project: Option<String>,
824
825 /// Include checkpoints from all projects
826 #[arg(long)]
827 all_projects: bool,
828
829 /// Maximum checkpoints to return
830 #[arg(short, long, default_value = "20")]
831 limit: usize,
832
833 /// Pagination offset
834 #[arg(long)]
835 offset: Option<usize>,
836 },
837
838 /// Show checkpoint details
839 Show {
840 /// Checkpoint ID
841 id: String,
842 },
843
844 /// Restore from checkpoint
845 Restore {
846 /// Checkpoint ID
847 id: String,
848
849 /// Only restore items in these categories (comma-separated)
850 #[arg(long, value_delimiter = ',')]
851 categories: Option<Vec<String>>,
852
853 /// Only restore items with these tags (comma-separated)
854 #[arg(long, value_delimiter = ',')]
855 tags: Option<Vec<String>>,
856 },
857
858 /// Delete a checkpoint
859 Delete {
860 /// Checkpoint ID
861 id: String,
862 },
863
864 /// Add items to an existing checkpoint
865 AddItems {
866 /// Checkpoint ID
867 id: String,
868
869 /// Context item keys to add (comma-separated)
870 #[arg(short, long, value_delimiter = ',', required = true)]
871 keys: Vec<String>,
872 },
873
874 /// Remove items from a checkpoint
875 RemoveItems {
876 /// Checkpoint ID
877 id: String,
878
879 /// Context item keys to remove (comma-separated)
880 #[arg(short, long, value_delimiter = ',', required = true)]
881 keys: Vec<String>,
882 },
883
884 /// List items in a checkpoint
885 Items {
886 /// Checkpoint ID
887 id: String,
888 },
889}
890
891// ============================================================================
892// Memory Commands (Project-level persistent storage)
893// ============================================================================
894
895#[derive(Subcommand, Debug)]
896pub enum MemoryCommands {
897 /// Save a memory item
898 Save {
899 /// Key
900 key: String,
901
902 /// Value
903 value: String,
904
905 /// Category (command, config, note)
906 #[arg(short, long, default_value = "command")]
907 category: String,
908 },
909
910 /// Get a memory item
911 Get {
912 /// Key
913 key: String,
914 },
915
916 /// List memory items
917 List {
918 /// Filter by category
919 #[arg(short, long)]
920 category: Option<String>,
921 },
922
923 /// Delete a memory item
924 Delete {
925 /// Key
926 key: String,
927 },
928}
929
930// ============================================================================
931// Sync Commands
932// ============================================================================
933
934#[derive(Subcommand, Debug)]
935pub enum SyncCommands {
936 /// Export to JSONL
937 Export {
938 /// Force export even if JSONL is newer
939 #[arg(long)]
940 force: bool,
941 },
942
943 /// Import from JSONL
944 Import {
945 /// Force import even with conflicts
946 #[arg(long)]
947 force: bool,
948 },
949
950 /// Show sync status
951 Status,
952}
953
954// ============================================================================
955// Project Commands
956// ============================================================================
957
958#[derive(Subcommand, Debug)]
959pub enum ProjectCommands {
960 /// Create a new project
961 Create(ProjectCreateArgs),
962
963 /// List all projects
964 List {
965 /// Include session count for each project
966 #[arg(long)]
967 session_count: bool,
968
969 /// Maximum projects to return
970 #[arg(short, long, default_value = "50")]
971 limit: usize,
972 },
973
974 /// Show project details
975 Show {
976 /// Project ID or path
977 id: String,
978 },
979
980 /// Update a project
981 Update(ProjectUpdateArgs),
982
983 /// Delete a project
984 Delete {
985 /// Project ID or path
986 id: String,
987
988 /// Skip confirmation and delete
989 #[arg(short, long)]
990 force: bool,
991 },
992}
993
994#[derive(Args, Debug)]
995pub struct ProjectCreateArgs {
996 /// Project path (defaults to current directory)
997 pub path: Option<String>,
998
999 /// Project name (defaults to directory name)
1000 #[arg(short, long)]
1001 pub name: Option<String>,
1002
1003 /// Project description
1004 #[arg(short, long)]
1005 pub description: Option<String>,
1006
1007 /// Issue ID prefix (e.g., "SC" creates SC-1, SC-2)
1008 #[arg(short = 'p', long)]
1009 pub issue_prefix: Option<String>,
1010}
1011
1012#[derive(Args, Debug)]
1013pub struct ProjectUpdateArgs {
1014 /// Project ID or path
1015 pub id: String,
1016
1017 /// New project name
1018 #[arg(short, long)]
1019 pub name: Option<String>,
1020
1021 /// New description
1022 #[arg(short, long)]
1023 pub description: Option<String>,
1024
1025 /// New issue ID prefix
1026 #[arg(short = 'p', long)]
1027 pub issue_prefix: Option<String>,
1028}
1029
1030// ============================================================================
1031// Plan Commands
1032// ============================================================================
1033
1034#[derive(Subcommand, Debug)]
1035pub enum PlanCommands {
1036 /// Create a new plan
1037 Create(PlanCreateArgs),
1038
1039 /// List plans
1040 List {
1041 /// Filter by status (draft, active, completed, all)
1042 #[arg(short, long, default_value = "active")]
1043 status: String,
1044
1045 /// Maximum plans to return
1046 #[arg(short, long, default_value = "50")]
1047 limit: usize,
1048
1049 /// Filter by session ID (use "current" for active TTY session)
1050 #[arg(long)]
1051 session: Option<String>,
1052 },
1053
1054 /// Show plan details
1055 Show {
1056 /// Plan ID
1057 id: String,
1058 },
1059
1060 /// Update a plan
1061 Update(PlanUpdateArgs),
1062
1063 /// Capture a plan from an AI coding agent's plan file
1064 Capture {
1065 /// Only look in a specific agent's directory (claude, gemini, opencode, cursor)
1066 #[arg(long)]
1067 agent: Option<String>,
1068
1069 /// Max age in minutes (default: 30)
1070 #[arg(long, default_value = "30")]
1071 max_age: u64,
1072
1073 /// Explicit plan file path (skip discovery)
1074 #[arg(long)]
1075 file: Option<PathBuf>,
1076 },
1077}
1078
1079#[derive(Args, Debug)]
1080pub struct PlanCreateArgs {
1081 /// Plan title
1082 pub title: String,
1083
1084 /// Plan content (markdown PRD/spec)
1085 #[arg(short, long)]
1086 pub content: Option<String>,
1087
1088 /// Plan status (draft, active, completed)
1089 #[arg(short, long, default_value = "active")]
1090 pub status: String,
1091
1092 /// Success criteria
1093 #[arg(long)]
1094 pub success_criteria: Option<String>,
1095
1096 /// Bind to a specific session (default: auto-resolve from TTY)
1097 #[arg(long)]
1098 pub session: Option<String>,
1099}
1100
1101#[derive(Args, Debug)]
1102pub struct PlanUpdateArgs {
1103 /// Plan ID
1104 pub id: String,
1105
1106 /// New title
1107 #[arg(long)]
1108 pub title: Option<String>,
1109
1110 /// New content
1111 #[arg(short, long)]
1112 pub content: Option<String>,
1113
1114 /// New status (draft, active, completed)
1115 #[arg(short, long)]
1116 pub status: Option<String>,
1117
1118 /// New success criteria
1119 #[arg(long)]
1120 pub success_criteria: Option<String>,
1121}
1122
1123// ============================================================================
1124// Embeddings Commands
1125// ============================================================================
1126
1127#[derive(Subcommand, Debug, Clone)]
1128pub enum EmbeddingsCommands {
1129 /// Show embeddings status and configuration
1130 Status,
1131
1132 /// Configure embedding provider
1133 Configure {
1134 /// Provider (ollama, huggingface)
1135 #[arg(short, long)]
1136 provider: Option<String>,
1137
1138 /// Enable embeddings
1139 #[arg(long)]
1140 enable: bool,
1141
1142 /// Disable embeddings
1143 #[arg(long)]
1144 disable: bool,
1145
1146 /// Model to use (provider-specific)
1147 #[arg(short, long)]
1148 model: Option<String>,
1149
1150 /// API endpoint (for custom servers)
1151 #[arg(long)]
1152 endpoint: Option<String>,
1153
1154 /// API token (for HuggingFace)
1155 #[arg(long)]
1156 token: Option<String>,
1157 },
1158
1159 /// Backfill embeddings for existing context items
1160 Backfill {
1161 /// Maximum items to process
1162 #[arg(short, long)]
1163 limit: Option<usize>,
1164
1165 /// Session ID to backfill (defaults to current)
1166 #[arg(short, long)]
1167 session: Option<String>,
1168
1169 /// Force regeneration of existing embeddings
1170 #[arg(long)]
1171 force: bool,
1172 },
1173
1174 /// Test embedding provider connectivity
1175 Test {
1176 /// Text to generate test embedding for
1177 #[arg(default_value = "Hello world")]
1178 text: String,
1179 },
1180
1181 /// Process pending embeddings in background (internal use)
1182 #[command(hide = true)]
1183 ProcessPending {
1184 /// Maximum items to process
1185 #[arg(short, long, default_value = "10")]
1186 limit: usize,
1187
1188 /// Run silently (no output)
1189 #[arg(long)]
1190 quiet: bool,
1191 },
1192
1193 /// Upgrade items with fast embeddings to quality embeddings
1194 ///
1195 /// Items saved with the 2-tier system get instant fast embeddings (Model2Vec).
1196 /// This command generates higher-quality embeddings (Ollama/HuggingFace)
1197 /// for items that only have fast embeddings.
1198 UpgradeQuality {
1199 /// Maximum items to process
1200 #[arg(short, long)]
1201 limit: Option<usize>,
1202
1203 /// Session ID to upgrade (defaults to all sessions)
1204 #[arg(short, long)]
1205 session: Option<String>,
1206 },
1207}