1use crate::domain::{MemoryScope, OutputFormat, TargetTool, WakeupProfile};
2use clap::{Args, Parser, Subcommand, ValueEnum};
3use std::path::PathBuf;
4
5#[derive(Debug, Parser)]
6#[command(
7 name = "spool",
8 version,
9 about = "Obsidian knowledge-based context extractor"
10)]
11pub struct Cli {
12 #[command(subcommand)]
13 pub command: Command,
14}
15
16#[derive(Debug, Subcommand)]
17pub enum Command {
18 Get(GetArgs),
19 Explain(ExplainArgs),
20 Wakeup(WakeupArgs),
21 Memory(MemoryArgs),
22 Mcp(McpArgs),
23 Hook(HookArgs),
24 Init(InitArgs),
25 Status(StatusArgs),
26 #[cfg(feature = "embedding")]
27 Embedding(EmbeddingArgs),
28 Knowledge(KnowledgeArgs),
29}
30
31#[derive(Debug, Clone, Args)]
32pub struct InitArgs {
33 #[arg(long)]
35 pub vault: Option<PathBuf>,
36 #[arg(long)]
38 pub project_id: Option<String>,
39 #[arg(long)]
41 pub repo: Option<PathBuf>,
42}
43
44#[derive(Debug, Clone, Args)]
45pub struct StatusArgs {
46 #[arg(long)]
48 pub config: Option<PathBuf>,
49}
50
51#[derive(Debug, Clone, Args)]
52pub struct CommonRouteArgs {
53 #[arg(long)]
54 pub config: PathBuf,
55 #[arg(long)]
56 pub task: String,
57 #[arg(long)]
58 pub cwd: PathBuf,
59 #[arg(long, value_delimiter = ',')]
60 pub files: Vec<String>,
61 #[arg(long, value_enum, default_value_t = TargetValue::Claude)]
62 pub target: TargetValue,
63}
64
65#[derive(Debug, Clone, Args)]
66pub struct GetArgs {
67 #[command(flatten)]
68 pub common: CommonRouteArgs,
69 #[arg(long, value_enum)]
70 pub format: Option<FormatValue>,
71}
72
73#[derive(Debug, Clone, Args)]
74pub struct ExplainArgs {
75 #[command(flatten)]
76 pub common: CommonRouteArgs,
77}
78
79#[derive(Debug, Clone, Args)]
80pub struct WakeupArgs {
81 #[command(flatten)]
82 pub common: CommonRouteArgs,
83 #[arg(long, value_enum)]
84 pub profile: WakeupProfileValue,
85 #[arg(long, value_enum)]
86 pub format: Option<FormatValue>,
87}
88
89#[derive(Debug, Clone, Args)]
90pub struct MemoryArgs {
91 #[command(subcommand)]
92 pub command: MemoryCommand,
93}
94
95#[derive(Debug, Clone, Subcommand)]
96pub enum MemoryCommand {
97 List(MemoryListArgs),
98 Show(MemoryShowArgs),
99 History(MemoryShowArgs),
100 RecordManual(MemoryRecordArgs),
101 Propose(MemoryRecordArgs),
102 Accept(MemoryActionArgs),
103 Promote(MemoryActionArgs),
104 Archive(MemoryActionArgs),
105 Import(MemoryImportArgs),
106 ImportGit(MemoryImportGitArgs),
107 SyncVault(MemorySyncVaultArgs),
108 Dedup(MemoryDedupArgs),
109 Consolidate(MemoryConsolidateArgs),
110 Prune(MemoryPruneArgs),
111 Lint(MemoryLintArgs),
112 SyncIndex(MemorySyncIndexArgs),
113 Stats(MemoryStatsArgs),
114}
115
116#[derive(Debug, Clone, Args)]
117pub struct MemoryListArgs {
118 #[arg(long)]
119 pub config: PathBuf,
120 #[arg(long, value_enum)]
121 pub view: MemoryListViewValue,
122 #[arg(long, value_enum)]
123 pub format: Option<MemoryFormatValue>,
124 #[arg(long)]
125 pub daemon_bin: Option<PathBuf>,
126}
127
128#[derive(Debug, Clone, Args)]
129pub struct MemoryShowArgs {
130 #[arg(long)]
131 pub config: PathBuf,
132 #[arg(long)]
133 pub record_id: String,
134 #[arg(long, value_enum)]
135 pub format: Option<MemoryFormatValue>,
136 #[arg(long)]
137 pub daemon_bin: Option<PathBuf>,
138}
139
140#[derive(Debug, Clone, Args)]
141pub struct MemoryActionArgs {
142 #[arg(long)]
143 pub config: PathBuf,
144 #[arg(long)]
145 pub record_id: String,
146 #[arg(long)]
147 pub actor: Option<String>,
148 #[arg(long)]
149 pub reason: Option<String>,
150 #[arg(long, value_delimiter = ',')]
151 pub evidence_refs: Vec<String>,
152}
153
154#[derive(Debug, Clone, Args)]
155pub struct MemoryRecordArgs {
156 #[arg(long)]
157 pub config: PathBuf,
158 #[arg(long)]
159 pub title: String,
160 #[arg(long)]
161 pub summary: String,
162 #[arg(long)]
163 pub memory_type: String,
164 #[arg(long, value_enum)]
165 pub scope: MemoryScopeValue,
166 #[arg(long)]
167 pub source_ref: String,
168 #[arg(long)]
169 pub project_id: Option<String>,
170 #[arg(long)]
171 pub user_id: Option<String>,
172 #[arg(long)]
173 pub sensitivity: Option<String>,
174 #[arg(long)]
175 pub actor: Option<String>,
176 #[arg(long)]
177 pub reason: Option<String>,
178 #[arg(long, value_delimiter = ',')]
179 pub evidence_refs: Vec<String>,
180}
181
182#[derive(Debug, Clone, Args)]
183pub struct MemoryImportArgs {
184 #[arg(long)]
185 pub config: PathBuf,
186 #[arg(long, value_enum)]
188 pub provider: SessionProviderValue,
189 #[arg(long)]
191 pub session_id: String,
192 #[arg(long, default_value_t = false)]
194 pub apply: bool,
195 #[arg(long, default_value = "spool-importer")]
196 pub actor: String,
197 #[arg(long, value_enum, default_value_t = MemoryFormatValue::Markdown)]
198 pub format: MemoryFormatValue,
199}
200
201#[derive(Debug, Clone, Args)]
202pub struct MemoryImportGitArgs {
203 #[arg(long)]
204 pub config: PathBuf,
205 #[arg(long)]
207 pub repo: Option<PathBuf>,
208 #[arg(long, default_value_t = 30)]
210 pub limit: usize,
211 #[arg(long, default_value_t = false)]
213 pub dry_run: bool,
214}
215
216#[derive(Debug, Clone, Args)]
217pub struct MemorySyncVaultArgs {
218 #[arg(long)]
219 pub config: PathBuf,
220 #[arg(long, default_value_t = false)]
222 pub dry_run: bool,
223 #[arg(long, default_value_t = false)]
225 pub enrich: bool,
226}
227
228#[derive(Debug, Clone, Args)]
229pub struct MemoryDedupArgs {
230 #[arg(long)]
231 pub config: PathBuf,
232}
233
234#[derive(Debug, Clone, Args)]
235pub struct MemoryConsolidateArgs {
236 #[arg(long)]
237 pub config: PathBuf,
238 #[arg(long, default_value_t = false)]
240 pub dry_run: bool,
241 #[arg(long, default_value_t = false)]
243 pub apply: bool,
244}
245
246#[derive(Debug, Clone, Args)]
247pub struct MemoryPruneArgs {
248 #[arg(long)]
249 pub config: PathBuf,
250 #[arg(long, default_value_t = false)]
252 pub dry_run: bool,
253 #[arg(long, default_value_t = false)]
255 pub apply: bool,
256}
257
258#[derive(Debug, Clone, Args)]
259pub struct MemoryLintArgs {
260 #[arg(long)]
261 pub config: PathBuf,
262 #[arg(long, default_value_t = false)]
264 pub json: bool,
265}
266
267#[derive(Debug, Clone, Args)]
268pub struct MemorySyncIndexArgs {
269 #[arg(long)]
270 pub config: PathBuf,
271 #[arg(long, default_value_t = false)]
273 pub apply: bool,
274}
275
276#[derive(Debug, Clone, Args)]
277pub struct MemoryStatsArgs {
278 #[arg(long)]
279 pub config: PathBuf,
280}
281
282#[derive(Debug, Clone, Args)]
289pub struct McpArgs {
290 #[command(subcommand)]
291 pub command: McpCommand,
292}
293
294#[derive(Debug, Clone, Subcommand)]
295pub enum McpCommand {
296 Install(McpInstallArgs),
297 Update(McpUpdateArgs),
298 Uninstall(McpUninstallArgs),
299 Doctor(McpDoctorArgs),
300}
301
302#[derive(Debug, Clone, Args)]
303pub struct McpInstallArgs {
304 #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
305 pub client: ClientValue,
306 #[arg(long)]
308 pub config: PathBuf,
309 #[arg(long)]
311 pub binary_path: Option<PathBuf>,
312 #[arg(long, default_value_t = false)]
314 pub dry_run: bool,
315 #[arg(long, default_value_t = false)]
317 pub force: bool,
318 #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
320 pub format: McpReportFormat,
321}
322
323#[derive(Debug, Clone, Args)]
324pub struct McpUpdateArgs {
325 #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
326 pub client: ClientValue,
327 #[arg(long)]
329 pub config: PathBuf,
330 #[arg(long)]
332 pub binary_path: Option<PathBuf>,
333 #[arg(long, default_value_t = false)]
335 pub dry_run: bool,
336 #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
338 pub format: McpReportFormat,
339}
340
341#[derive(Debug, Clone, Args)]
342pub struct McpUninstallArgs {
343 #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
344 pub client: ClientValue,
345 #[arg(long, default_value_t = false)]
346 pub dry_run: bool,
347 #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
348 pub format: McpReportFormat,
349}
350
351#[derive(Debug, Clone, Args)]
352pub struct McpDoctorArgs {
353 #[arg(long, value_enum, default_value_t = ClientValue::Claude)]
354 pub client: ClientValue,
355 #[arg(long)]
357 pub config: PathBuf,
358 #[arg(long)]
360 pub binary_path: Option<PathBuf>,
361 #[arg(long, value_enum, default_value_t = McpReportFormat::Text)]
362 pub format: McpReportFormat,
363}
364
365#[derive(Debug, Clone, Copy, ValueEnum)]
366pub enum ClientValue {
367 Claude,
368 Codex,
369 Cursor,
370 Opencode,
371}
372
373#[derive(Debug, Clone, Copy, ValueEnum)]
374pub enum McpReportFormat {
375 Text,
376 Json,
377}
378
379#[derive(Debug, Clone, Args)]
386pub struct HookArgs {
387 #[command(subcommand)]
388 pub command: HookCommand,
389}
390
391#[derive(Debug, Clone, Subcommand)]
392pub enum HookCommand {
393 SessionStart(HookSessionStartArgs),
394 UserPrompt(HookSimpleArgs),
395 PostToolUse(HookPostToolUseArgs),
396 Stop(HookStopArgs),
397 PreCompact(HookSimpleArgs),
398}
399
400#[derive(Debug, Clone, Args)]
401pub struct HookSessionStartArgs {
402 #[arg(long)]
403 pub config: PathBuf,
404 #[arg(long)]
406 pub cwd: Option<PathBuf>,
407 #[arg(long)]
409 pub task: Option<String>,
410 #[arg(long, value_enum, default_value_t = WakeupProfileValue::Project)]
412 pub profile: WakeupProfileValue,
413}
414
415#[derive(Debug, Clone, Args)]
416pub struct HookSimpleArgs {
417 #[arg(long)]
418 pub config: PathBuf,
419 #[arg(long)]
420 pub cwd: Option<PathBuf>,
421}
422
423#[derive(Debug, Clone, Args)]
424pub struct HookPostToolUseArgs {
425 #[arg(long)]
426 pub config: PathBuf,
427 #[arg(long)]
428 pub cwd: Option<PathBuf>,
429 #[arg(long)]
431 pub tool_name: Option<String>,
432 #[arg(long)]
434 pub payload: Option<String>,
435}
436
437#[derive(Debug, Clone, Args)]
445pub struct HookStopArgs {
446 #[arg(long)]
447 pub config: PathBuf,
448 #[arg(long)]
449 pub cwd: Option<PathBuf>,
450 #[arg(long)]
451 pub transcript_path: Option<PathBuf>,
452 #[arg(long)]
455 pub hook_input: Option<String>,
456 #[arg(long, hide = true)]
460 pub home: Option<PathBuf>,
461}
462
463#[derive(Debug, Clone, Copy, ValueEnum)]
464pub enum SessionProviderValue {
465 Claude,
466 Codex,
467}
468
469impl SessionProviderValue {
470 pub fn as_str(self) -> &'static str {
471 match self {
472 Self::Claude => "claude",
473 Self::Codex => "codex",
474 }
475 }
476}
477
478#[derive(Debug, Clone, Copy, ValueEnum)]
479pub enum TargetValue {
480 Claude,
481 Codex,
482 Opencode,
483}
484
485impl From<TargetValue> for TargetTool {
486 fn from(value: TargetValue) -> Self {
487 match value {
488 TargetValue::Claude => TargetTool::Claude,
489 TargetValue::Codex => TargetTool::Codex,
490 TargetValue::Opencode => TargetTool::Opencode,
491 }
492 }
493}
494
495#[derive(Debug, Clone, Copy, ValueEnum)]
496pub enum FormatValue {
497 Prompt,
498 Markdown,
499 Json,
500}
501
502#[derive(Debug, Clone, Copy, ValueEnum)]
503pub enum WakeupProfileValue {
504 Developer,
505 Project,
506}
507
508#[derive(Debug, Clone, Copy, ValueEnum)]
509pub enum MemoryListViewValue {
510 PendingReview,
511 WakeupReady,
512}
513
514#[derive(Debug, Clone, Copy, ValueEnum)]
515pub enum MemoryFormatValue {
516 Markdown,
517 Json,
518}
519
520#[derive(Debug, Clone, Copy, ValueEnum)]
521pub enum MemoryScopeValue {
522 User,
523 Project,
524 Workspace,
525 Team,
526 Agent,
527}
528
529impl From<FormatValue> for OutputFormat {
530 fn from(value: FormatValue) -> Self {
531 match value {
532 FormatValue::Prompt => OutputFormat::Prompt,
533 FormatValue::Markdown => OutputFormat::Markdown,
534 FormatValue::Json => OutputFormat::Json,
535 }
536 }
537}
538
539impl From<WakeupProfileValue> for WakeupProfile {
540 fn from(value: WakeupProfileValue) -> Self {
541 match value {
542 WakeupProfileValue::Developer => WakeupProfile::Developer,
543 WakeupProfileValue::Project => WakeupProfile::Project,
544 }
545 }
546}
547
548impl From<MemoryScopeValue> for MemoryScope {
549 fn from(value: MemoryScopeValue) -> Self {
550 match value {
551 MemoryScopeValue::User => MemoryScope::User,
552 MemoryScopeValue::Project => MemoryScope::Project,
553 MemoryScopeValue::Workspace => MemoryScope::Workspace,
554 MemoryScopeValue::Team => MemoryScope::Team,
555 MemoryScopeValue::Agent => MemoryScope::Agent,
556 }
557 }
558}
559
560#[cfg(feature = "embedding")]
566#[derive(Debug, Clone, Args)]
567pub struct EmbeddingArgs {
568 #[command(subcommand)]
569 pub command: EmbeddingCommand,
570}
571
572#[cfg(feature = "embedding")]
573#[derive(Debug, Clone, Subcommand)]
574pub enum EmbeddingCommand {
575 Build(EmbeddingBuildArgs),
577 Status(EmbeddingStatusArgs),
579}
580
581#[cfg(feature = "embedding")]
582#[derive(Debug, Clone, Args)]
583pub struct EmbeddingBuildArgs {
584 #[arg(long)]
585 pub config: PathBuf,
586}
587
588#[cfg(feature = "embedding")]
589#[derive(Debug, Clone, Args)]
590pub struct EmbeddingStatusArgs {
591 #[arg(long)]
592 pub config: PathBuf,
593}
594
595#[derive(Debug, Clone, Args)]
600pub struct KnowledgeArgs {
601 #[command(subcommand)]
602 pub command: KnowledgeCommand,
603}
604
605#[derive(Debug, Clone, Subcommand)]
606pub enum KnowledgeCommand {
607 Distill(KnowledgeDistillArgs),
609}
610
611#[derive(Debug, Clone, Args)]
612pub struct KnowledgeDistillArgs {
613 #[arg(long)]
614 pub config: PathBuf,
615 #[arg(long, default_value_t = false)]
617 pub dry_run: bool,
618 #[arg(long, default_value_t = false)]
620 pub apply: bool,
621 #[arg(long, default_value = "spool-knowledge")]
623 pub actor: String,
624}