oxibrain_cli/cli.rs
1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Parser, Debug)]
5#[command(
6 name = "oxibrain",
7 version,
8 about = "A second brain for humans and agents"
9)]
10pub struct Cli {
11 #[arg(long, env = "OXIBRAIN_DIR", global = true)]
12 pub dir: Option<PathBuf>,
13 #[command(subcommand)]
14 pub command: Command,
15}
16
17#[derive(Subcommand, Debug)]
18pub enum Command {
19 /// Initialize a new brain store.
20 Init {
21 #[arg(long, default_value = "personal")]
22 space: String,
23 },
24 /// Ingest a file or stdin as an episode.
25 Ingest {
26 /// File path, or `-` for stdin.
27 path: PathBuf,
28 #[arg(long, default_value = "personal")]
29 space: String,
30 },
31 /// Sync a directory of markdown notes into a space. Idempotent: unchanged
32 /// files are skipped; new and modified files are ingested with
33 /// occurred_at = file mtime.
34 Sync {
35 /// Directory to scan recursively for .md files.
36 path: PathBuf,
37 #[arg(long, default_value = "personal")]
38 space: String,
39 },
40 /// Show store statistics.
41 Stats,
42 /// Health check.
43 Doctor,
44 /// Back up the store.
45 Backup {
46 #[arg(long)]
47 no_projection: bool,
48 #[arg(long)]
49 no_cache: bool,
50 /// Output directory (default: sibling of store dir).
51 #[arg(long)]
52 out: Option<PathBuf>,
53 },
54 /// Restore from a backup.
55 Restore { backup: PathBuf },
56 /// Ask a question (hybrid query).
57 Ask {
58 question: String,
59 #[arg(long, default_value = "personal")]
60 space: String,
61 },
62 /// Entity management (DESIGN §12.4: `entity show|merge|split|alias`).
63 Entity {
64 #[command(subcommand)]
65 command: EntityCmd,
66 },
67 Timeline {
68 entity_id: String,
69 #[arg(long, default_value = "personal")]
70 space: String,
71 },
72 /// Provenance for a statement.
73 Why {
74 statement_id: String,
75 #[arg(long, default_value = "personal")]
76 space: String,
77 /// Print what `rank` discarded for a query instead of provenance.
78 /// `statement_id` is then the query text (DESIGN §11.8).
79 #[arg(long)]
80 dropped: bool,
81 /// Confidence floor for --dropped (default 0). Raises it to see
82 /// BelowConfidenceFloor drops.
83 #[arg(long, default_value_t = 0.0)]
84 min_confidence: f32,
85 },
86 /// List contradicted statements.
87 Contradictions {
88 #[arg(long, default_value = "personal")]
89 space: String,
90 },
91 /// Render a page (brief) with followable links. `--kind entity` is the
92 /// default; `--kind space` shows counts + top entities; `--kind topic`
93 /// keyword-searches entity surfaces (`--topic` is the keyword).
94 Page {
95 /// Entity id (when --kind entity, default), or ignored for space/topic.
96 entity: Option<String>,
97 #[arg(long, default_value = "personal")]
98 space: String,
99 /// Target kind: entity (default), space, or topic.
100 #[arg(long, default_value = "entity")]
101 kind: String,
102 /// Keyword for --kind topic.
103 #[arg(long)]
104 topic: Option<String>,
105 },
106 /// Reproject the store.
107 Reproject,
108 /// Redact (the only true delete).
109 Redact {
110 target: String,
111 #[arg(long, default_value = "personal")]
112 space: String,
113 #[arg(long)]
114 dry_run: bool,
115 #[arg(long)]
116 reason: String,
117 },
118 /// Export to JSONL.
119 Export {
120 #[arg(long)]
121 out: Option<PathBuf>,
122 },
123 /// Import from JSONL.
124 Import { file: PathBuf },
125 /// Import from an oxios-memory SQLite database (DESIGN §16.3).
126 ImportOxios {
127 /// Path to the oxios-memory `memory.db` file.
128 db: PathBuf,
129 /// Target space (default: personal).
130 #[arg(long, default_value = "personal")]
131 space: String,
132 },
133 /// Token management (DESIGN §12.4: `token issue|list|revoke`).
134 Token {
135 #[command(subcommand)]
136 command: TokenCmd,
137 },
138 Serve {
139 /// Listen on a Unix-domain socket path instead of stdio.
140 #[arg(long)]
141 socket: Option<PathBuf>,
142 /// Listen on loopback HTTP (e.g. `127.0.0.1:8080`) instead of stdio.
143 #[arg(long)]
144 http: Option<String>,
145 /// Require token authentication on socket connections (DESIGN §11.2).
146 #[arg(long)]
147 require_token: bool,
148 /// Run as a background daemon: write a PID file and shut down
149 /// gracefully on SIGTERM/SIGINT (DESIGN §4.3, §15). External
150 /// supervision (launchd) handles backgrounding; this flag does not fork.
151 #[arg(long)]
152 daemon: bool,
153 /// Serve the desktop brain UI from this directory (GET requests).
154 /// Point at the built `apps/brain-ui/dist/`.
155 #[arg(long)]
156 ui_dir: Option<PathBuf>,
157 },
158 /// Predicate registry (DESIGN §12.4: `predicate add|list`).
159 Predicate {
160 #[command(subcommand)]
161 command: PredicateCmd,
162 },
163 /// Extract a single episode (calls the LLM, validates, projects).
164 Extract {
165 /// Episode ID to extract.
166 episode_id: String,
167 #[arg(long, default_value = "personal")]
168 space: String,
169 },
170 /// Re-extract all primary episodes with the configured extractor.
171 Reextract {
172 #[arg(long, default_value = "personal")]
173 space: String,
174 },
175 /// Model artifact management (§8.4: `model list|pull|verify|use`).
176 Model {
177 #[command(subcommand)]
178 command: ModelCmd,
179 },
180
181 /// Run the extraction evaluation suite (DESIGN §14.2).
182 Eval {
183 /// Suite: `fast` (fixture-replayed, no network) or `full` (live provider).
184 #[arg(long, default_value = "fast")]
185 suite: String,
186 },
187}
188
189// ── Nested subcommand groups (DESIGN §12.4) ────────────────────────────────
190
191#[derive(Subcommand, Debug)]
192pub enum ModelCmd {
193 /// List installed models and their verification status.
194 List,
195 /// Download the default model set (or a named model).
196 Pull {
197 /// Model name or file. Omit to pull the whole default set.
198 name: Option<String>,
199 },
200 /// Re-hash installed models against the manifest.
201 Verify {
202 /// Model name. Omit to verify all.
203 name: Option<String>,
204 },
205 /// Resolve the active model for extraction (prints path + digest).
206 Use { name: String },
207}
208
209#[derive(Subcommand, Debug)]
210pub enum TokenCmd {
211 /// Issue a new token (returns the secret once).
212 Issue {
213 #[arg(long, default_value = "personal")]
214 space: String,
215 #[arg(long, help = "Comma-separated capabilities (Read,Ingest,Write,Sample)")]
216 caps: String,
217 #[arg(long)]
218 label: Option<String>,
219 },
220 /// List all tokens (secrets redacted).
221 List,
222 /// Revoke a token by id.
223 Revoke { id: String },
224}
225
226#[derive(Subcommand, Debug)]
227pub enum PredicateCmd {
228 /// List predicates in the core/v1 registry.
229 List,
230}
231
232#[derive(Subcommand, Debug)]
233pub enum EntityCmd {
234 /// Show entity beliefs.
235 Show {
236 id: String,
237 #[arg(long, default_value = "personal")]
238 space: String,
239 },
240}