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, missing-fields, stale, unknown-type
154 #[arg(long)]
155 rules: Option<String>,
156 /// Filter output by severity: error | warning
157 #[arg(long)]
158 severity: Option<String>,
159 /// Output format: text | json
160 #[arg(long)]
161 format: Option<String>,
162 },
163 /// Suggest related pages to link
164 Suggest {
165 /// Slug or wiki:// URI
166 slug: String,
167 /// Max suggestions
168 #[arg(long, short = 'n')]
169 limit: Option<usize>,
170 /// Output format: text | json
171 #[arg(long)]
172 format: Option<String>,
173 },
174 /// Inspect and manage type schemas
175 Schema {
176 /// The schema subcommand.
177 #[command(subcommand)]
178 action: SchemaAction,
179 },
180 /// Export the full wiki to a file (llms.txt, llms-full, or json)
181 Export {
182 /// Output path (relative to wiki root or absolute; default: llms.txt)
183 #[arg(long)]
184 path: Option<String>,
185 /// Export format: llms-txt | llms-full | json
186 #[arg(long)]
187 format: Option<String>,
188 /// Page status filter: active | all (default: active, excludes archived)
189 #[arg(long)]
190 status: Option<String>,
191 },
192 /// Start the wiki MCP/ACP server
193 Serve {
194 /// Enable HTTP transport (optional port, e.g. :8080)
195 #[arg(long, value_name = "PORT")]
196 http: Option<Option<String>>,
197 /// Enable ACP transport
198 #[arg(long)]
199 acp: bool,
200 /// Enable filesystem watcher
201 #[arg(long)]
202 watch: bool,
203 /// Print what would be started, no server
204 #[arg(long)]
205 dry_run: bool,
206 },
207 /// Auto-ingest on file save (standalone watcher)
208 Watch {
209 /// Target wiki name
210 #[arg(long)]
211 wiki: Option<String>,
212 },
213 /// Inspect and manage server logs
214 Logs {
215 /// The logs subcommand.
216 #[command(subcommand)]
217 action: LogsAction,
218 },
219}
220
221/// Subcommands for `llm-wiki logs`.
222#[derive(Subcommand)]
223pub enum LogsAction {
224 /// Show recent log entries
225 Tail {
226 /// Number of lines to show (default: 50)
227 #[arg(long, default_value = "50")]
228 lines: usize,
229 },
230 /// List log files
231 List,
232 /// Delete all log files
233 Clear,
234}
235
236/// Subcommands for `llm-wiki spaces`.
237#[derive(Subcommand)]
238pub enum SpacesAction {
239 /// Create a new wiki repository
240 Create {
241 /// Path to create the wiki at
242 path: String,
243 /// Wiki name — used in wiki:// URIs
244 #[arg(long)]
245 name: String,
246 /// Optional one-line description
247 #[arg(long)]
248 description: Option<String>,
249 /// Update space entry if name differs from existing
250 #[arg(long)]
251 force: bool,
252 /// Set as default wiki
253 #[arg(long)]
254 set_default: bool,
255 /// Content directory relative to repo root (default: "wiki")
256 #[arg(long)]
257 wiki_root: Option<String>,
258 },
259 /// Register an existing wiki repository without creating files
260 Register {
261 /// Absolute path to the existing wiki repository
262 path: String,
263 /// Wiki name — used in wiki:// URIs
264 #[arg(long)]
265 name: String,
266 /// Optional one-line description
267 #[arg(long)]
268 description: Option<String>,
269 /// Content directory relative to repo root (overrides wiki.toml)
270 #[arg(long)]
271 wiki_root: Option<String>,
272 },
273 /// List all registered wikis
274 List {
275 /// Wiki name (omit for all)
276 name: Option<String>,
277 /// Output format: text | json
278 #[arg(long)]
279 format: Option<String>,
280 },
281 /// Remove a wiki from the registry
282 Remove {
283 /// Wiki name to remove
284 name: String,
285 /// Also delete the wiki directory from disk
286 #[arg(long)]
287 delete: bool,
288 },
289 /// Set the default wiki
290 SetDefault {
291 /// Wiki name to set as default
292 name: String,
293 },
294}
295
296/// Subcommands for `llm-wiki config`.
297#[derive(Subcommand)]
298pub enum ConfigAction {
299 /// Print a config value
300 Get {
301 /// Config key (e.g. defaults.search_top_k)
302 key: String,
303 },
304 /// Set a config value
305 Set {
306 /// Config key
307 key: String,
308 /// Config value
309 value: String,
310 /// Write to global config
311 #[arg(long)]
312 global: bool,
313 /// Write to per-wiki config
314 #[arg(long)]
315 wiki: Option<String>,
316 },
317 /// Print all resolved config
318 List {
319 /// Global config only
320 #[arg(long)]
321 global: bool,
322 /// Per-wiki config only
323 #[arg(long)]
324 wiki: Option<String>,
325 /// Output format: text | json
326 #[arg(long)]
327 format: Option<String>,
328 },
329}
330
331/// Subcommands for `llm-wiki content`.
332#[derive(Subcommand)]
333pub enum ContentAction {
334 /// Read a page or asset by slug or wiki:// URI
335 Read {
336 /// Slug or wiki:// URI
337 uri: String,
338 /// Strip frontmatter from output
339 #[arg(long)]
340 no_frontmatter: bool,
341 /// List co-located assets instead of content
342 #[arg(long)]
343 list_assets: bool,
344 },
345 /// Write a file into the wiki tree
346 Write {
347 /// Slug or wiki:// URI
348 uri: String,
349 /// Read content from a file instead of stdin
350 #[arg(long)]
351 file: Option<String>,
352 },
353 /// Create a page or section with scaffolded frontmatter
354 New {
355 /// Slug or wiki:// URI
356 uri: String,
357 /// Create a section instead of a page
358 #[arg(long)]
359 section: bool,
360 /// Create as bundle (folder + index.md)
361 #[arg(long)]
362 bundle: bool,
363 /// Page title (default: derived from slug)
364 #[arg(long)]
365 name: Option<String>,
366 /// Page type (default: page)
367 #[arg(long, name = "type")]
368 r#type: Option<String>,
369 /// Show what would be created without creating
370 #[arg(long)]
371 dry_run: bool,
372 },
373 /// Commit pending changes to git
374 Commit {
375 /// Page slugs to commit (omit for --all)
376 slugs: Vec<String>,
377 /// Commit all pending changes
378 #[arg(long)]
379 all: bool,
380 /// Commit message
381 #[arg(long, short)]
382 message: Option<String>,
383 },
384}
385
386/// Subcommands for `llm-wiki index`.
387#[derive(Subcommand)]
388pub enum IndexAction {
389 /// Rebuild the search index from committed Markdown
390 Rebuild {
391 /// Walk and count pages, no write
392 #[arg(long)]
393 dry_run: bool,
394 /// Output format: text | json
395 #[arg(long)]
396 format: Option<String>,
397 },
398 /// Inspect index health
399 Status {
400 /// Output format: text | json
401 #[arg(long)]
402 format: Option<String>,
403 },
404}
405
406/// Subcommands for `llm-wiki schema`.
407#[derive(Subcommand)]
408pub enum SchemaAction {
409 /// List all registered types
410 List {
411 /// Output format: text | json
412 #[arg(long)]
413 format: Option<String>,
414 },
415 /// Show JSON Schema or frontmatter template for a type
416 Show {
417 /// Type name
418 name: String,
419 /// Print frontmatter template instead of schema
420 #[arg(long)]
421 template: bool,
422 /// Output format: text | json
423 #[arg(long)]
424 format: Option<String>,
425 },
426 /// Register a custom type
427 Add {
428 /// Type name
429 name: String,
430 /// Path to JSON Schema file
431 schema_path: String,
432 },
433 /// Unregister a type and remove its pages from the index
434 Remove {
435 /// Type name
436 name: String,
437 /// Also delete/modify the schema file
438 #[arg(long)]
439 delete: bool,
440 /// Also delete page .md files from disk
441 #[arg(long)]
442 delete_pages: bool,
443 /// Show what would be done without doing it
444 #[arg(long)]
445 dry_run: bool,
446 },
447 /// Validate schema files and index resolution
448 Validate {
449 /// Validate a specific type only (omit for all)
450 name: Option<String>,
451 },
452}