diff --git a/crates/kebab-cli/src/main.rs b/crates/kebab-cli/src/main.rs index 71f9f52..fca4756 100644 --- a/crates/kebab-cli/src/main.rs +++ b/crates/kebab-cli/src/main.rs @@ -177,6 +177,9 @@ enum Cmd { /// Health check. Doctor, + /// Print introspection report (wire schemas, capabilities, model versions, stats). + Schema, + /// Launch the Ratatui shell (P9-1 — Library pane only; search / /// ask / inspect panes land with p9-2 / p9-3 / p9-4). Tui, @@ -604,6 +607,18 @@ fn run(cli: &Cli) -> anyhow::Result<()> { Ok(()) } + Cmd::Schema => { + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let report = kebab_app::schema_with_config(&cfg)?; + if cli.json { + let v = wire::wire_schema(&report); + println!("{}", serde_json::to_string(&v)?); + } else { + print_schema_text(&report); + } + Ok(()) + } + Cmd::Doctor => { let report = kebab_app::doctor_with_config_path(cli.config.as_deref())?; if cli.json { @@ -720,6 +735,49 @@ fn run(cli: &Cli) -> anyhow::Result<()> { } } +fn print_schema_text(s: &kebab_app::SchemaV1) { + println!("kebab v{}\n", s.kebab_version); + + println!("wire schemas"); + println!(" {}", s.wire.schemas.join(", ")); + println!(); + + println!("capabilities"); + let caps = [ + ("json_mode", s.capabilities.json_mode), + ("ingest_progress", s.capabilities.ingest_progress), + ("ingest_cancellation", s.capabilities.ingest_cancellation), + ("rag_multi_turn", s.capabilities.rag_multi_turn), + ("search_cache", s.capabilities.search_cache), + ("incremental_ingest", s.capabilities.incremental_ingest), + ("streaming_ask", s.capabilities.streaming_ask), + ("http_daemon", s.capabilities.http_daemon), + ("mcp_server", s.capabilities.mcp_server), + ("single_file_ingest", s.capabilities.single_file_ingest), + ]; + for (name, on) in caps { + let mark = if on { "✓" } else { "✗" }; + println!(" {mark} {name}"); + } + println!(); + + println!("models"); + println!(" parser_version {}", s.models.parser_version); + println!(" chunker_version {}", s.models.chunker_version); + println!(" embedding_version {}", s.models.embedding_version); + println!(" prompt_template_version {}", s.models.prompt_template_version); + println!(" index_version {}", s.models.index_version); + println!(" corpus_revision {}", s.models.corpus_revision); + println!(); + + println!("stats"); + println!(" doc_count {}", s.stats.doc_count); + println!(" chunk_count {}", s.stats.chunk_count); + println!(" asset_count {}", s.stats.asset_count); + let last = s.stats.last_ingest_at.as_deref().unwrap_or("(never)"); + println!(" last_ingest_at {last}"); +} + /// Minimal stdin/stdout confirm prompt for destructive ops. No new dep — /// uses stdlib `IsTerminal` (the caller is expected to have already /// short-circuited the non-TTY case). Returns `Ok(true)` only when the