llm_wiki/cli.rs
1use clap::{Parser, Subcommand};
2
3/// Root CLI entry point — parses subcommands and global flags.
4#[derive(Parser)]
5#[command(
6 name = "llm-wiki",
7 version,
8 about = "Git-backed wiki engine with MCP server"
9)]
10pub struct Cli {
11 /// The subcommand to execute.
12 #[command(subcommand)]
13 pub command: Commands,
14
15 /// Target a specific wiki
16 #[arg(long, global = true)]
17 pub wiki: Option<String>,
18
19 /// Path to global config file (default: ~/.llm-wiki/config.toml).
20 /// Overrides the LLM_WIKI_CONFIG environment variable.
21 #[arg(long, global = true)]
22 pub config: Option<std::path::PathBuf>,
23}
24
25/// Top-level subcommands available from the `llm-wiki` CLI.
26#[derive(Subcommand)]
27pub enum Commands {
28 /// Manage wiki spaces
29 Spaces {
30 /// The spaces subcommand.
31 #[command(subcommand)]
32 action: SpacesAction,
33 },
34 /// Read and write configuration
35 Config {
36 /// The config subcommand.
37 #[command(subcommand)]
38 action: ConfigAction,
39 },
40 /// Content operations (read, write, new, commit)
41 Content {
42 /// The content subcommand.
43 #[command(subcommand)]
44 action: ContentAction,
45 },
46 /// Full-text BM25 search
47 Search {
48 /// Search query
49 query: String,
50 /// Filter by frontmatter type
51 #[arg(long, name = "type")]
52 r#type: Option<String>,
53 /// Omit excerpts — refs only
54 #[arg(long)]
55 no_excerpt: bool,
56 /// Max results (default: from config)
57 #[arg(long)]
58 top_k: Option<usize>,
59 /// Include section index pages in results
60 #[arg(long)]
61 include_sections: bool,
62 /// Search across all registered wikis
63 #[arg(long)]
64 cross_wiki: bool,
65 /// Output format: text | json
66 #[arg(long)]
67 format: Option<String>,
68 },
69 /// Paginated enumeration of wiki pages
70 List {
71 /// Filter by frontmatter type
72 #[arg(long, name = "type")]
73 r#type: Option<String>,
74 /// Filter by frontmatter status
75 #[arg(long)]
76 status: Option<String>,
77 /// Page number, 1-based
78 #[arg(long, default_value = "1")]
79 page: usize,
80 /// Results per page
81 #[arg(long)]
82 page_size: Option<usize>,
83 /// Output format: text | json
84 #[arg(long)]
85 format: Option<String>,
86 },
87 /// Validate and index files in the wiki tree
88 Ingest {
89 /// Slug, URI, or path relative to wiki root
90 path: String,
91 /// Validate only, no commit
92 #[arg(long)]
93 dry_run: bool,
94 /// Redact secrets from file bodies before validation (opt-in; lossy)
95 #[arg(long)]
96 redact: bool,
97 /// Output format: text | json
98 #[arg(long)]
99 format: Option<String>,
100 },
101 /// Generate a concept graph
102 Graph {
103 /// Output format: mermaid | dot | llms
104 #[arg(long)]
105 format: Option<String>,
106 /// Subgraph from this node (slug)
107 #[arg(long)]
108 root: Option<String>,
109 /// Hop limit from root
110 #[arg(long)]
111 depth: Option<usize>,
112 /// Comma-separated page types to include
113 #[arg(long, name = "type")]
114 r#type: Option<String>,
115 /// Filter edges by relation label
116 #[arg(long)]
117 relation: Option<String>,
118 /// File path for output (default: stdout)
119 #[arg(long)]
120 output: Option<String>,
121 /// Merge all mounted wikis into a unified graph
122 #[arg(long)]
123 cross_wiki: bool,
124 },
125 /// Manage the tantivy search index
126 Index {
127 /// The index subcommand.
128 #[command(subcommand)]
129 action: IndexAction,
130 },
131 /// Git commit history for a page
132 History {
133 /// Slug or wiki:// URI
134 slug: String,
135 /// Max entries to return
136 #[arg(long, short = 'n')]
137 limit: Option<usize>,
138 /// Disable rename tracking
139 #[arg(long)]
140 no_follow: bool,
141 /// Output format: text | json
142 #[arg(long)]
143 format: Option<String>,
144 },
145 /// Wiki health dashboard
146 Stats {
147 /// Output format: text | json
148 #[arg(long)]
149 format: Option<String>,
150 },
151 /// Run deterministic lint rules on the wiki index
152 Lint {
153 /// Comma-separated rule names: orphan, broken-link, broken-cross-wiki-link,
154 /// missing-fields, stale, unknown-type, articulation-point, bridge, periphery
155 #[arg(long)]
156 rules: Option<String>,
157 /// Filter output by severity: error | warning
158 #[arg(long)]
159 severity: Option<String>,
160 /// Output format: text | json
161 #[arg(long)]
162 format: Option<String>,
163 },
164 /// Suggest related pages to link
165 Suggest {
166 /// Slug or wiki:// URI
167 slug: String,
168 /// Max suggestions
169 #[arg(long, short = 'n')]
170 limit: Option<usize>,
171 /// Output format: text | json
172 #[arg(long)]
173 format: Option<String>,
174 },
175 /// Inspect and manage type schemas
176 Schema {
177 /// The schema subcommand.
178 #[command(subcommand)]
179 action: SchemaAction,
180 },
181 /// Export the full wiki to a file (llms.txt, llms-full, or json)
182 Export {
183 /// Output path (relative to wiki root or absolute; default: llms.txt)
184 #[arg(long)]
185 path: Option<String>,
186 /// Export format: llms-txt | llms-full | json
187 #[arg(long)]
188 format: Option<String>,
189 /// Page status filter: active | all (default: active, excludes archived)
190 #[arg(long)]
191 status: Option<String>,
192 },
193 /// Start the wiki MCP/ACP server
194 Serve {
195 /// Enable HTTP transport (optional port, e.g. :8080)
196 #[arg(long, value_name = "PORT")]
197 http: Option<Option<String>>,
198 /// Enable ACP transport
199 #[arg(long)]
200 acp: bool,
201 /// Enable filesystem watcher
202 #[arg(long)]
203 watch: bool,
204 /// Print what would be started, no server
205 #[arg(long)]
206 dry_run: bool,
207 },
208 /// Auto-ingest on file save (standalone watcher)
209 Watch {
210 /// Target wiki name
211 #[arg(long)]
212 wiki: Option<String>,
213 },
214 /// Inspect and manage server logs
215 Logs {
216 /// The logs subcommand.
217 #[command(subcommand)]
218 action: LogsAction,
219 },
220}
221
222/// Subcommands for `llm-wiki logs`.
223#[derive(Subcommand)]
224pub enum LogsAction {
225 /// Show recent log entries
226 Tail {
227 /// Number of lines to show (default: 50)
228 #[arg(long, default_value = "50")]
229 lines: usize,
230 },
231 /// List log files
232 List,
233 /// Delete all log files
234 Clear,
235}
236
237/// Subcommands for `llm-wiki spaces`.
238#[derive(Subcommand)]
239pub enum SpacesAction {
240 /// Create a new wiki repository
241 Create {
242 /// Path to create the wiki at
243 path: String,
244 /// Wiki name — used in wiki:// URIs
245 #[arg(long)]
246 name: String,
247 /// Optional one-line description
248 #[arg(long)]
249 description: Option<String>,
250 /// Update space entry if name differs from existing
251 #[arg(long)]
252 force: bool,
253 /// Set as default wiki
254 #[arg(long)]
255 set_default: bool,
256 /// Content directory relative to repo root (default: "wiki")
257 #[arg(long)]
258 wiki_root: Option<String>,
259 },
260 /// Register an existing wiki repository without creating files
261 Register {
262 /// Absolute path to the existing wiki repository
263 path: String,
264 /// Wiki name — used in wiki:// URIs
265 #[arg(long)]
266 name: String,
267 /// Optional one-line description
268 #[arg(long)]
269 description: Option<String>,
270 /// Content directory relative to repo root (overrides wiki.toml)
271 #[arg(long)]
272 wiki_root: Option<String>,
273 },
274 /// List all registered wikis
275 List {
276 /// Wiki name (omit for all)
277 name: Option<String>,
278 /// Output format: text | json
279 #[arg(long)]
280 format: Option<String>,
281 },
282 /// Remove a wiki from the registry
283 Remove {
284 /// Wiki name to remove
285 name: String,
286 /// Also delete the wiki directory from disk
287 #[arg(long)]
288 delete: bool,
289 },
290 /// Set the default wiki
291 SetDefault {
292 /// Wiki name to set as default
293 name: String,
294 },
295}
296
297/// Subcommands for `llm-wiki config`.
298#[derive(Subcommand)]
299pub enum ConfigAction {
300 /// Print a config value
301 Get {
302 /// Config key (e.g. defaults.search_top_k)
303 key: String,
304 },
305 /// Set a config value
306 Set {
307 /// Config key
308 key: String,
309 /// Config value
310 value: String,
311 /// Write to global config
312 #[arg(long)]
313 global: bool,
314 /// Write to per-wiki config
315 #[arg(long)]
316 wiki: Option<String>,
317 },
318 /// Print all resolved config
319 List {
320 /// Global config only
321 #[arg(long)]
322 global: bool,
323 /// Per-wiki config only
324 #[arg(long)]
325 wiki: Option<String>,
326 /// Output format: text | json
327 #[arg(long)]
328 format: Option<String>,
329 },
330}
331
332/// Subcommands for `llm-wiki content`.
333#[derive(Subcommand)]
334pub enum ContentAction {
335 /// Read a page or asset by slug or wiki:// URI
336 Read {
337 /// Slug or wiki:// URI
338 uri: String,
339 /// Strip frontmatter from output
340 #[arg(long)]
341 no_frontmatter: bool,
342 /// List co-located assets instead of content
343 #[arg(long)]
344 list_assets: bool,
345 },
346 /// Write a file into the wiki tree
347 Write {
348 /// Slug or wiki:// URI
349 uri: String,
350 /// Read content from a file instead of stdin
351 #[arg(long)]
352 file: Option<String>,
353 },
354 /// Create a page or section with scaffolded frontmatter
355 New {
356 /// Slug or wiki:// URI
357 uri: String,
358 /// Create a section instead of a page
359 #[arg(long)]
360 section: bool,
361 /// Create as bundle (folder + index.md)
362 #[arg(long)]
363 bundle: bool,
364 /// Page title (default: derived from slug)
365 #[arg(long)]
366 name: Option<String>,
367 /// Page type (default: page)
368 #[arg(long, name = "type")]
369 r#type: Option<String>,
370 /// Show what would be created without creating
371 #[arg(long)]
372 dry_run: bool,
373 },
374 /// Commit pending changes to git
375 Commit {
376 /// Page slugs to commit (omit for --all)
377 slugs: Vec<String>,
378 /// Commit all pending changes
379 #[arg(long)]
380 all: bool,
381 /// Commit message
382 #[arg(long, short)]
383 message: Option<String>,
384 },
385}
386
387/// Subcommands for `llm-wiki index`.
388#[derive(Subcommand)]
389pub enum IndexAction {
390 /// Rebuild the search index from committed Markdown
391 Rebuild {
392 /// Walk and count pages, no write
393 #[arg(long)]
394 dry_run: bool,
395 /// Output format: text | json
396 #[arg(long)]
397 format: Option<String>,
398 },
399 /// Inspect index health
400 Status {
401 /// Output format: text | json
402 #[arg(long)]
403 format: Option<String>,
404 },
405}
406
407/// Subcommands for `llm-wiki schema`.
408#[derive(Subcommand)]
409pub enum SchemaAction {
410 /// List all registered types
411 List {
412 /// Output format: text | json
413 #[arg(long)]
414 format: Option<String>,
415 },
416 /// Show JSON Schema or frontmatter template for a type
417 Show {
418 /// Type name
419 name: String,
420 /// Print frontmatter template instead of schema
421 #[arg(long)]
422 template: bool,
423 /// Output format: text | json
424 #[arg(long)]
425 format: Option<String>,
426 },
427 /// Register a custom type
428 Add {
429 /// Type name
430 name: String,
431 /// Path to JSON Schema file
432 schema_path: String,
433 },
434 /// Unregister a type and remove its pages from the index
435 Remove {
436 /// Type name
437 name: String,
438 /// Also delete/modify the schema file
439 #[arg(long)]
440 delete: bool,
441 /// Also delete page .md files from disk
442 #[arg(long)]
443 delete_pages: bool,
444 /// Show what would be done without doing it
445 #[arg(long)]
446 dry_run: bool,
447 },
448 /// Validate schema files and index resolution
449 Validate {
450 /// Validate a specific type only (omit for all)
451 name: Option<String>,
452 },
453}