diff --git a/Cargo.lock b/Cargo.lock index 090a277..43199b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3366,27 +3366,27 @@ dependencies = [ ] [[package]] -name = "kb-app" +name = "kebab-app" version = "0.1.0" dependencies = [ "anyhow", "blake3", "dirs 5.0.1", - "kb-chunk", - "kb-config", - "kb-core", - "kb-embed", - "kb-embed-local", - "kb-llm", - "kb-llm-local", - "kb-normalize", - "kb-parse-md", - "kb-parse-types", - "kb-rag", - "kb-search", - "kb-source-fs", - "kb-store-sqlite", - "kb-store-vector", + "kebab-chunk", + "kebab-config", + "kebab-core", + "kebab-embed", + "kebab-embed-local", + "kebab-llm", + "kebab-llm-local", + "kebab-normalize", + "kebab-parse-md", + "kebab-parse-types", + "kebab-rag", + "kebab-search", + "kebab-source-fs", + "kebab-store-sqlite", + "kebab-store-vector", "rusqlite", "serde", "serde_json", @@ -3399,14 +3399,14 @@ dependencies = [ ] [[package]] -name = "kb-chunk" +name = "kebab-chunk" version = "0.1.0" dependencies = [ "anyhow", "blake3", - "kb-core", - "kb-normalize", - "kb-parse-md", + "kebab-core", + "kebab-normalize", + "kebab-parse-md", "serde_json", "serde_json_canonicalizer", "time", @@ -3414,32 +3414,32 @@ dependencies = [ ] [[package]] -name = "kb-cli" +name = "kebab-cli" version = "0.1.0" dependencies = [ "anyhow", "clap", - "kb-app", - "kb-config", - "kb-core", - "kb-eval", + "kebab-app", + "kebab-config", + "kebab-core", + "kebab-eval", "serde_json", ] [[package]] -name = "kb-config" +name = "kebab-config" version = "0.1.0" dependencies = [ "anyhow", "dirs 5.0.1", - "kb-core", + "kebab-core", "serde", "serde_json", "toml", ] [[package]] -name = "kb-core" +name = "kebab-core" version = "0.1.0" dependencies = [ "anyhow", @@ -3453,13 +3453,13 @@ dependencies = [ ] [[package]] -name = "kb-embed" +name = "kebab-embed" version = "0.1.0" dependencies = [ "anyhow", "blake3", - "kb-config", - "kb-core", + "kebab-config", + "kebab-core", "proptest", "serde", "thiserror 2.0.18", @@ -3467,27 +3467,27 @@ dependencies = [ ] [[package]] -name = "kb-embed-local" +name = "kebab-embed-local" version = "0.1.0" dependencies = [ "anyhow", "fastembed", - "kb-config", - "kb-embed", + "kebab-config", + "kebab-embed", "serde_json", "tempfile", "tracing", ] [[package]] -name = "kb-eval" +name = "kebab-eval" version = "0.1.0" dependencies = [ "anyhow", - "kb-app", - "kb-config", - "kb-core", - "kb-store-sqlite", + "kebab-app", + "kebab-config", + "kebab-core", + "kebab-store-sqlite", "rusqlite", "serde", "serde_json", @@ -3499,22 +3499,22 @@ dependencies = [ ] [[package]] -name = "kb-llm" +name = "kebab-llm" version = "0.1.0" dependencies = [ "anyhow", - "kb-core", + "kebab-core", "proptest", ] [[package]] -name = "kb-llm-local" +name = "kebab-llm-local" version = "0.1.0" dependencies = [ "anyhow", - "kb-config", - "kb-core", - "kb-llm", + "kebab-config", + "kebab-core", + "kebab-llm", "reqwest", "serde", "serde_json", @@ -3525,13 +3525,13 @@ dependencies = [ ] [[package]] -name = "kb-normalize" +name = "kebab-normalize" version = "0.1.0" dependencies = [ "anyhow", - "kb-core", - "kb-parse-md", - "kb-parse-types", + "kebab-core", + "kebab-parse-md", + "kebab-parse-types", "serde", "serde_json", "time", @@ -3540,12 +3540,12 @@ dependencies = [ ] [[package]] -name = "kb-parse-md" +name = "kebab-parse-md" version = "0.1.0" dependencies = [ "anyhow", - "kb-core", - "kb-parse-types", + "kebab-core", + "kebab-parse-types", "lingua", "pulldown-cmark", "serde", @@ -3557,24 +3557,24 @@ dependencies = [ ] [[package]] -name = "kb-parse-types" +name = "kebab-parse-types" version = "0.1.0" dependencies = [ - "kb-core", + "kebab-core", "serde", ] [[package]] -name = "kb-rag" +name = "kebab-rag" version = "0.1.0" dependencies = [ "anyhow", "blake3", - "kb-config", - "kb-core", - "kb-llm", - "kb-search", - "kb-store-sqlite", + "kebab-config", + "kebab-core", + "kebab-llm", + "kebab-search", + "kebab-store-sqlite", "regex", "rusqlite", "serde", @@ -3586,16 +3586,16 @@ dependencies = [ ] [[package]] -name = "kb-search" +name = "kebab-search" version = "0.1.0" dependencies = [ "anyhow", "globset", - "kb-config", - "kb-core", - "kb-embed", - "kb-store-sqlite", - "kb-store-vector", + "kebab-config", + "kebab-core", + "kebab-embed", + "kebab-store-sqlite", + "kebab-store-vector", "rusqlite", "serde_json", "tempfile", @@ -3604,14 +3604,14 @@ dependencies = [ ] [[package]] -name = "kb-source-fs" +name = "kebab-source-fs" version = "0.1.0" dependencies = [ "anyhow", "blake3", "ignore", - "kb-config", - "kb-core", + "kebab-config", + "kebab-core", "serde", "serde_json", "tempfile", @@ -3621,17 +3621,17 @@ dependencies = [ ] [[package]] -name = "kb-store-sqlite" +name = "kebab-store-sqlite" version = "0.1.0" dependencies = [ "anyhow", "blake3", "globset", - "kb-chunk", - "kb-config", - "kb-core", - "kb-normalize", - "kb-parse-md", + "kebab-chunk", + "kebab-config", + "kebab-core", + "kebab-normalize", + "kebab-parse-md", "refinery", "rusqlite", "serde_json", @@ -3642,7 +3642,7 @@ dependencies = [ ] [[package]] -name = "kb-store-vector" +name = "kebab-store-vector" version = "0.1.0" dependencies = [ "anyhow", @@ -3651,9 +3651,9 @@ dependencies = [ "arrow-schema", "blake3", "futures", - "kb-config", - "kb-core", - "kb-store-sqlite", + "kebab-config", + "kebab-core", + "kebab-store-sqlite", "lancedb", "rusqlite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 91a6252..b2ece6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,31 +1,31 @@ [workspace] resolver = "3" members = [ - "crates/kb-core", - "crates/kb-parse-types", - "crates/kb-config", - "crates/kb-source-fs", - "crates/kb-parse-md", - "crates/kb-normalize", - "crates/kb-chunk", - "crates/kb-store-sqlite", - "crates/kb-store-vector", - "crates/kb-search", - "crates/kb-embed", - "crates/kb-embed-local", - "crates/kb-llm", - "crates/kb-llm-local", - "crates/kb-rag", - "crates/kb-app", - "crates/kb-cli", - "crates/kb-eval", + "crates/kebab-core", + "crates/kebab-parse-types", + "crates/kebab-config", + "crates/kebab-source-fs", + "crates/kebab-parse-md", + "crates/kebab-normalize", + "crates/kebab-chunk", + "crates/kebab-store-sqlite", + "crates/kebab-store-vector", + "crates/kebab-search", + "crates/kebab-embed", + "crates/kebab-embed-local", + "crates/kebab-llm", + "crates/kebab-llm-local", + "crates/kebab-rag", + "crates/kebab-app", + "crates/kebab-cli", + "crates/kebab-eval", ] [workspace.package] edition = "2024" rust-version = "1.85" license = "MIT OR Apache-2.0" -repository = "https://github.com/altair823/kb" +repository = "https://github.com/altair823/kebab" version = "0.1.0" [workspace.dependencies] diff --git a/crates/kb-app/Cargo.toml b/crates/kebab-app/Cargo.toml similarity index 51% rename from crates/kb-app/Cargo.toml rename to crates/kebab-app/Cargo.toml index 9fb7dcd..e8fae60 100644 --- a/crates/kb-app/Cargo.toml +++ b/crates/kebab-app/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-app" +name = "kebab-app" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,21 +8,21 @@ repository = { workspace = true } description = "Facade — orchestrates components for kb-cli/tui/desktop" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-source-fs = { path = "../kb-source-fs" } -kb-parse-md = { path = "../kb-parse-md" } -kb-parse-types = { path = "../kb-parse-types" } -kb-normalize = { path = "../kb-normalize" } -kb-chunk = { path = "../kb-chunk" } -kb-store-sqlite = { path = "../kb-store-sqlite" } -kb-store-vector = { path = "../kb-store-vector" } -kb-search = { path = "../kb-search" } -kb-embed = { path = "../kb-embed" } -kb-embed-local = { path = "../kb-embed-local" } -kb-llm = { path = "../kb-llm" } -kb-llm-local = { path = "../kb-llm-local" } -kb-rag = { path = "../kb-rag" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-source-fs = { path = "../kebab-source-fs" } +kebab-parse-md = { path = "../kebab-parse-md" } +kebab-parse-types = { path = "../kebab-parse-types" } +kebab-normalize = { path = "../kebab-normalize" } +kebab-chunk = { path = "../kebab-chunk" } +kebab-store-sqlite = { path = "../kebab-store-sqlite" } +kebab-store-vector = { path = "../kebab-store-vector" } +kebab-search = { path = "../kebab-search" } +kebab-embed = { path = "../kebab-embed" } +kebab-embed-local = { path = "../kebab-embed-local" } +kebab-llm = { path = "../kebab-llm" } +kebab-llm-local = { path = "../kebab-llm-local" } +kebab-rag = { path = "../kebab-rag" } anyhow = { workspace = true } blake3 = { workspace = true } serde = { workspace = true } diff --git a/crates/kb-app/src/app.rs b/crates/kebab-app/src/app.rs similarity index 96% rename from crates/kb-app/src/app.rs rename to crates/kebab-app/src/app.rs index 8156b3e..4080a15 100644 --- a/crates/kb-app/src/app.rs +++ b/crates/kebab-app/src/app.rs @@ -37,16 +37,16 @@ use std::sync::{Arc, OnceLock}; use anyhow::{Context, Result, anyhow}; -use kb_core::{ +use kebab_core::{ Answer, Embedder, IndexVersion, LanguageModel, Retriever, SearchHit, SearchMode, SearchQuery, VectorStore, }; -use kb_embed_local::FastembedEmbedder; -use kb_llm_local::OllamaLanguageModel; -use kb_rag::{AskOpts, RagPipeline}; -use kb_search::{HybridRetriever, LexicalRetriever, VectorRetriever}; -use kb_store_sqlite::SqliteStore; -use kb_store_vector::LanceVectorStore; +use kebab_embed_local::FastembedEmbedder; +use kebab_llm_local::OllamaLanguageModel; +use kebab_rag::{AskOpts, RagPipeline}; +use kebab_search::{HybridRetriever, LexicalRetriever, VectorRetriever}; +use kebab_store_sqlite::SqliteStore; +use kebab_store_vector::LanceVectorStore; /// Facade state — see module docs for lifetime rules. /// @@ -55,7 +55,7 @@ use kb_store_vector::LanceVectorStore; /// ask calls. The OnceLock-backed `embedder` / `vector` fields ensure /// the cold-start cost is paid exactly once per instance. pub struct App { - pub(crate) config: kb_config::Config, + pub(crate) config: kebab_config::Config, pub(crate) sqlite: Arc, /// Memoized embedder — built lazily on first `embedder()` call when /// embeddings are enabled. `OnceLock` keeps the struct `Sync` and @@ -80,7 +80,7 @@ impl App { /// Downstream `LanceVectorStore::new` (called by [`Self::vector`]) /// internally drives a `tokio::Runtime::block_on`, which panics if /// invoked from inside another tokio runtime. - pub fn open_with_config(config: kb_config::Config) -> Result { + pub fn open_with_config(config: kebab_config::Config) -> Result { let sqlite = SqliteStore::open(&config).context("kb-app: open SqliteStore")?; sqlite .run_migrations() @@ -286,7 +286,7 @@ impl App { /// the active config. This token surfaces in `SearchHit.index_version` /// and on snapshot tests; including the chunker version pins it to /// the chunking policy in effect. -fn lexical_index_version(config: &kb_config::Config) -> IndexVersion { +fn lexical_index_version(config: &kebab_config::Config) -> IndexVersion { IndexVersion(format!("lex:{}", config.chunking.chunker_version)) } diff --git a/crates/kb-app/src/doctor_signal.rs b/crates/kebab-app/src/doctor_signal.rs similarity index 100% rename from crates/kb-app/src/doctor_signal.rs rename to crates/kebab-app/src/doctor_signal.rs diff --git a/crates/kb-app/src/lib.rs b/crates/kebab-app/src/lib.rs similarity index 90% rename from crates/kb-app/src/lib.rs rename to crates/kebab-app/src/lib.rs index aefc030..f5e6727 100644 --- a/crates/kb-app/src/lib.rs +++ b/crates/kebab-app/src/lib.rs @@ -23,7 +23,7 @@ //! ## Config seam (`*_with_config`) //! //! Each public free function has a `#[doc(hidden)] pub fn *_with_config` -//! companion that takes a fully-resolved [`kb_config::Config`] directly. +//! companion that takes a fully-resolved [`kebab_config::Config`] directly. //! Three callers go through it: (1) the top-level free functions //! themselves, after `load_config()`; (2) `kb-cli` when the user passes //! `--config ` (CLI builds the Config via @@ -39,16 +39,16 @@ use std::sync::Arc; use anyhow::{Context, anyhow}; use serde::{Deserialize, Serialize}; -use kb_chunk::MdHeadingV1Chunker; -use kb_core::{ +use kebab_chunk::MdHeadingV1Chunker; +use kebab_core::{ Answer, CanonicalDocument, Chunk, ChunkId, ChunkPolicy, ChunkerVersion, Chunker, DocFilter, DocSummary, DocumentId, DocumentStore, Embedder, EmbeddingInput, EmbeddingKind, IngestReport, ParserVersion, RawAsset, SearchHit, SearchQuery, SourceConnector, SourceScope, SourceUri, VectorRecord, VectorStore, }; -use kb_normalize::build_canonical_document; -use kb_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; -use kb_source_fs::FsSourceConnector; +use kebab_normalize::build_canonical_document; +use kebab_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; +use kebab_source_fs::FsSourceConnector; mod app; pub mod doctor_signal; @@ -65,11 +65,11 @@ const KB_PARSE_MD_VERSION: &str = "pulldown-cmark-0.x"; /// Caller-supplied knobs for one [`ask`] invocation. /// -/// Re-exported from [`kb_rag::AskOpts`] (P4-3 owns the type) so kb-cli's -/// `use kb_app::AskOpts` keeps working without churn. The struct gained +/// Re-exported from [`kebab_rag::AskOpts`] (P4-3 owns the type) so kb-cli's +/// `use kebab_app::AskOpts` keeps working without churn. The struct gained /// a `stream_sink` field in P4-3; non-streaming callers (kb-cli today) /// pass `stream_sink: None`. -pub use kb_rag::AskOpts; +pub use kebab_rag::AskOpts; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct DoctorReport { @@ -90,10 +90,10 @@ pub struct DoctorCheck { /// Create XDG dirs and write a starter `config.toml`. Idempotent unless /// `force=true` (which overwrites an existing config). pub fn init_workspace(force: bool) -> anyhow::Result<()> { - let cfg_path = kb_config::Config::xdg_config_path(); - let data_dir = kb_config::Config::xdg_data_dir(); - let cache_dir = kb_config::Config::xdg_cache_dir(); - let state_dir = kb_config::Config::xdg_state_dir(); + let cfg_path = kebab_config::Config::xdg_config_path(); + let data_dir = kebab_config::Config::xdg_data_dir(); + let cache_dir = kebab_config::Config::xdg_cache_dir(); + let state_dir = kebab_config::Config::xdg_state_dir(); for d in [ cfg_path.parent().map(PathBuf::from).unwrap_or_default(), @@ -107,11 +107,11 @@ pub fn init_workspace(force: bool) -> anyhow::Result<()> { } } - let workspace_root = expand_tilde(&kb_config::Config::defaults().workspace.root); + let workspace_root = expand_tilde(&kebab_config::Config::defaults().workspace.root); std::fs::create_dir_all(&workspace_root)?; if !cfg_path.exists() || force { - let cfg = kb_config::Config::defaults(); + let cfg = kebab_config::Config::defaults(); let toml_text = toml::to_string_pretty(&cfg)?; std::fs::write(&cfg_path, toml_text)?; } @@ -141,8 +141,8 @@ fn expand_tilde(s: &str) -> PathBuf { /// Callers that already have a Config in hand (CLI honoring `--config`, /// integration tests, TUI session) should bypass this and call the /// matching `*_with_config` helper directly. -fn load_config() -> anyhow::Result { - kb_config::Config::load(None) +fn load_config() -> anyhow::Result { + kebab_config::Config::load(None) } // ── ingest ──────────────────────────────────────────────────────────────── @@ -154,11 +154,11 @@ pub fn ingest(scope: SourceScope, summary_only: bool) -> anyhow::Result anyhow::Result { @@ -205,13 +205,13 @@ pub fn ingest_with_config( let started_at = time::OffsetDateTime::now_utc(); - let mut items: Vec = Vec::new(); + let mut items: Vec = Vec::new(); let mut new_count: u32 = 0; let mut updated_count: u32 = 0; let mut skipped_count: u32 = 0; let mut error_count: u32 = 0; // Aggregate counts surfaced into `ingest_runs` (and tracing). Not - // exposed on `IngestReport` today — `kb_core::IngestReport` is a + // exposed on `IngestReport` today — `kebab_core::IngestReport` is a // wire-stable struct without these fields — but persisting them // means audit tooling and `kb jobs` (P+) can recover the totals // without re-walking the DB. @@ -242,8 +242,8 @@ pub fn ingest_with_config( "kb-app::ingest: per-file fatal" ); error_count = error_count.saturating_add(1); - kb_core::IngestItem { - kind: kb_core::IngestItemKind::Error, + kebab_core::IngestItem { + kind: kebab_core::IngestItemKind::Error, doc_id: None, doc_path: asset.workspace_path.clone(), asset_id: Some(asset.asset_id.clone()), @@ -259,7 +259,7 @@ pub fn ingest_with_config( }; match item.kind { - kb_core::IngestItemKind::New => { + kebab_core::IngestItemKind::New => { new_count = new_count.saturating_add(1); let n = item.chunk_count.unwrap_or(0); chunks_indexed = chunks_indexed.saturating_add(n); @@ -267,7 +267,7 @@ pub fn ingest_with_config( embeddings_indexed = embeddings_indexed.saturating_add(n); } } - kb_core::IngestItemKind::Updated => { + kebab_core::IngestItemKind::Updated => { updated_count = updated_count.saturating_add(1); let n = item.chunk_count.unwrap_or(0); chunks_indexed = chunks_indexed.saturating_add(n); @@ -275,10 +275,10 @@ pub fn ingest_with_config( embeddings_indexed = embeddings_indexed.saturating_add(n); } } - kb_core::IngestItemKind::Skipped => { + kebab_core::IngestItemKind::Skipped => { skipped_count = skipped_count.saturating_add(1) } - kb_core::IngestItemKind::Error => { + kebab_core::IngestItemKind::Error => { error_count = error_count.saturating_add(1) } } @@ -293,9 +293,9 @@ pub fn ingest_with_config( "scope": scope, "summary_only": summary_only, }); - let job_id_res = ::create( + let job_id_res = ::create( &app.sqlite, - kb_core::JobKind::Ingest, + kebab_core::JobKind::Ingest, payload, ); match job_id_res { @@ -312,7 +312,7 @@ pub fn ingest_with_config( "chunks_indexed": chunks_indexed, "embeddings_indexed": embeddings_indexed, }); - if let Err(e) = ::update_progress( + if let Err(e) = ::update_progress( &app.sqlite, &jid, progress, @@ -323,10 +323,10 @@ pub fn ingest_with_config( "kb-app::ingest: JobRepo::update_progress failed" ); } - if let Err(e) = ::finish( + if let Err(e) = ::finish( &app.sqlite, &jid, - kb_core::JobStatus::Succeeded, + kebab_core::JobStatus::Succeeded, None, ) { tracing::warn!( @@ -370,7 +370,7 @@ pub fn ingest_with_config( } }; let run_id = mint_ingest_run_id(&scope_json, started_at); - let row = kb_store_sqlite::IngestRunRow { + let row = kebab_store_sqlite::IngestRunRow { run_id: &run_id, scope_json: &scope_json, scanned: scanned_count, @@ -432,7 +432,7 @@ fn mint_ingest_run_id(scope_json: &str, at: time::OffsetDateTime) -> String { /// vs `JobRepo`) on the same store. Plain `app.sqlite.create(...)` /// would pick one based on inherent vs trait methods; we go through /// `<… as JobRepo>` to be explicit. -type SqliteStoreAlias = kb_store_sqlite::SqliteStore; +type SqliteStoreAlias = kebab_store_sqlite::SqliteStore; /// Process a single asset: read bytes, parse, normalize, chunk, /// persist, embed. Per-asset failures bubble up to the caller for @@ -444,18 +444,18 @@ fn ingest_one_asset( parser_version: &ParserVersion, chunk_policy: &ChunkPolicy, embedder: Option<&Arc>, - vector_store: Option<&Arc>, + vector_store: Option<&Arc>, existing_doc_ids: &std::collections::HashSet, -) -> anyhow::Result { +) -> anyhow::Result { tracing::debug!( target: "kb-app::ingest", path = %asset.workspace_path.0, "processing asset" ); // Only handle Markdown for now; other media types are P6+ work. - if asset.media_type != kb_core::MediaType::Markdown { - return Ok(kb_core::IngestItem { - kind: kb_core::IngestItemKind::Skipped, + if asset.media_type != kebab_core::MediaType::Markdown { + return Ok(kebab_core::IngestItem { + kind: kebab_core::IngestItemKind::Skipped, doc_id: None, doc_path: asset.workspace_path.clone(), asset_id: Some(asset.asset_id.clone()), @@ -472,8 +472,8 @@ fn ingest_one_asset( let path = match &asset.source_uri { SourceUri::File(p) => p.clone(), SourceUri::Kb(_) => { - return Ok(kb_core::IngestItem { - kind: kb_core::IngestItemKind::Skipped, + return Ok(kebab_core::IngestItem { + kind: kebab_core::IngestItemKind::Skipped, doc_id: None, doc_path: asset.workspace_path.clone(), asset_id: Some(asset.asset_id.clone()), @@ -569,7 +569,7 @@ fn ingest_one_asset( .iter() .zip(vectors) .map(|(c, v)| VectorRecord { - embedding_id: kb_core::id_for_embedding( + embedding_id: kebab_core::id_for_embedding( &c.chunk_id, &model_id, &model_version, @@ -592,12 +592,12 @@ fn ingest_one_asset( } let kind = if existing_doc_ids.contains(&canonical.doc_id.0) { - kb_core::IngestItemKind::Updated + kebab_core::IngestItemKind::Updated } else { - kb_core::IngestItemKind::New + kebab_core::IngestItemKind::New }; - Ok(kb_core::IngestItem { + Ok(kebab_core::IngestItem { kind, doc_id: Some(canonical.doc_id.clone()), doc_path: asset.workspace_path.clone(), @@ -613,7 +613,7 @@ fn ingest_one_asset( } /// Convenience: end byte of the frontmatter region (or 0 when absent). -fn fm_span_end(span: Option) -> usize { +fn fm_span_end(span: Option) -> usize { span.map(|s| s.end).unwrap_or(0) } @@ -640,7 +640,7 @@ fn build_body_hints(asset: &RawAsset) -> BodyHints { } /// Build a `ChunkPolicy` from the active config. -fn chunk_policy_from_config(config: &kb_config::Config) -> ChunkPolicy { +fn chunk_policy_from_config(config: &kebab_config::Config) -> ChunkPolicy { ChunkPolicy { target_tokens: config.chunking.target_tokens, overlap_tokens: config.chunking.overlap_tokens, @@ -660,7 +660,7 @@ pub fn list_docs(filter: DocFilter) -> anyhow::Result> { /// ([`list_docs`]), not this. #[doc(hidden)] pub fn list_docs_with_config( - config: kb_config::Config, + config: kebab_config::Config, filter: DocFilter, ) -> anyhow::Result> { let app = App::open_with_config(config)?; @@ -676,7 +676,7 @@ pub fn inspect_doc(id: &DocumentId) -> anyhow::Result { /// ([`inspect_doc`]), not this. #[doc(hidden)] pub fn inspect_doc_with_config( - config: kb_config::Config, + config: kebab_config::Config, id: &DocumentId, ) -> anyhow::Result { let app = App::open_with_config(config)?; @@ -694,7 +694,7 @@ pub fn inspect_chunk(id: &ChunkId) -> anyhow::Result { /// ([`inspect_chunk`]), not this. #[doc(hidden)] pub fn inspect_chunk_with_config( - config: kb_config::Config, + config: kebab_config::Config, id: &ChunkId, ) -> anyhow::Result { let app = App::open_with_config(config)?; @@ -716,7 +716,7 @@ pub fn search(query: SearchQuery) -> anyhow::Result> { /// directly to amortize the embedder / vector-store cold start. #[doc(hidden)] pub fn search_with_config( - config: kb_config::Config, + config: kebab_config::Config, query: SearchQuery, ) -> anyhow::Result> { App::open_with_config(config)?.search(query) @@ -740,7 +740,7 @@ pub fn ask(query: &str, opts: AskOpts) -> anyhow::Result { /// [`App::ask`]. #[doc(hidden)] pub fn ask_with_config( - config: kb_config::Config, + config: kebab_config::Config, query: &str, opts: AskOpts, ) -> anyhow::Result { @@ -761,10 +761,10 @@ pub fn doctor_with_config_path(config_path: Option<&std::path::Path>) -> anyhow: // override first, else XDG default. Report whichever was probed. let cfg_path: PathBuf = match config_path { Some(p) => p.to_path_buf(), - None => kb_config::Config::xdg_config_path(), + None => kebab_config::Config::xdg_config_path(), }; let (config_ok, config_detail, loaded_cfg) = if cfg_path.exists() { - match kb_config::Config::from_file(&cfg_path) { + match kebab_config::Config::from_file(&cfg_path) { Ok(c) => (true, cfg_path.display().to_string(), Some(c)), Err(e) => (false, format!("{} ({e})", cfg_path.display()), None), } @@ -804,7 +804,7 @@ pub fn doctor_with_config_path(config_path: Option<&std::path::Path>) -> anyhow: let merged = c.clone().apply_env(&env); expand_tilde(&merged.storage.data_dir) } - None => kb_config::Config::xdg_data_dir(), + None => kebab_config::Config::xdg_data_dir(), }; let writable = (|| -> anyhow::Result<()> { std::fs::create_dir_all(&data_dir)?; diff --git a/crates/kb-app/src/logging.rs b/crates/kebab-app/src/logging.rs similarity index 95% rename from crates/kb-app/src/logging.rs rename to crates/kebab-app/src/logging.rs index 30865fc..a78c4c9 100644 --- a/crates/kb-app/src/logging.rs +++ b/crates/kebab-app/src/logging.rs @@ -19,7 +19,7 @@ pub enum LogLevel { /// — a second call is a no-op (the second `try_init` is dropped silently /// but the guard is still returned so the caller can keep it alive). pub fn init(level: LogLevel) -> Result { - let log_dir = kb_config::Config::xdg_state_dir().join("logs"); + let log_dir = kebab_config::Config::xdg_state_dir().join("logs"); std::fs::create_dir_all(&log_dir)?; let file_appender = tracing_appender::rolling::daily(&log_dir, "kb.log"); diff --git a/crates/kb-app/tests/ask_smoke.rs b/crates/kebab-app/tests/ask_smoke.rs similarity index 82% rename from crates/kb-app/tests/ask_smoke.rs rename to crates/kebab-app/tests/ask_smoke.rs index 0e47db1..8df4f3f 100644 --- a/crates/kb-app/tests/ask_smoke.rs +++ b/crates/kebab-app/tests/ask_smoke.rs @@ -21,12 +21,12 @@ use common::TestEnv; #[ignore = "requires real Ollama on 127.0.0.1:11434"] fn ask_lexical_smoke() { let env = TestEnv::lexical_only(); - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); - let opts = kb_app::AskOpts { + let opts = kebab_app::AskOpts { k: 5, explain: false, - mode: kb_core::SearchMode::Lexical, + mode: kebab_core::SearchMode::Lexical, temperature: Some(0.0), seed: Some(0), stream_sink: None, @@ -34,10 +34,10 @@ fn ask_lexical_smoke() { // The fixture workspace contains "ownership" content; the model's // citation behavior depends on its training, so we don't assert on // grounded — only that the call returns a structurally-valid Answer. - let answer = kb_app::ask_with_config(env.config.clone(), "ownership", opts) + let answer = kebab_app::ask_with_config(env.config.clone(), "ownership", opts) .expect("ask returns Ok with a real Ollama backend"); // retrieval summary always populated, regardless of grounded path. - assert_eq!(answer.retrieval.mode, kb_core::SearchMode::Lexical); + assert_eq!(answer.retrieval.mode, kebab_core::SearchMode::Lexical); assert!(answer.retrieval.k >= 5); assert!(answer.retrieval.trace_id.0.starts_with("ret_")); } diff --git a/crates/kb-app/tests/common/mod.rs b/crates/kebab-app/tests/common/mod.rs similarity index 96% rename from crates/kb-app/tests/common/mod.rs rename to crates/kebab-app/tests/common/mod.rs index 59b04ce..cded98f 100644 --- a/crates/kb-app/tests/common/mod.rs +++ b/crates/kebab-app/tests/common/mod.rs @@ -12,7 +12,7 @@ use std::path::{Path, PathBuf}; -use kb_config::Config; +use kebab_config::Config; use tempfile::TempDir; /// Test environment: owns a `TempDir` and exposes a `Config` whose @@ -72,8 +72,8 @@ impl TestEnv { } } - pub fn scope(&self) -> kb_core::SourceScope { - kb_core::SourceScope { + pub fn scope(&self) -> kebab_core::SourceScope { + kebab_core::SourceScope { root: self.workspace_root.clone(), include: self.config.workspace.include.clone(), exclude: self.config.workspace.exclude.clone(), diff --git a/crates/kb-app/tests/fixtures/workspace/intro.md b/crates/kebab-app/tests/fixtures/workspace/intro.md similarity index 100% rename from crates/kb-app/tests/fixtures/workspace/intro.md rename to crates/kebab-app/tests/fixtures/workspace/intro.md diff --git a/crates/kb-app/tests/fixtures/workspace/notes/cargo.md b/crates/kebab-app/tests/fixtures/workspace/notes/cargo.md similarity index 100% rename from crates/kb-app/tests/fixtures/workspace/notes/cargo.md rename to crates/kebab-app/tests/fixtures/workspace/notes/cargo.md diff --git a/crates/kb-app/tests/fixtures/workspace/notes/python.md b/crates/kebab-app/tests/fixtures/workspace/notes/python.md similarity index 100% rename from crates/kb-app/tests/fixtures/workspace/notes/python.md rename to crates/kebab-app/tests/fixtures/workspace/notes/python.md diff --git a/crates/kb-app/tests/ingest_lexical.rs b/crates/kebab-app/tests/ingest_lexical.rs similarity index 81% rename from crates/kb-app/tests/ingest_lexical.rs rename to crates/kebab-app/tests/ingest_lexical.rs index 344dee9..f3ae13f 100644 --- a/crates/kb-app/tests/ingest_lexical.rs +++ b/crates/kebab-app/tests/ingest_lexical.rs @@ -9,7 +9,7 @@ use common::TestEnv; fn ingest_then_list_inspects_round_trip() { let env = TestEnv::lexical_only(); let report = - kb_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); // The fixture has 3 markdown files; first ingest should label them // all as New. @@ -27,16 +27,16 @@ fn ingest_then_list_inspects_round_trip() { } // list_docs returns the 3 docs. - let docs = kb_app::list_docs_with_config( + let docs = kebab_app::list_docs_with_config( env.config.clone(), - kb_core::DocFilter::default(), + kebab_core::DocFilter::default(), ) .unwrap(); assert_eq!(docs.len(), 3, "docs: {docs:?}"); // inspect_doc round-trips one of them. let any_doc_id = docs[0].doc_id.clone(); - let canonical = kb_app::inspect_doc_with_config(env.config.clone(), &any_doc_id) + let canonical = kebab_app::inspect_doc_with_config(env.config.clone(), &any_doc_id) .unwrap(); assert_eq!(canonical.doc_id, any_doc_id); assert!(!canonical.blocks.is_empty(), "blocks empty"); @@ -47,20 +47,20 @@ fn ingest_idempotent_on_second_run() { let env = TestEnv::lexical_only(); let r1 = - kb_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); assert_eq!(r1.new, 3); let r2 = - kb_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); // Same files re-ingested — labelled Updated, not duplicated. assert_eq!(r2.scanned, 3, "second scan: {r2:?}"); assert_eq!(r2.new, 0, "second run new should be 0: {r2:?}"); assert_eq!(r2.updated, 3, "second run updated: {r2:?}"); // list_docs still has 3 docs (no duplicates). - let docs = kb_app::list_docs_with_config( + let docs = kebab_app::list_docs_with_config( env.config.clone(), - kb_core::DocFilter::default(), + kebab_core::DocFilter::default(), ) .unwrap(); assert_eq!(docs.len(), 3); @@ -70,7 +70,7 @@ fn ingest_idempotent_on_second_run() { fn ingest_summary_only_drops_items() { let env = TestEnv::lexical_only(); let report = - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); assert_eq!(report.scanned, 3); assert!(report.items.is_none(), "summary-only should null items"); } @@ -82,7 +82,7 @@ fn ingest_records_ingest_runs_row_with_aggregate_counts() { // of every run. `summary_only=true` writes `items_json=NULL`; the // counts MUST still be present. let env = TestEnv::lexical_only(); - let report = kb_app::ingest_with_config(env.config.clone(), env.scope(), true) + let report = kebab_app::ingest_with_config(env.config.clone(), env.scope(), true) .unwrap(); assert_eq!(report.scanned, 3); @@ -137,7 +137,7 @@ fn ingest_provider_none_skips_lance() { // tables under it). let env = TestEnv::lexical_only(); let report = - kb_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), false).unwrap(); assert_eq!(report.errors, 0, "lexical-only run must not error"); assert_eq!(report.new, 3); @@ -170,22 +170,22 @@ fn ingest_provider_none_skips_lance() { #[test] fn list_docs_filters_by_tags_any() { let env = TestEnv::lexical_only(); - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); - let filter = kb_core::DocFilter { + let filter = kebab_core::DocFilter { tags_any: vec!["python".to_string()], ..Default::default() }; - let docs = kb_app::list_docs_with_config(env.config.clone(), filter).unwrap(); + let docs = kebab_app::list_docs_with_config(env.config.clone(), filter).unwrap(); assert_eq!(docs.len(), 1, "expected only the python doc: {docs:?}"); assert!(docs[0].tags.contains(&"python".to_string())); - let rust_filter = kb_core::DocFilter { + let rust_filter = kebab_core::DocFilter { tags_any: vec!["rust".to_string()], ..Default::default() }; let rust_docs = - kb_app::list_docs_with_config(env.config.clone(), rust_filter).unwrap(); + kebab_app::list_docs_with_config(env.config.clone(), rust_filter).unwrap(); // intro.md and notes/cargo.md both tag "rust". assert_eq!(rust_docs.len(), 2, "expected 2 rust docs: {rust_docs:?}"); } @@ -194,8 +194,8 @@ fn list_docs_filters_by_tags_any() { fn inspect_doc_not_found_returns_actionable_error() { let env = TestEnv::lexical_only(); let bogus = - kb_core::DocumentId("0000000000000000000000000000000000000000000000000000000000000000".to_string()); - let err = kb_app::inspect_doc_with_config(env.config.clone(), &bogus).unwrap_err(); + kebab_core::DocumentId("0000000000000000000000000000000000000000000000000000000000000000".to_string()); + let err = kebab_app::inspect_doc_with_config(env.config.clone(), &bogus).unwrap_err(); let msg = format!("{err:#}"); assert!( msg.contains("not found"), @@ -210,10 +210,10 @@ fn inspect_doc_not_found_returns_actionable_error() { #[test] fn inspect_chunk_not_found_returns_actionable_error() { let env = TestEnv::lexical_only(); - let bogus = kb_core::ChunkId( + let bogus = kebab_core::ChunkId( "0000000000000000000000000000000000000000000000000000000000000000".to_string(), ); - let err = kb_app::inspect_chunk_with_config(env.config.clone(), &bogus) + let err = kebab_app::inspect_chunk_with_config(env.config.clone(), &bogus) .unwrap_err(); let msg = format!("{err:#}"); assert!(msg.contains("not found"), "got: {msg}"); diff --git a/crates/kb-app/tests/search_lexical.rs b/crates/kebab-app/tests/search_lexical.rs similarity index 61% rename from crates/kb-app/tests/search_lexical.rs rename to crates/kebab-app/tests/search_lexical.rs index 3391ede..e50f6a3 100644 --- a/crates/kb-app/tests/search_lexical.rs +++ b/crates/kebab-app/tests/search_lexical.rs @@ -5,24 +5,24 @@ mod common; use common::TestEnv; -fn lexical_query(text: &str) -> kb_core::SearchQuery { - kb_core::SearchQuery { +fn lexical_query(text: &str) -> kebab_core::SearchQuery { + kebab_core::SearchQuery { text: text.to_string(), - mode: kb_core::SearchMode::Lexical, + mode: kebab_core::SearchMode::Lexical, k: 10, - filters: kb_core::SearchFilters::default(), + filters: kebab_core::SearchFilters::default(), } } #[test] fn lexical_search_returns_hits_after_ingest() { let env = TestEnv::lexical_only(); - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); // "Ownership" appears as a heading + paragraph in intro.md and // matches FTS5 default tokenizer easily. let hits = - kb_app::search_with_config(env.config.clone(), lexical_query("ownership")) + kebab_app::search_with_config(env.config.clone(), lexical_query("ownership")) .unwrap(); assert!(!hits.is_empty(), "expected ≥1 hit for 'ownership'"); @@ -34,7 +34,7 @@ fn lexical_search_returns_hits_after_ingest() { ); assert_eq!( h.retrieval.method, - kb_core::SearchMode::Lexical, + kebab_core::SearchMode::Lexical, "method label should be Lexical" ); } @@ -43,8 +43,8 @@ fn lexical_search_returns_hits_after_ingest() { #[test] fn lexical_search_empty_query_returns_empty() { let env = TestEnv::lexical_only(); - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); - let hits = kb_app::search_with_config(env.config.clone(), lexical_query(" ")) + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + let hits = kebab_app::search_with_config(env.config.clone(), lexical_query(" ")) .unwrap(); assert!(hits.is_empty(), "blank query must short-circuit empty"); } @@ -52,15 +52,15 @@ fn lexical_search_empty_query_returns_empty() { #[test] fn vector_mode_with_provider_none_errors_clearly() { let env = TestEnv::lexical_only(); - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); - let q = kb_core::SearchQuery { + let q = kebab_core::SearchQuery { text: "ownership".to_string(), - mode: kb_core::SearchMode::Vector, + mode: kebab_core::SearchMode::Vector, k: 10, - filters: kb_core::SearchFilters::default(), + filters: kebab_core::SearchFilters::default(), }; - let err = kb_app::search_with_config(env.config.clone(), q).unwrap_err(); + let err = kebab_app::search_with_config(env.config.clone(), q).unwrap_err(); let msg = format!("{err:#}"); assert!( msg.contains("embeddings disabled") || msg.contains("disabled"), diff --git a/crates/kb-app/tests/search_vector.rs b/crates/kebab-app/tests/search_vector.rs similarity index 75% rename from crates/kb-app/tests/search_vector.rs rename to crates/kebab-app/tests/search_vector.rs index 1ac7df6..5e80e09 100644 --- a/crates/kb-app/tests/search_vector.rs +++ b/crates/kebab-app/tests/search_vector.rs @@ -31,21 +31,21 @@ fn ingest_then_hybrid_search_returns_hits() { let env = TestEnv::with_embeddings(); let report = - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); assert_eq!(report.errors, 0, "no per-file errors: {report:?}"); assert_eq!(report.new, 3); - let q = kb_core::SearchQuery { + let q = kebab_core::SearchQuery { text: "ownership".to_string(), - mode: kb_core::SearchMode::Hybrid, + mode: kebab_core::SearchMode::Hybrid, k: 10, - filters: kb_core::SearchFilters::default(), + filters: kebab_core::SearchFilters::default(), }; - let hits = kb_app::search_with_config(env.config.clone(), q).unwrap(); + let hits = kebab_app::search_with_config(env.config.clone(), q).unwrap(); assert!(!hits.is_empty(), "expected hybrid hits for 'ownership'"); let methods: Vec<_> = hits.iter().map(|h| h.retrieval.method).collect(); assert!( - methods.iter().all(|m| *m == kb_core::SearchMode::Hybrid), + methods.iter().all(|m| *m == kebab_core::SearchMode::Hybrid), "every hit must report method=Hybrid: {methods:?}" ); } @@ -58,22 +58,22 @@ fn ingest_then_vector_search_carries_embedding_model() { let env = TestEnv::with_embeddings(); let report = - kb_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); + kebab_app::ingest_with_config(env.config.clone(), env.scope(), true).unwrap(); assert_eq!(report.errors, 0, "no per-file errors: {report:?}"); assert_eq!(report.new, 3); - let q = kb_core::SearchQuery { + let q = kebab_core::SearchQuery { text: "ownership".to_string(), - mode: kb_core::SearchMode::Vector, + mode: kebab_core::SearchMode::Vector, k: 10, - filters: kb_core::SearchFilters::default(), + filters: kebab_core::SearchFilters::default(), }; - let hits = kb_app::search_with_config(env.config.clone(), q).unwrap(); + let hits = kebab_app::search_with_config(env.config.clone(), q).unwrap(); assert!(!hits.is_empty(), "expected vector hits for 'ownership'"); // Vector mode dispatches through `VectorRetriever` and MUST stamp // each hit with the configured embedding_model id. - let expected = kb_core::EmbeddingModelId(env.config.models.embedding.model.clone()); + let expected = kebab_core::EmbeddingModelId(env.config.models.embedding.model.clone()); for h in &hits { assert_eq!( h.embedding_model, @@ -82,7 +82,7 @@ fn ingest_then_vector_search_carries_embedding_model() { ); assert_eq!( h.retrieval.method, - kb_core::SearchMode::Vector, + kebab_core::SearchMode::Vector, "vector-mode hit must report method=Vector" ); } diff --git a/crates/kb-chunk/Cargo.toml b/crates/kebab-chunk/Cargo.toml similarity index 84% rename from crates/kb-chunk/Cargo.toml rename to crates/kebab-chunk/Cargo.toml index 2238643..be91c3c 100644 --- a/crates/kb-chunk/Cargo.toml +++ b/crates/kebab-chunk/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-chunk" +name = "kebab-chunk" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,7 +8,7 @@ repository = { workspace = true } description = "Chunkers that turn kb-core::CanonicalDocument into kb-core::Chunk batches (§3.5, §4.2, §7.2)" [dependencies] -kb-core = { path = "../kb-core" } +kebab-core = { path = "../kebab-core" } serde_json_canonicalizer = "0.3" blake3 = { workspace = true } anyhow = { workspace = true } @@ -20,7 +20,7 @@ tracing = { workspace = true } # regular deps per design §8 (chunker consumes CanonicalDocument from kb-core # only); `cargo tree -p kb-chunk --depth 1` (default scope, excludes dev-deps) # confirms this. -kb-parse-md = { path = "../kb-parse-md" } -kb-normalize = { path = "../kb-normalize" } +kebab-parse-md = { path = "../kebab-parse-md" } +kebab-normalize = { path = "../kebab-normalize" } serde_json = { workspace = true } time = { workspace = true } diff --git a/crates/kb-chunk/src/lib.rs b/crates/kebab-chunk/src/lib.rs similarity index 91% rename from crates/kb-chunk/src/lib.rs rename to crates/kebab-chunk/src/lib.rs index 4cff5f1..dc4b7c0 100644 --- a/crates/kb-chunk/src/lib.rs +++ b/crates/kebab-chunk/src/lib.rs @@ -1,4 +1,4 @@ -//! `kb-chunk` — chunkers that emit [`kb_core::Chunk`] batches. +//! `kb-chunk` — chunkers that emit [`kebab_core::Chunk`] batches. //! //! Per design §3.5 (Chunk), §4.2 (chunk_id recipe), §7.2 (`Chunker` //! trait), §0 Q3/§14 (chunking priority). diff --git a/crates/kb-chunk/src/md_heading_v1.rs b/crates/kebab-chunk/src/md_heading_v1.rs similarity index 98% rename from crates/kb-chunk/src/md_heading_v1.rs rename to crates/kebab-chunk/src/md_heading_v1.rs index 7626ea5..f9054b7 100644 --- a/crates/kb-chunk/src/md_heading_v1.rs +++ b/crates/kebab-chunk/src/md_heading_v1.rs @@ -1,6 +1,6 @@ //! `md-heading-v1` — heading-aware Markdown chunker. -use kb_core::{ +use kebab_core::{ Block, BlockId, CanonicalDocument, Chunk, ChunkPolicy, Chunker, ChunkerVersion, DocumentId, SourceSpan, id_for_chunk, }; @@ -24,7 +24,7 @@ const POLICY_HASH_HEX_LEN: usize = 16; /// Heading-aware Markdown chunker. /// -/// Implements [`kb_core::Chunker`] for Markdown-derived +/// Implements [`kebab_core::Chunker`] for Markdown-derived /// [`CanonicalDocument`]s. /// /// **Behavior contract** (design §0 / §14, in priority order): @@ -409,7 +409,7 @@ fn estimate_block_tokens(b: &Block) -> usize { } /// Borrow the `CommonBlock` of any [`Block`] variant. -fn common(b: &Block) -> &kb_core::CommonBlock { +fn common(b: &Block) -> &kebab_core::CommonBlock { match b { Block::Heading(h) => &h.common, Block::Paragraph(t) | Block::Quote(t) => &t.common, @@ -424,7 +424,7 @@ fn common(b: &Block) -> &kb_core::CommonBlock { #[cfg(test)] mod tests { use super::*; - use kb_core::{ + use kebab_core::{ AssetId, CodeBlock, CommonBlock, HeadingBlock, ImageRefBlock, Lang, Metadata, Provenance, SourceType, TableBlock, TextBlock, TrustLevel, WorkspacePath, id_for_block, @@ -433,7 +433,7 @@ mod tests { fn make_doc(blocks: Vec) -> CanonicalDocument { CanonicalDocument { - doc_id: kb_core::DocumentId("d".repeat(32)), + doc_id: kebab_core::DocumentId("d".repeat(32)), source_asset_id: AssetId("a".repeat(32)), workspace_path: WorkspacePath::new("notes/test.md".into()).unwrap(), title: "Test".into(), @@ -450,14 +450,14 @@ mod tests { user: Default::default(), }, provenance: Provenance { events: vec![] }, - parser_version: kb_core::ParserVersion("test-parser-0".into()), + parser_version: kebab_core::ParserVersion("test-parser-0".into()), schema_version: 1, doc_version: 1, } } - fn doc_id() -> kb_core::DocumentId { - kb_core::DocumentId("d".repeat(32)) + fn doc_id() -> kebab_core::DocumentId { + kebab_core::DocumentId("d".repeat(32)) } fn span(start: u32, end: u32) -> SourceSpan { diff --git a/crates/kb-chunk/tests/long_section_snapshot.rs b/crates/kebab-chunk/tests/long_section_snapshot.rs similarity index 97% rename from crates/kb-chunk/tests/long_section_snapshot.rs rename to crates/kebab-chunk/tests/long_section_snapshot.rs index 3148300..ef2c982 100644 --- a/crates/kb-chunk/tests/long_section_snapshot.rs +++ b/crates/kebab-chunk/tests/long_section_snapshot.rs @@ -13,13 +13,13 @@ use std::path::PathBuf; -use kb_chunk::MdHeadingV1Chunker; -use kb_core::{ +use kebab_chunk::MdHeadingV1Chunker; +use kebab_core::{ AssetId, AssetStorage, Checksum, ChunkPolicy, ChunkerVersion, Chunker, MediaType, ParserVersion, RawAsset, SourceUri, WorkspacePath, }; -use kb_normalize::build_canonical_document; -use kb_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; +use kebab_normalize::build_canonical_document; +use kebab_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; use serde_json::Value; use time::OffsetDateTime; diff --git a/crates/kb-cli/Cargo.toml b/crates/kebab-cli/Cargo.toml similarity index 82% rename from crates/kb-cli/Cargo.toml rename to crates/kebab-cli/Cargo.toml index ae46920..591b60f 100644 --- a/crates/kb-cli/Cargo.toml +++ b/crates/kebab-cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-cli" +name = "kebab-cli" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -12,9 +12,9 @@ name = "kb" path = "src/main.rs" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-app = { path = "../kb-app" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-app = { path = "../kebab-app" } # kb-eval re-exports `compute_aggregate` / `compare_runs` / # `render_report_md` (P5-2). The DoD calls for these to be reached # "via kb-app", but kb-eval already depends on kb-app (P5-1 runner @@ -22,7 +22,7 @@ kb-app = { path = "../kb-app" } # require kb-app → kb-eval, forming a cycle. We therefore wire # kb-cli → kb-eval directly; documented in # `tasks/p5/p5-2-metrics-compare.md`. -kb-eval = { path = "../kb-eval" } +kebab-eval = { path = "../kebab-eval" } anyhow = { workspace = true } serde_json = { workspace = true } clap = { version = "4", features = ["derive"] } diff --git a/crates/kb-cli/src/main.rs b/crates/kebab-cli/src/main.rs similarity index 82% rename from crates/kb-cli/src/main.rs rename to crates/kebab-cli/src/main.rs index b71203c..25b9624 100644 --- a/crates/kb-cli/src/main.rs +++ b/crates/kebab-cli/src/main.rs @@ -6,7 +6,7 @@ use std::process::ExitCode; use clap::{Parser, Subcommand}; -use kb_app::doctor_signal::{DoctorUnhealthy, NoHitSignal, RefusalSignal}; +use kebab_app::doctor_signal::{DoctorUnhealthy, NoHitSignal, RefusalSignal}; mod wire; @@ -170,12 +170,12 @@ enum ModeFlag { Hybrid, } -impl From for kb_core::SearchMode { +impl From for kebab_core::SearchMode { fn from(m: ModeFlag) -> Self { match m { - ModeFlag::Lexical => kb_core::SearchMode::Lexical, - ModeFlag::Vector => kb_core::SearchMode::Vector, - ModeFlag::Hybrid => kb_core::SearchMode::Hybrid, + ModeFlag::Lexical => kebab_core::SearchMode::Lexical, + ModeFlag::Vector => kebab_core::SearchMode::Vector, + ModeFlag::Hybrid => kebab_core::SearchMode::Hybrid, } } } @@ -183,15 +183,15 @@ impl From for kb_core::SearchMode { fn main() -> ExitCode { let cli = Cli::parse(); let level = if cli.debug { - kb_app::logging::LogLevel::Debug + kebab_app::logging::LogLevel::Debug } else if cli.verbose { - kb_app::logging::LogLevel::Verbose + kebab_app::logging::LogLevel::Verbose } else { - kb_app::logging::LogLevel::Default + kebab_app::logging::LogLevel::Default }; // Fail-soft: if logging init errors (e.g. XDG state dir is read-only), // proceed without a guard rather than crashing — `kb` is still usable. - let _log_guard = kb_app::logging::init(level).ok(); + let _log_guard = kebab_app::logging::init(level).ok(); match run(&cli) { Ok(()) => ExitCode::from(0), Err(e) => { @@ -227,14 +227,14 @@ fn exit_code(err: &anyhow::Error) -> u8 { fn run(cli: &Cli) -> anyhow::Result<()> { match &cli.command { Cmd::Init { force } => { - kb_app::init_workspace(*force)?; + kebab_app::init_workspace(*force)?; if !cli.json { println!( "created {}", - kb_config::Config::xdg_config_path().display() + kebab_config::Config::xdg_config_path().display() ); - println!("created {}", kb_config::Config::xdg_data_dir().display()); - println!("created {}", kb_config::Config::xdg_state_dir().display()); + println!("created {}", kebab_config::Config::xdg_data_dir().display()); + println!("created {}", kebab_config::Config::xdg_state_dir().display()); println!("hint edit the config above, then `kb ingest`"); } Ok(()) @@ -244,13 +244,13 @@ fn run(cli: &Cli) -> anyhow::Result<()> { root, summary_only, } => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let scope = kb_core::SourceScope { + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let scope = kebab_core::SourceScope { root: root.clone().unwrap_or_else(|| PathBuf::from(&cfg.workspace.root)), include: cfg.workspace.include.clone(), exclude: cfg.workspace.exclude.clone(), }; - let report = kb_app::ingest_with_config(cfg, scope, *summary_only)?; + let report = kebab_app::ingest_with_config(cfg, scope, *summary_only)?; if cli.json { println!("{}", serde_json::to_string(&wire::wire_ingest(&report))?); } else { @@ -269,8 +269,8 @@ fn run(cli: &Cli) -> anyhow::Result<()> { Cmd::List { what } => match what { ListWhat::Docs => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let docs = kb_app::list_docs_with_config(cfg, kb_core::DocFilter::default())?; + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let docs = kebab_app::list_docs_with_config(cfg, kebab_core::DocFilter::default())?; if cli.json { println!("{}", serde_json::to_string(&wire::wire_doc_summaries(&docs))?); } else { @@ -284,9 +284,9 @@ fn run(cli: &Cli) -> anyhow::Result<()> { Cmd::Inspect { what } => match what { InspectWhat::Doc { id } => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let doc_id: kb_core::DocumentId = id.parse()?; - let doc = kb_app::inspect_doc_with_config(cfg, &doc_id)?; + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let doc_id: kebab_core::DocumentId = id.parse()?; + let doc = kebab_app::inspect_doc_with_config(cfg, &doc_id)?; // Inspect doc emits a `CanonicalDocument` — there's no §2 // wire schema for it (P1-5 will decide whether this also // becomes a tagged wrapper or stays as the raw domain @@ -296,9 +296,9 @@ fn run(cli: &Cli) -> anyhow::Result<()> { Ok(()) } InspectWhat::Chunk { id } => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let chunk_id: kb_core::ChunkId = id.parse()?; - let chunk = kb_app::inspect_chunk_with_config(cfg, &chunk_id)?; + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let chunk_id: kebab_core::ChunkId = id.parse()?; + let chunk = kebab_app::inspect_chunk_with_config(cfg, &chunk_id)?; println!("{}", serde_json::to_string(&wire::wire_chunk_inspection(&chunk))?); Ok(()) } @@ -310,14 +310,14 @@ fn run(cli: &Cli) -> anyhow::Result<()> { mode, explain: _, } => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let q = kb_core::SearchQuery { + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let q = kebab_core::SearchQuery { text: query.clone(), mode: (*mode).into(), k: *k, - filters: kb_core::SearchFilters::default(), + filters: kebab_core::SearchFilters::default(), }; - let hits = kb_app::search_with_config(cfg, q)?; + let hits = kebab_app::search_with_config(cfg, q)?; if cli.json { println!("{}", serde_json::to_string(&wire::wire_search_hits(&hits))?); } else { @@ -351,8 +351,8 @@ fn run(cli: &Cli) -> anyhow::Result<()> { temperature, seed, } => { - let cfg = kb_config::Config::load(cli.config.as_deref())?; - let opts = kb_app::AskOpts { + let cfg = kebab_config::Config::load(cli.config.as_deref())?; + let opts = kebab_app::AskOpts { k: *k, explain: *explain, mode: (*mode).into(), @@ -363,7 +363,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> { // wires up a real `mpsc::Sender` here. stream_sink: None, }; - let ans = kb_app::ask_with_config(cfg, query, opts)?; + let ans = kebab_app::ask_with_config(cfg, query, opts)?; if cli.json { println!("{}", serde_json::to_string(&wire::wire_answer(&ans))?); } else { @@ -377,7 +377,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> { } Cmd::Doctor => { - let report = kb_app::doctor_with_config_path(cli.config.as_deref())?; + let report = kebab_app::doctor_with_config_path(cli.config.as_deref())?; if cli.json { println!("{}", serde_json::to_string(&wire::wire_doctor(&report))?); } else { @@ -409,7 +409,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> { temperature, seed, } => { - let opts = kb_eval::EvalRunOpts { + let opts = kebab_eval::EvalRunOpts { suite: suite.clone(), mode: (*mode).into(), with_rag: *with_rag, @@ -417,7 +417,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> { temperature: *temperature, seed: *seed, }; - let run = kb_eval::run_eval(&opts)?; + let run = kebab_eval::run_eval(&opts)?; if cli.json { println!("{}", serde_json::to_string_pretty(&run)?); } else { @@ -430,8 +430,8 @@ fn run(cli: &Cli) -> anyhow::Result<()> { } EvalWhat::Aggregate { run_id } => { - let agg = kb_eval::compute_aggregate(run_id)?; - kb_eval::store_aggregate(run_id, &agg)?; + let agg = kebab_eval::compute_aggregate(run_id)?; + kebab_eval::store_aggregate(run_id, &agg)?; if cli.json { println!("{}", serde_json::to_string_pretty(&agg)?); } else { @@ -450,20 +450,20 @@ fn run(cli: &Cli) -> anyhow::Result<()> { strict_chunker_version, write_report, } => { - let cfg = kb_config::Config::load(None)?; - let opts = kb_eval::CompareOpts { + let cfg = kebab_config::Config::load(None)?; + let opts = kebab_eval::CompareOpts { strict_chunker_version: *strict_chunker_version, }; - let report = kb_eval::compare_runs_with_config(&cfg, run_a, run_b, &opts)?; - let md = kb_eval::render_report_md(&report); + let report = kebab_eval::compare_runs_with_config(&cfg, run_a, run_b, &opts)?; + let md = kebab_eval::render_report_md(&report); if cli.json { println!("{}", serde_json::to_string_pretty(&report)?); } else { print!("{md}"); } if *write_report { - let resolved_data_dir = kb_config::expand_path(&cfg.storage.data_dir, ""); - let runs_dir = kb_config::expand_path( + let resolved_data_dir = kebab_config::expand_path(&cfg.storage.data_dir, ""); + let runs_dir = kebab_config::expand_path( &cfg.storage.runs_dir, &resolved_data_dir.to_string_lossy(), ); diff --git a/crates/kb-cli/src/wire.rs b/crates/kebab-cli/src/wire.rs similarity index 97% rename from crates/kb-cli/src/wire.rs rename to crates/kebab-cli/src/wire.rs index 7dc236d..fd1493a 100644 --- a/crates/kb-cli/src/wire.rs +++ b/crates/kebab-cli/src/wire.rs @@ -17,8 +17,8 @@ use serde_json::Value; -use kb_app::DoctorReport; -use kb_core::{Answer, Chunk, DocSummary, IngestReport, SearchHit}; +use kebab_app::DoctorReport; +use kebab_core::{Answer, Chunk, DocSummary, IngestReport, SearchHit}; /// Insert `schema_version` into an object-shaped `Value`. Helper for the /// "serialize, then tag" pattern used by all the per-type wrappers below. @@ -132,7 +132,7 @@ mod tests { #[test] fn ingest_wrapper_tags_schema_version() { - use kb_core::SourceScope; + use kebab_core::SourceScope; let r = IngestReport { scope: SourceScope { root: std::path::PathBuf::from("/tmp"), diff --git a/crates/kb-config/Cargo.toml b/crates/kebab-config/Cargo.toml similarity index 87% rename from crates/kb-config/Cargo.toml rename to crates/kebab-config/Cargo.toml index f1430f8..4b40b01 100644 --- a/crates/kb-config/Cargo.toml +++ b/crates/kebab-config/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-config" +name = "kebab-config" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -9,7 +9,7 @@ description = "Config schema + XDG path resolution" [dependencies] # kb-core::CoreError reserved for P1-* config errors -kb-core = { path = "../kb-core" } +kebab-core = { path = "../kebab-core" } anyhow = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/kb-config/src/lib.rs b/crates/kebab-config/src/lib.rs similarity index 100% rename from crates/kb-config/src/lib.rs rename to crates/kebab-config/src/lib.rs diff --git a/crates/kb-config/src/paths.rs b/crates/kebab-config/src/paths.rs similarity index 100% rename from crates/kb-config/src/paths.rs rename to crates/kebab-config/src/paths.rs diff --git a/crates/kb-core/Cargo.toml b/crates/kebab-core/Cargo.toml similarity index 96% rename from crates/kb-core/Cargo.toml rename to crates/kebab-core/Cargo.toml index 795aad3..38b50af 100644 --- a/crates/kb-core/Cargo.toml +++ b/crates/kebab-core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-core" +name = "kebab-core" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } diff --git a/crates/kb-core/src/answer.rs b/crates/kebab-core/src/answer.rs similarity index 100% rename from crates/kb-core/src/answer.rs rename to crates/kebab-core/src/answer.rs diff --git a/crates/kb-core/src/asset.rs b/crates/kebab-core/src/asset.rs similarity index 100% rename from crates/kb-core/src/asset.rs rename to crates/kebab-core/src/asset.rs diff --git a/crates/kb-core/src/chunk.rs b/crates/kebab-core/src/chunk.rs similarity index 100% rename from crates/kb-core/src/chunk.rs rename to crates/kebab-core/src/chunk.rs diff --git a/crates/kb-core/src/citation.rs b/crates/kebab-core/src/citation.rs similarity index 100% rename from crates/kb-core/src/citation.rs rename to crates/kebab-core/src/citation.rs diff --git a/crates/kb-core/src/document.rs b/crates/kebab-core/src/document.rs similarity index 100% rename from crates/kb-core/src/document.rs rename to crates/kebab-core/src/document.rs diff --git a/crates/kb-core/src/errors.rs b/crates/kebab-core/src/errors.rs similarity index 100% rename from crates/kb-core/src/errors.rs rename to crates/kebab-core/src/errors.rs diff --git a/crates/kb-core/src/ids.rs b/crates/kebab-core/src/ids.rs similarity index 100% rename from crates/kb-core/src/ids.rs rename to crates/kebab-core/src/ids.rs diff --git a/crates/kb-core/src/ingest.rs b/crates/kebab-core/src/ingest.rs similarity index 100% rename from crates/kb-core/src/ingest.rs rename to crates/kebab-core/src/ingest.rs diff --git a/crates/kb-core/src/jobs.rs b/crates/kebab-core/src/jobs.rs similarity index 100% rename from crates/kb-core/src/jobs.rs rename to crates/kebab-core/src/jobs.rs diff --git a/crates/kb-core/src/lib.rs b/crates/kebab-core/src/lib.rs similarity index 100% rename from crates/kb-core/src/lib.rs rename to crates/kebab-core/src/lib.rs diff --git a/crates/kb-core/src/media.rs b/crates/kebab-core/src/media.rs similarity index 100% rename from crates/kb-core/src/media.rs rename to crates/kebab-core/src/media.rs diff --git a/crates/kb-core/src/metadata.rs b/crates/kebab-core/src/metadata.rs similarity index 100% rename from crates/kb-core/src/metadata.rs rename to crates/kebab-core/src/metadata.rs diff --git a/crates/kb-core/src/normalize.rs b/crates/kebab-core/src/normalize.rs similarity index 100% rename from crates/kb-core/src/normalize.rs rename to crates/kebab-core/src/normalize.rs diff --git a/crates/kb-core/src/search.rs b/crates/kebab-core/src/search.rs similarity index 100% rename from crates/kb-core/src/search.rs rename to crates/kebab-core/src/search.rs diff --git a/crates/kb-core/src/traits.rs b/crates/kebab-core/src/traits.rs similarity index 100% rename from crates/kb-core/src/traits.rs rename to crates/kebab-core/src/traits.rs diff --git a/crates/kb-core/src/vector.rs b/crates/kebab-core/src/vector.rs similarity index 100% rename from crates/kb-core/src/vector.rs rename to crates/kebab-core/src/vector.rs diff --git a/crates/kb-core/src/versions.rs b/crates/kebab-core/src/versions.rs similarity index 100% rename from crates/kb-core/src/versions.rs rename to crates/kebab-core/src/versions.rs diff --git a/crates/kb-embed-local/Cargo.toml b/crates/kebab-embed-local/Cargo.toml similarity index 85% rename from crates/kb-embed-local/Cargo.toml rename to crates/kebab-embed-local/Cargo.toml index 3a32217..41c30c8 100644 --- a/crates/kb-embed-local/Cargo.toml +++ b/crates/kebab-embed-local/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-embed-local" +name = "kebab-embed-local" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "Local fastembed-rs adapter implementing kb_core::Embedder (multilingual-e5-small default)" [dependencies] -kb-config = { path = "../kb-config" } -kb-embed = { path = "../kb-embed" } +kebab-config = { path = "../kebab-config" } +kebab-embed = { path = "../kebab-embed" } # Default features bring `ort-download-binaries` (bundled ONNX runtime) # and `hf-hub-native-tls` (first-run model download). No extra features # needed for the multilingual-e5-small path. diff --git a/crates/kb-embed-local/src/lib.rs b/crates/kebab-embed-local/src/lib.rs similarity index 97% rename from crates/kb-embed-local/src/lib.rs rename to crates/kebab-embed-local/src/lib.rs index 2d0796c..7267fe3 100644 --- a/crates/kb-embed-local/src/lib.rs +++ b/crates/kebab-embed-local/src/lib.rs @@ -1,5 +1,5 @@ //! `kb-embed-local` — `FastembedEmbedder`, a local ONNX-backed -//! [`Embedder`](kb_embed::Embedder) implementation. +//! [`Embedder`](kebab_embed::Embedder) implementation. //! //! Wraps [`fastembed::TextEmbedding`] for the default `multilingual-e5-small` //! (384-dim) model. Honors `config.models.embedding.batch_size` and applies @@ -26,8 +26,8 @@ use std::sync::Mutex; use anyhow::{Context, Result}; use fastembed::{EmbeddingModel, InitOptions, TextEmbedding}; -use kb_config::expand_path; -use kb_embed::{Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion}; +use kebab_config::expand_path; +use kebab_embed::{Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion}; /// Subdirectory under `config.storage.model_dir` where the fastembed /// adapter writes / reads ONNX + tokenizer files. Hard-coded per task @@ -58,9 +58,9 @@ impl FastembedEmbedder { /// `config.models.embedding.dimensions` matches the model's actual /// dim BEFORE returning, so a mismatch fails at construction (not on /// first `embed`). - pub fn new(config: &kb_config::Config) -> Result { + pub fn new(config: &kebab_config::Config) -> Result { // 1. Resolve `{data_dir}/models/fastembed/` from the config - // templates. Goes through the shared `kb_config::expand_path` + // templates. Goes through the shared `kebab_config::expand_path` // so every crate resolves storage paths identically. let data_dir = expand_path(&config.storage.data_dir, ""); let model_dir = expand_path(&config.storage.model_dir, &data_dir.to_string_lossy()); @@ -224,7 +224,7 @@ pub(crate) fn check_dim(model_dim: usize, cfg_dim: usize) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use kb_embed::EmbeddingInput; + use kebab_embed::EmbeddingInput; // ── check_dim ──────────────────────────────────────────────────── // diff --git a/crates/kb-embed-local/tests/embed_model.rs b/crates/kebab-embed-local/tests/embed_model.rs similarity index 96% rename from crates/kb-embed-local/tests/embed_model.rs rename to crates/kebab-embed-local/tests/embed_model.rs index b2af074..df545ff 100644 --- a/crates/kb-embed-local/tests/embed_model.rs +++ b/crates/kebab-embed-local/tests/embed_model.rs @@ -22,16 +22,16 @@ use std::hash::{Hash, Hasher}; use std::sync::OnceLock; use std::time::Instant; -use kb_embed::{Embedder, EmbeddingInput, EmbeddingKind}; -use kb_embed_local::FastembedEmbedder; +use kebab_embed::{Embedder, EmbeddingInput, EmbeddingKind}; +use kebab_embed_local::FastembedEmbedder; /// Build a `Config` whose `data_dir` lives in a per-process temp dir so /// the test never writes into the developer's real `~/.local/share/kb`. /// Returns the `Config` and the `TempDir` guard (caller keeps the guard /// alive for the test duration). -fn test_config() -> (kb_config::Config, tempfile::TempDir) { +fn test_config() -> (kebab_config::Config, tempfile::TempDir) { let tmp = tempfile::tempdir().expect("create tempdir"); - let mut cfg = kb_config::Config::defaults(); + let mut cfg = kebab_config::Config::defaults(); cfg.storage.data_dir = tmp.path().to_string_lossy().into_owned(); // model_dir keeps its default `{data_dir}/models` template; the // adapter resolves it itself. @@ -141,12 +141,12 @@ fn output_vectors_are_l2_normalized() { }, ]; let out = emb.embed(&inputs).expect("embed"); - // Per `kb_embed::assert_unit_norm` docs: `5e-4` is the safe bound at + // Per `kebab_embed::assert_unit_norm` docs: `5e-4` is the safe bound at // 384 dims (f32::EPSILON × √384 ≈ 2.3e-6, but ONNX kernels add // their own per-component noise; 1e-3 is very generous and matches // the spec's `± 1e-3`). - kb_embed::assert_unit_norm(&out, 1e-3); - kb_embed::assert_vector_shape(&out, 384); + kebab_embed::assert_unit_norm(&out, 1e-3); + kebab_embed::assert_vector_shape(&out, 384); } // ─── determinism ────────────────────────────────────────────────────── diff --git a/crates/kb-embed-local/tests/fixtures/embed/known-sentences.json b/crates/kebab-embed-local/tests/fixtures/embed/known-sentences.json similarity index 100% rename from crates/kb-embed-local/tests/fixtures/embed/known-sentences.json rename to crates/kebab-embed-local/tests/fixtures/embed/known-sentences.json diff --git a/crates/kb-embed/Cargo.toml b/crates/kebab-embed/Cargo.toml similarity index 90% rename from crates/kb-embed/Cargo.toml rename to crates/kebab-embed/Cargo.toml index 2719095..451607b 100644 --- a/crates/kb-embed/Cargo.toml +++ b/crates/kebab-embed/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-embed" +name = "kebab-embed" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "Embedder trait re-exports + opt-in deterministic MockEmbedder for downstream tests" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } serde = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } diff --git a/crates/kb-embed/src/lib.rs b/crates/kebab-embed/src/lib.rs similarity index 96% rename from crates/kb-embed/src/lib.rs rename to crates/kebab-embed/src/lib.rs index a4adba8..e1b5c0c 100644 --- a/crates/kb-embed/src/lib.rs +++ b/crates/kebab-embed/src/lib.rs @@ -1,9 +1,9 @@ //! `kb-embed` — thin re-export crate for the [`Embedder`] trait surface. //! //! This crate exists so downstream code (`kb-store-vector`, `kb-search`, -//! adapters in p3-2) can `use kb_embed::Embedder` and stay stable across +//! adapters in p3-2) can `use kebab_embed::Embedder` and stay stable across //! kb-core reorganizations. It defines **no new types**; everything is a -//! re-export of [`kb_core`]. +//! re-export of [`kebab_core`]. //! //! ## Mock implementation //! @@ -19,7 +19,7 @@ // Per spec §7.2 — these are the only public-surface types this crate offers. // Adding new types is forbidden by the task contract. -pub use kb_core::{ +pub use kebab_core::{ Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion, }; diff --git a/crates/kb-embed/src/mock.rs b/crates/kebab-embed/src/mock.rs similarity index 98% rename from crates/kb-embed/src/mock.rs rename to crates/kebab-embed/src/mock.rs index 3ca846a..d538bc3 100644 --- a/crates/kb-embed/src/mock.rs +++ b/crates/kebab-embed/src/mock.rs @@ -38,7 +38,7 @@ //! * Different `text` → different output with overwhelming probability. //! * All output components are finite (`is_finite()`). -use kb_core::{Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion}; +use kebab_core::{Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion}; /// Deterministic test double. See module docs for the hashing recipe. pub struct MockEmbedder { diff --git a/crates/kb-embed/tests/mock.rs b/crates/kebab-embed/tests/mock.rs similarity index 99% rename from crates/kb-embed/tests/mock.rs rename to crates/kebab-embed/tests/mock.rs index 3923f1d..d3ac109 100644 --- a/crates/kb-embed/tests/mock.rs +++ b/crates/kebab-embed/tests/mock.rs @@ -4,7 +4,7 @@ #![cfg(feature = "mock")] -use kb_embed::{ +use kebab_embed::{ Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion, MockEmbedder, assert_unit_norm, assert_vector_shape, }; diff --git a/crates/kb-embed/tests/reexports.rs b/crates/kebab-embed/tests/reexports.rs similarity index 99% rename from crates/kb-embed/tests/reexports.rs rename to crates/kebab-embed/tests/reexports.rs index cfa5c14..72203ab 100644 --- a/crates/kb-embed/tests/reexports.rs +++ b/crates/kebab-embed/tests/reexports.rs @@ -5,7 +5,7 @@ //! Runs under both `cargo test -p kb-embed` and //! `cargo test -p kb-embed --features mock`. -use kb_embed::{ +use kebab_embed::{ Embedder, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion, assert_vector_shape, }; diff --git a/crates/kb-eval/Cargo.toml b/crates/kebab-eval/Cargo.toml similarity index 83% rename from crates/kb-eval/Cargo.toml rename to crates/kebab-eval/Cargo.toml index 4281ee4..137d058 100644 --- a/crates/kb-eval/Cargo.toml +++ b/crates/kebab-eval/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-eval" +name = "kebab-eval" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -9,10 +9,10 @@ description = "Golden-fixture eval runner: load YAML, drive kb-app search/ask, [dependencies] # Allowed deps per p5-1 spec — domain types + facade only. -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-app = { path = "../kb-app" } -kb-store-sqlite = { path = "../kb-store-sqlite" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-app = { path = "../kebab-app" } +kebab-store-sqlite = { path = "../kebab-store-sqlite" } serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } diff --git a/crates/kb-eval/src/compare.rs b/crates/kebab-eval/src/compare.rs similarity index 98% rename from crates/kb-eval/src/compare.rs rename to crates/kebab-eval/src/compare.rs index 686e2c5..24ed840 100644 --- a/crates/kb-eval/src/compare.rs +++ b/crates/kebab-eval/src/compare.rs @@ -14,9 +14,9 @@ use std::fmt::Write as _; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use kb_config::Config; -use kb_core::{ChunkId, DocumentId}; -use kb_store_sqlite::SqliteStore; +use kebab_config::Config; +use kebab_core::{ChunkId, DocumentId}; +use kebab_store_sqlite::SqliteStore; use crate::loader::load_golden_set; use crate::metrics::{ @@ -300,7 +300,7 @@ fn extract_chunker_version(snapshot_json: &str) -> Option { } fn parse_results( - rows: &[kb_store_sqlite::EvalQueryResultRecord], + rows: &[kebab_store_sqlite::EvalQueryResultRecord], ) -> Result> { let mut out = HashMap::with_capacity(rows.len()); for row in rows { @@ -456,9 +456,9 @@ mod tests { let g = GoldenQuery { id: "q1".into(), query: "q".into(), - lang: kb_core::Lang(String::new()), + lang: kebab_core::Lang(String::new()), expected_doc_ids: vec![], - expected_chunk_ids: vec![kb_core::ChunkId("c1".into())], + expected_chunk_ids: vec![kebab_core::ChunkId("c1".into())], must_contain: vec![], forbidden: vec![], difficulty: None, diff --git a/crates/kb-eval/src/lib.rs b/crates/kebab-eval/src/lib.rs similarity index 94% rename from crates/kb-eval/src/lib.rs rename to crates/kebab-eval/src/lib.rs index 14cf89b..c0e0b01 100644 --- a/crates/kb-eval/src/lib.rs +++ b/crates/kebab-eval/src/lib.rs @@ -1,7 +1,7 @@ //! `kb-eval` — golden-fixture eval runner (P5-1). //! //! Loads `fixtures/golden_queries.yaml`, runs each entry through the -//! [`kb_app`] facade (lexical / vector / hybrid + optional RAG), and +//! [`kebab_app`] facade (lexical / vector / hybrid + optional RAG), and //! persists results into `eval_runs` / `eval_query_results` plus //! `runs_dir//per_query.jsonl` (design §5.7, §6.3). //! diff --git a/crates/kb-eval/src/loader.rs b/crates/kebab-eval/src/loader.rs similarity index 96% rename from crates/kb-eval/src/loader.rs rename to crates/kebab-eval/src/loader.rs index 5b24fa5..9112ec8 100644 --- a/crates/kb-eval/src/loader.rs +++ b/crates/kebab-eval/src/loader.rs @@ -6,7 +6,7 @@ //! tests that don't have a SQLite store handy. //! - [`load_golden_set_validated`] — additionally verifies every //! `expected_doc_id` / `expected_chunk_id` exists in the SQLite DB -//! the supplied [`kb_config::Config`] points at. Used by +//! the supplied [`kebab_config::Config`] points at. Used by //! [`crate::run_eval`] in production so a stale golden set fails //! fast at run start. @@ -14,7 +14,7 @@ use std::collections::{BTreeSet, HashSet}; use std::path::Path; use anyhow::{Context, Result, anyhow}; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use crate::types::GoldenQuery; @@ -43,11 +43,11 @@ pub fn load_golden_set(path: &Path) -> Result> { /// Currently used only by the in-module tests below; production code /// inlines `load_golden_set` + `validate_against_db` in /// [`crate::run_eval_with_config`] so the validation can run against -/// an already-opened [`kb_config::Config`] without re-parsing YAML. +/// an already-opened [`kebab_config::Config`] without re-parsing YAML. #[cfg(test)] pub(crate) fn load_golden_set_validated( yaml_path: &Path, - cfg: &kb_config::Config, + cfg: &kebab_config::Config, ) -> Result> { let queries = load_golden_set(yaml_path)?; validate_against_db(&queries, cfg)?; @@ -73,7 +73,7 @@ fn check_unique_ids(queries: &[GoldenQuery]) -> Result<()> { /// Read every doc_id / chunk_id referenced by `queries` and confirm /// SQLite has rows for them. Builds a sorted, deduplicated error /// message listing every missing ID. -pub(crate) fn validate_against_db(queries: &[GoldenQuery], cfg: &kb_config::Config) -> Result<()> { +pub(crate) fn validate_against_db(queries: &[GoldenQuery], cfg: &kebab_config::Config) -> Result<()> { // Short-circuit when there is nothing to validate — saves opening // SQLite for golden sets that omit expected_*_ids entirely. let needs_check = queries @@ -140,8 +140,8 @@ mod tests { //! `tests/loader.rs`; only the validated-variant cases need to sit //! next to the function so they can see the `pub(crate)` symbol. use super::*; - use kb_config::Config; - use kb_store_sqlite::SqliteStore; + use kebab_config::Config; + use kebab_store_sqlite::SqliteStore; use rusqlite::params; use std::fs; use tempfile::tempdir; diff --git a/crates/kb-eval/src/metrics.rs b/crates/kebab-eval/src/metrics.rs similarity index 98% rename from crates/kb-eval/src/metrics.rs rename to crates/kebab-eval/src/metrics.rs index e8056ef..6f25784 100644 --- a/crates/kb-eval/src/metrics.rs +++ b/crates/kebab-eval/src/metrics.rs @@ -13,9 +13,9 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use kb_config::Config; -use kb_core::{ChunkId, Citation, DocumentId}; -use kb_store_sqlite::SqliteStore; +use kebab_config::Config; +use kebab_core::{ChunkId, Citation, DocumentId}; +use kebab_store_sqlite::SqliteStore; use crate::loader::load_golden_set; use crate::types::{GoldenQuery, QueryResult}; @@ -175,7 +175,7 @@ fn load_golden_for_metrics() -> Result> { /// `tasks/p5/p5-2-metrics-compare.md`), this will need to take one. pub(crate) fn aggregate_from_rows( queries: &[GoldenQuery], - rows: &[kb_store_sqlite::EvalQueryResultRecord], + rows: &[kebab_store_sqlite::EvalQueryResultRecord], ) -> Result { let golden_by_id: HashMap<&str, &GoldenQuery> = queries.iter().map(|q| (q.id.as_str(), q)).collect(); @@ -395,14 +395,14 @@ fn ratio_or_zero(num: u32, denom: u32) -> f32 { #[cfg(test)] mod tests { use super::*; - use kb_core::{ + use kebab_core::{ ChunkId, ChunkerVersion, Citation, DocumentId, IndexVersion, RetrievalDetail, SearchHit, SearchMode, }; - use kb_core::asset::WorkspacePath; - use kb_core::media::Lang; - use kb_core::answer::{Answer, AnswerCitation, AnswerRetrievalSummary, ModelRef, TokenUsage, TraceId}; - use kb_core::versions::PromptTemplateVersion; + use kebab_core::asset::WorkspacePath; + use kebab_core::media::Lang; + use kebab_core::answer::{Answer, AnswerCitation, AnswerRetrievalSummary, ModelRef, TokenUsage, TraceId}; + use kebab_core::versions::PromptTemplateVersion; use time::OffsetDateTime; fn gq(id: &str, expected_chunks: &[&str], expected_docs: &[&str]) -> GoldenQuery { @@ -460,9 +460,9 @@ mod tests { } fn record(id: &str, hits: Vec, error: Option, answer: Option) - -> kb_store_sqlite::EvalQueryResultRecord + -> kebab_store_sqlite::EvalQueryResultRecord { - kb_store_sqlite::EvalQueryResultRecord { + kebab_store_sqlite::EvalQueryResultRecord { query_id: id.into(), result_json: serde_json::to_string(&qr(id, hits, error, answer)).unwrap(), } diff --git a/crates/kb-eval/src/runner.rs b/crates/kebab-eval/src/runner.rs similarity index 93% rename from crates/kb-eval/src/runner.rs rename to crates/kebab-eval/src/runner.rs index 6e5f2ad..b76d72e 100644 --- a/crates/kb-eval/src/runner.rs +++ b/crates/kebab-eval/src/runner.rs @@ -6,10 +6,10 @@ use std::path::PathBuf; use std::time::Instant; use anyhow::{Context, Result}; -use kb_app::App; -use kb_config::expand_path; -use kb_core::{SearchFilters, SearchQuery}; -use kb_store_sqlite::{EvalRunRow, SqliteStore}; +use kebab_app::App; +use kebab_config::expand_path; +use kebab_core::{SearchFilters, SearchQuery}; +use kebab_store_sqlite::{EvalRunRow, SqliteStore}; use time::OffsetDateTime; use crate::loader::{load_golden_set, validate_against_db}; @@ -25,18 +25,18 @@ fn elapsed_ms_u32(start: Instant) -> u32 { } /// Run the golden suite end-to-end against the active XDG-loaded -/// [`kb_config::Config`]. Wraps [`run_eval_with_config`] with +/// [`kebab_config::Config`]. Wraps [`run_eval_with_config`] with /// `Config::load(None)`. pub fn run_eval(opts: &EvalRunOpts) -> Result { - let cfg = kb_config::Config::load(None).context("load Config for run_eval")?; + let cfg = kebab_config::Config::load(None).context("load Config for run_eval")?; run_eval_with_config(&cfg, opts) } /// Run the golden suite end-to-end against an explicit -/// [`kb_config::Config`]. Used by integration tests (TempDir-backed +/// [`kebab_config::Config`]. Used by integration tests (TempDir-backed /// data_dir) and any future caller that wants to drive the runner /// against a non-default config. -pub fn run_eval_with_config(cfg: &kb_config::Config, opts: &EvalRunOpts) -> Result { +pub fn run_eval_with_config(cfg: &kebab_config::Config, opts: &EvalRunOpts) -> Result { let started = Instant::now(); // ── 1. Load golden set ──────────────────────────────────────────────── @@ -167,7 +167,7 @@ fn execute_query(app: &App, gq: &GoldenQuery, opts: &EvalRunOpts) -> QueryResult // call did not already error out (we want one error per query, not // a duplicated one). let answer = if opts.with_rag && error.is_none() { - let ask_opts = kb_app::AskOpts { + let ask_opts = kebab_app::AskOpts { k: opts.k, explain: true, mode: opts.mode, @@ -206,7 +206,7 @@ fn execute_query(app: &App, gq: &GoldenQuery, opts: &EvalRunOpts) -> QueryResult /// stable run-time property of the config alone. P5-2 may compose it /// from `embedding.{model,version,dimensions}` if it needs the field /// for compare reports. -fn build_config_snapshot(cfg: &kb_config::Config) -> Result { +fn build_config_snapshot(cfg: &kebab_config::Config) -> Result { let cfg_value = serde_json::to_value(cfg).context("serialize Config")?; Ok(serde_json::json!({ "config": cfg_value, @@ -234,7 +234,7 @@ fn build_config_snapshot(cfg: &kb_config::Config) -> Result { /// `run_id` collision would already have failed the `eval_runs` /// PRIMARY KEY upstream). fn write_per_query_jsonl( - cfg: &kb_config::Config, + cfg: &kebab_config::Config, run_id: &str, per_query: &[QueryResult], ) -> Result<()> { diff --git a/crates/kb-eval/src/types.rs b/crates/kebab-eval/src/types.rs similarity index 92% rename from crates/kb-eval/src/types.rs rename to crates/kebab-eval/src/types.rs index 6165770..db5e15d 100644 --- a/crates/kb-eval/src/types.rs +++ b/crates/kebab-eval/src/types.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; -use kb_core::{Answer, ChunkId, DocumentId, Lang, SearchHit, SearchMode}; +use kebab_core::{Answer, ChunkId, DocumentId, Lang, SearchHit, SearchMode}; /// One golden query loaded from `fixtures/golden_queries.yaml`. /// @@ -41,10 +41,10 @@ pub struct EvalRunOpts { /// Suite label persisted into `eval_runs.suite`. The shipped /// fixture is `"golden"`; other suites can reuse the same runner. pub suite: String, - /// Retrieval mode forwarded to every `kb_app::search` / - /// `kb_app::ask` call inside the run. + /// Retrieval mode forwarded to every `kebab_app::search` / + /// `kebab_app::ask` call inside the run. pub mode: SearchMode, - /// When `true`, also call `kb_app::ask` per query and record the + /// When `true`, also call `kebab_app::ask` per query and record the /// resulting `Answer` on the `QueryResult`. pub with_rag: bool, /// Top-k forwarded to retrieval (and `AskOpts.k` when `with_rag`). diff --git a/crates/kb-eval/tests/fixtures/eval/compare-1.json b/crates/kebab-eval/tests/fixtures/eval/compare-1.json similarity index 100% rename from crates/kb-eval/tests/fixtures/eval/compare-1.json rename to crates/kebab-eval/tests/fixtures/eval/compare-1.json diff --git a/crates/kb-eval/tests/fixtures/eval/run-1.json b/crates/kebab-eval/tests/fixtures/eval/run-1.json similarity index 100% rename from crates/kb-eval/tests/fixtures/eval/run-1.json rename to crates/kebab-eval/tests/fixtures/eval/run-1.json diff --git a/crates/kb-eval/tests/loader.rs b/crates/kebab-eval/tests/loader.rs similarity index 98% rename from crates/kb-eval/tests/loader.rs rename to crates/kebab-eval/tests/loader.rs index 115dacb..62f4033 100644 --- a/crates/kb-eval/tests/loader.rs +++ b/crates/kebab-eval/tests/loader.rs @@ -8,7 +8,7 @@ use std::fs; -use kb_eval::load_golden_set; +use kebab_eval::load_golden_set; use tempfile::tempdir; // ── 1. parser accepts well-formed YAML with optional fields ────────────────── diff --git a/crates/kb-eval/tests/metrics_and_compare.rs b/crates/kebab-eval/tests/metrics_and_compare.rs similarity index 98% rename from crates/kb-eval/tests/metrics_and_compare.rs rename to crates/kebab-eval/tests/metrics_and_compare.rs index 9721df9..be9746f 100644 --- a/crates/kb-eval/tests/metrics_and_compare.rs +++ b/crates/kebab-eval/tests/metrics_and_compare.rs @@ -9,17 +9,17 @@ use std::fs; use std::path::PathBuf; -use kb_config::Config; -use kb_core::{ +use kebab_config::Config; +use kebab_core::{ ChunkId, ChunkerVersion, Citation, DocumentId, IndexVersion, Lang, RetrievalDetail, SearchHit, SearchMode, asset::WorkspacePath, }; -use kb_eval::{ +use kebab_eval::{ AggregateMetrics, CompareOpts, CompareReport, ComparisonKind, GoldenQuery, QueryResult, compare_runs_with_config, compute_aggregate_with_config, store_aggregate_with_config, }; -use kb_store_sqlite::{EvalRunRow, SqliteStore}; +use kebab_store_sqlite::{EvalRunRow, SqliteStore}; use tempfile::TempDir; use time::OffsetDateTime; @@ -259,7 +259,7 @@ fn compare_runs_classifies_win_loss_draw_regression() { drop(store); let report = compare_runs_with_config(&cfg, "run_a", "run_b", &CompareOpts::default()).unwrap(); - let by_id: std::collections::HashMap<&str, &kb_eval::QueryComparison> = + let by_id: std::collections::HashMap<&str, &kebab_eval::QueryComparison> = report.per_query.iter().map(|c| (c.query_id.as_str(), c)).collect(); assert_eq!(by_id["q-001"].kind, ComparisonKind::Loss); assert_eq!(by_id["q-002"].kind, ComparisonKind::Win); @@ -414,7 +414,7 @@ fn render_report_md_is_human_readable() { drop(store); let report = compare_runs_with_config(&cfg, "run_a", "run_b", &CompareOpts::default()).unwrap(); - let md = kb_eval::render_report_md(&report); + let md = kebab_eval::render_report_md(&report); assert!(md.starts_with("# Eval compare:"), "md = {md}"); assert!(md.contains("hit@1")); assert!(md.contains("MRR")); diff --git a/crates/kb-eval/tests/runner.rs b/crates/kebab-eval/tests/runner.rs similarity index 98% rename from crates/kb-eval/tests/runner.rs rename to crates/kebab-eval/tests/runner.rs index 248c1fd..e13e0f9 100644 --- a/crates/kb-eval/tests/runner.rs +++ b/crates/kebab-eval/tests/runner.rs @@ -1,6 +1,6 @@ //! Runner integration tests for `kb-eval` (P5-1). //! -//! Drives [`kb_eval::run_eval_with_config`] end-to-end against a +//! Drives [`kebab_eval::run_eval_with_config`] end-to-end against a //! TempDir-backed config: //! //! - tiny seeded SQLite corpus (3 docs / 3 chunks) used as the @@ -17,10 +17,10 @@ use std::fs; use std::path::{Path, PathBuf}; use std::sync::Mutex; -use kb_config::Config; -use kb_core::SearchMode; -use kb_eval::{EvalRunOpts, QueryResult, run_eval_with_config}; -use kb_store_sqlite::SqliteStore; +use kebab_config::Config; +use kebab_core::SearchMode; +use kebab_eval::{EvalRunOpts, QueryResult, run_eval_with_config}; +use kebab_store_sqlite::SqliteStore; use rusqlite::params; use tempfile::TempDir; @@ -110,7 +110,7 @@ fn seed_corpus(store: &SqliteStore) { // Build the FTS index so lexical search returns hits. Reuses the // same connection guard rather than reopening — the SAVEPOINT // protocol nests correctly under the existing read_conn lock. - kb_store_sqlite::rebuild_chunks_fts(&conn).unwrap(); + kebab_store_sqlite::rebuild_chunks_fts(&conn).unwrap(); drop(conn); } diff --git a/crates/kb-llm-local/Cargo.toml b/crates/kebab-llm-local/Cargo.toml similarity index 91% rename from crates/kb-llm-local/Cargo.toml rename to crates/kebab-llm-local/Cargo.toml index 20fc623..6cc8669 100644 --- a/crates/kb-llm-local/Cargo.toml +++ b/crates/kebab-llm-local/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-llm-local" +name = "kebab-llm-local" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,9 +8,9 @@ repository = { workspace = true } description = "Ollama HTTP adapter implementing kb_core::LanguageModel via reqwest::blocking" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-llm = { path = "../kb-llm" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-llm = { path = "../kebab-llm" } # `default-features = false` drops the `default-tls` (native-tls / openssl) # feature so we don't pull in a system OpenSSL; we explicitly pin rustls. # Note: `default-features = false` does NOT drop tokio — reqwest 0.12's diff --git a/crates/kb-llm-local/src/error.rs b/crates/kebab-llm-local/src/error.rs similarity index 100% rename from crates/kb-llm-local/src/error.rs rename to crates/kebab-llm-local/src/error.rs diff --git a/crates/kb-llm-local/src/lib.rs b/crates/kebab-llm-local/src/lib.rs similarity index 91% rename from crates/kb-llm-local/src/lib.rs rename to crates/kebab-llm-local/src/lib.rs index 64a6233..3e0bbfd 100644 --- a/crates/kb-llm-local/src/lib.rs +++ b/crates/kebab-llm-local/src/lib.rs @@ -1,5 +1,5 @@ //! `kb-llm-local` — Ollama HTTP adapter implementing -//! [`kb_core::LanguageModel`] over the local `POST /api/generate` endpoint. +//! [`kebab_core::LanguageModel`] over the local `POST /api/generate` endpoint. //! //! ## Why a separate crate //! @@ -39,11 +39,11 @@ mod ollama; pub use error::LlmError; pub use ollama::OllamaLanguageModel; -// Re-export the trait surface so adapter consumers can `use kb_llm_local::*` +// Re-export the trait surface so adapter consumers can `use kebab_llm_local::*` // without also depending on `kb-llm` directly. These are the same symbols // `kb-llm` re-exports from `kb-core`; this crate adds **no new types** to // the trait surface (`LlmError` and `OllamaLanguageModel` are // implementation-side only). -pub use kb_llm::{ +pub use kebab_llm::{ FinishReason, GenerateRequest, LanguageModel, ModelRef, TokenChunk, TokenUsage, }; diff --git a/crates/kb-llm-local/src/ollama.rs b/crates/kebab-llm-local/src/ollama.rs similarity index 98% rename from crates/kb-llm-local/src/ollama.rs rename to crates/kebab-llm-local/src/ollama.rs index f4f2f9e..51e2a49 100644 --- a/crates/kb-llm-local/src/ollama.rs +++ b/crates/kebab-llm-local/src/ollama.rs @@ -41,7 +41,7 @@ use std::io::{BufRead, BufReader}; use std::time::Duration; -use kb_core::{ +use kebab_core::{ FinishReason, GenerateRequest, LanguageModel, ModelRef, TokenChunk, TokenUsage, }; use serde::{Deserialize, Serialize}; @@ -68,7 +68,7 @@ pub struct OllamaLanguageModel { } impl OllamaLanguageModel { - /// Build an adapter from a workspace [`kb_config::Config`]. Reads + /// Build an adapter from a workspace [`kebab_config::Config`]. Reads /// `config.models.llm.{provider, model, endpoint, context_tokens, /// temperature, seed}`. /// @@ -76,7 +76,7 @@ impl OllamaLanguageModel { /// expected to have validated `provider == "ollama"`; this constructor /// trusts the config and would happily build for an unknown provider. /// (Provider routing is the App layer's job, not the adapter's.) - pub fn new(config: &kb_config::Config) -> anyhow::Result { + pub fn new(config: &kebab_config::Config) -> anyhow::Result { let llm = &config.models.llm; let client = reqwest::blocking::Client::builder() .timeout(REQUEST_TIMEOUT) @@ -292,7 +292,7 @@ impl Iterator for OllamaStream { // pipelines that expect a terminal frame still terminate. self.done = true; tracing::warn!( - target: "kb_llm_local", + target: "kebab_llm_local", "ollama stream ended without a `done: true` frame; synthesizing Aborted", ); return Some(Ok(TokenChunk::Done { @@ -361,14 +361,14 @@ impl Iterator for OllamaStream { }; let prompt_tokens = line.prompt_eval_count.unwrap_or_else(|| { tracing::warn!( - target: "kb_llm_local", + target: "kebab_llm_local", "ollama done frame missing prompt_eval_count; defaulting to 0", ); 0 }); let completion_tokens = line.eval_count.unwrap_or_else(|| { tracing::warn!( - target: "kb_llm_local", + target: "kebab_llm_local", "ollama done frame missing eval_count; defaulting to 0", ); 0 diff --git a/crates/kb-llm-local/tests/construction.rs b/crates/kebab-llm-local/tests/construction.rs similarity index 94% rename from crates/kb-llm-local/tests/construction.rs rename to crates/kebab-llm-local/tests/construction.rs index 80ed36b..37f842b 100644 --- a/crates/kb-llm-local/tests/construction.rs +++ b/crates/kebab-llm-local/tests/construction.rs @@ -2,8 +2,8 @@ //! relevant config fields and exposes them via the trait surface, all //! without touching the network (per design §7.2 lazy-connect contract). -use kb_config::Config; -use kb_llm_local::{LanguageModel, OllamaLanguageModel}; +use kebab_config::Config; +use kebab_llm_local::{LanguageModel, OllamaLanguageModel}; #[test] fn construction_with_default_config_returns_expected_model_ref() { diff --git a/crates/kb-llm-local/tests/integration.rs b/crates/kebab-llm-local/tests/integration.rs similarity index 92% rename from crates/kb-llm-local/tests/integration.rs rename to crates/kebab-llm-local/tests/integration.rs index 6d33813..16a237c 100644 --- a/crates/kb-llm-local/tests/integration.rs +++ b/crates/kebab-llm-local/tests/integration.rs @@ -11,9 +11,9 @@ //! These hit `http://127.0.0.1:11434` directly and require an actual model //! pulled locally. CI runs default (non-ignored) tests only. -use kb_config::Config; -use kb_core::{GenerateRequest, TokenChunk}; -use kb_llm_local::{LanguageModel, OllamaLanguageModel}; +use kebab_config::Config; +use kebab_core::{GenerateRequest, TokenChunk}; +use kebab_llm_local::{LanguageModel, OllamaLanguageModel}; #[test] #[ignore = "requires a local Ollama daemon + pulled model"] diff --git a/crates/kb-llm-local/tests/streaming.rs b/crates/kebab-llm-local/tests/streaming.rs similarity index 99% rename from crates/kb-llm-local/tests/streaming.rs rename to crates/kebab-llm-local/tests/streaming.rs index f52d04e..cc25940 100644 --- a/crates/kb-llm-local/tests/streaming.rs +++ b/crates/kebab-llm-local/tests/streaming.rs @@ -10,9 +10,9 @@ //! error mapping, finish-reason mapping, missing-counter degradation, and //! determinism semantics. -use kb_config::Config; -use kb_core::{FinishReason, GenerateRequest, TokenChunk}; -use kb_llm_local::{LanguageModel, LlmError, OllamaLanguageModel}; +use kebab_config::Config; +use kebab_core::{FinishReason, GenerateRequest, TokenChunk}; +use kebab_llm_local::{LanguageModel, LlmError, OllamaLanguageModel}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; diff --git a/crates/kb-llm/Cargo.toml b/crates/kebab-llm/Cargo.toml similarity index 90% rename from crates/kb-llm/Cargo.toml rename to crates/kebab-llm/Cargo.toml index f70ab76..15684b5 100644 --- a/crates/kb-llm/Cargo.toml +++ b/crates/kebab-llm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-llm" +name = "kebab-llm" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,7 +8,7 @@ repository = { workspace = true } description = "LanguageModel trait re-export + feature-gated MockLanguageModel for downstream tests" [dependencies] -kb-core = { path = "../kb-core" } +kebab-core = { path = "../kebab-core" } anyhow = { workspace = true } [features] diff --git a/crates/kb-llm/src/lib.rs b/crates/kebab-llm/src/lib.rs similarity index 94% rename from crates/kb-llm/src/lib.rs rename to crates/kebab-llm/src/lib.rs index d2d7ccd..5001c52 100644 --- a/crates/kb-llm/src/lib.rs +++ b/crates/kebab-llm/src/lib.rs @@ -1,8 +1,8 @@ //! `kb-llm` — thin re-export crate for the [`LanguageModel`] trait surface. //! //! This crate exists so downstream code (`kb-rag`, adapters in p4-2) can -//! `use kb_llm::LanguageModel` and stay stable across kb-core reorganizations. -//! It defines **no new types**; everything is a re-export of [`kb_core`]. +//! `use kebab_llm::LanguageModel` and stay stable across kb-core reorganizations. +//! It defines **no new types**; everything is a re-export of [`kebab_core`]. //! //! ## Mock implementation //! @@ -20,7 +20,7 @@ // Per spec §7.2 — these are the only public-surface types this crate offers. // Adding new types is forbidden by the task contract. -pub use kb_core::{ +pub use kebab_core::{ FinishReason, GenerateRequest, LanguageModel, ModelRef, TokenChunk, TokenUsage, }; diff --git a/crates/kb-llm/src/mock.rs b/crates/kebab-llm/src/mock.rs similarity index 99% rename from crates/kb-llm/src/mock.rs rename to crates/kebab-llm/src/mock.rs index e84c90d..c63faef 100644 --- a/crates/kb-llm/src/mock.rs +++ b/crates/kebab-llm/src/mock.rs @@ -36,7 +36,7 @@ //! - No tokenizer. `usage.prompt_tokens` / `completion_tokens` are whatever //! the constructor was given — the mock does not count. -use kb_core::{ +use kebab_core::{ FinishReason, GenerateRequest, LanguageModel, ModelRef, TokenChunk, TokenUsage, }; diff --git a/crates/kb-llm/tests/mock.rs b/crates/kebab-llm/tests/mock.rs similarity index 99% rename from crates/kb-llm/tests/mock.rs rename to crates/kebab-llm/tests/mock.rs index c086cda..2d5bbbd 100644 --- a/crates/kb-llm/tests/mock.rs +++ b/crates/kebab-llm/tests/mock.rs @@ -4,7 +4,7 @@ #![cfg(feature = "mock")] -use kb_llm::{ +use kebab_llm::{ FinishReason, GenerateRequest, LanguageModel, MockLanguageModel, TokenChunk, TokenUsage, assert_finish_chunk, }; diff --git a/crates/kb-llm/tests/reexports.rs b/crates/kebab-llm/tests/reexports.rs similarity index 99% rename from crates/kb-llm/tests/reexports.rs rename to crates/kebab-llm/tests/reexports.rs index 88f5db2..8df88d5 100644 --- a/crates/kb-llm/tests/reexports.rs +++ b/crates/kebab-llm/tests/reexports.rs @@ -5,7 +5,7 @@ //! Runs under both `cargo test -p kb-llm` and //! `cargo test -p kb-llm --features mock`. -use kb_llm::{ +use kebab_llm::{ FinishReason, GenerateRequest, LanguageModel, ModelRef, TokenChunk, TokenUsage, assert_finish_chunk, }; diff --git a/crates/kb-normalize/Cargo.toml b/crates/kebab-normalize/Cargo.toml similarity index 85% rename from crates/kb-normalize/Cargo.toml rename to crates/kebab-normalize/Cargo.toml index 6d61e35..9138837 100644 --- a/crates/kb-normalize/Cargo.toml +++ b/crates/kebab-normalize/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-normalize" +name = "kebab-normalize" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "Lift parser output (kb-parse-types) into kb-core::CanonicalDocument with deterministic IDs (§3.4, §4.2, §4.3)" [dependencies] -kb-core = { path = "../kb-core" } -kb-parse-types = { path = "../kb-parse-types" } +kebab-core = { path = "../kebab-core" } +kebab-parse-types = { path = "../kebab-parse-types" } serde = { workspace = true } serde_json = { workspace = true } unicode-normalization = "0.1" @@ -23,5 +23,5 @@ tracing = { workspace = true } # Forbidden as a regular dep per design §8 (kb-normalize must not depend # on any specific parser); `cargo tree -p kb-normalize --depth 1` (the # default scope, excluding dev-deps) confirms this. -kb-parse-md = { path = "../kb-parse-md" } +kebab-parse-md = { path = "../kebab-parse-md" } serde_json = { workspace = true } diff --git a/crates/kb-normalize/src/lib.rs b/crates/kebab-normalize/src/lib.rs similarity index 97% rename from crates/kb-normalize/src/lib.rs rename to crates/kebab-normalize/src/lib.rs index 9e08c76..2723d6f 100644 --- a/crates/kb-normalize/src/lib.rs +++ b/crates/kebab-normalize/src/lib.rs @@ -1,5 +1,5 @@ //! `kb-normalize` — lift parser output (`kb-parse-types`) into a -//! [`kb_core::CanonicalDocument`] with deterministic IDs. +//! [`kebab_core::CanonicalDocument`] with deterministic IDs. //! //! Per design §3.4 (CanonicalDocument / Block), §4.2 (ID recipe), §4.3 //! (ordinal rule), §3.6 (Provenance), §8 (module boundaries). @@ -20,16 +20,16 @@ use std::collections::HashMap; use anyhow::Result; -use kb_core::{ +use kebab_core::{ Block, BlockId, CanonicalDocument, CodeBlock, CommonBlock, DocumentId, HeadingBlock, ImageRefBlock, Inline, Lang, ListBlock, Metadata, ParserVersion, Provenance, ProvenanceEvent, ProvenanceKind, RawAsset, TableBlock, TextBlock, }; -use kb_parse_types::{ParsedBlock, ParsedPayload, Warning, WarningKind}; +use kebab_parse_types::{ParsedBlock, ParsedPayload, Warning, WarningKind}; use time::OffsetDateTime; use unicode_normalization::UnicodeNormalization; -pub use kb_core::{id_for_block, id_for_doc}; +pub use kebab_core::{id_for_block, id_for_doc}; /// Build a [`CanonicalDocument`] from the raw asset, frontmatter /// metadata, parser blocks, parser version, and any warnings. @@ -38,7 +38,7 @@ pub use kb_core::{id_for_block, id_for_doc}; /// /// * `doc_id = id_for_doc(workspace_path, asset_id, parser_version)` — /// `workspace_path` is consumed verbatim from `asset` (already NFC + -/// POSIX per `kb_core::normalize::to_posix`). +/// POSIX per `kebab_core::normalize::to_posix`). /// * `block_id = id_for_block(doc_id, kind, heading_path, ordinal, /// source_span)` — `ordinal` is **0-based, scoped to (heading_path, /// block_kind), in document order** per §4.3. @@ -329,7 +329,7 @@ fn flatten_inline(i: &Inline, out: &mut String) { #[cfg(test)] mod tests { use super::*; - use kb_core::{ + use kebab_core::{ AssetId, AssetStorage, Checksum, MediaType, SourceSpan, SourceType, SourceUri, TrustLevel, WorkspacePath, normalize::to_posix, }; @@ -386,7 +386,7 @@ mod tests { let h1_b = vec!["B".to_string()]; vec![ ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Paragraph, + kind: kebab_parse_types::ParsedBlockKind::Paragraph, heading_path: h1_a.clone(), source_span: SourceSpan::Line { start: 1, end: 1 }, payload: ParsedPayload::Paragraph { @@ -395,7 +395,7 @@ mod tests { }, }, ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Paragraph, + kind: kebab_parse_types::ParsedBlockKind::Paragraph, heading_path: h1_a.clone(), source_span: SourceSpan::Line { start: 2, end: 2 }, payload: ParsedPayload::Paragraph { @@ -404,7 +404,7 @@ mod tests { }, }, ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Paragraph, + kind: kebab_parse_types::ParsedBlockKind::Paragraph, heading_path: h1_a.clone(), source_span: SourceSpan::Line { start: 3, end: 3 }, payload: ParsedPayload::Paragraph { @@ -413,7 +413,7 @@ mod tests { }, }, ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Code, + kind: kebab_parse_types::ParsedBlockKind::Code, heading_path: h1_a, source_span: SourceSpan::Line { start: 4, end: 5 }, payload: ParsedPayload::Code { @@ -422,7 +422,7 @@ mod tests { }, }, ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Paragraph, + kind: kebab_parse_types::ParsedBlockKind::Paragraph, heading_path: h1_b, source_span: SourceSpan::Line { start: 6, end: 6 }, payload: ParsedPayload::Paragraph { @@ -715,7 +715,7 @@ mod tests { fn audio_ref_block_skipped_with_warning() { let span = SourceSpan::Line { start: 1, end: 1 }; let blocks = vec![ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::AudioRef, + kind: kebab_parse_types::ParsedBlockKind::AudioRef, heading_path: vec![], source_span: span, payload: ParsedPayload::AudioRef { @@ -759,7 +759,7 @@ mod tests { let nfd_heading = "\u{1100}\u{1161}".to_string(); // 가 (NFD) let nfc_heading = "\u{AC00}".to_string(); // 가 (NFC) let mk_block = |heading: String| ParsedBlock { - kind: kb_parse_types::ParsedBlockKind::Paragraph, + kind: kebab_parse_types::ParsedBlockKind::Paragraph, heading_path: vec![heading], source_span: span.clone(), payload: ParsedPayload::Paragraph { diff --git a/crates/kb-normalize/tests/normalize_snapshot.rs b/crates/kebab-normalize/tests/normalize_snapshot.rs similarity index 97% rename from crates/kb-normalize/tests/normalize_snapshot.rs rename to crates/kebab-normalize/tests/normalize_snapshot.rs index ec10ddf..65b04f6 100644 --- a/crates/kb-normalize/tests/normalize_snapshot.rs +++ b/crates/kebab-normalize/tests/normalize_snapshot.rs @@ -15,12 +15,12 @@ use std::path::PathBuf; -use kb_core::{ +use kebab_core::{ AssetId, AssetStorage, Checksum, MediaType, ParserVersion, RawAsset, SourceUri, WorkspacePath, }; -use kb_normalize::build_canonical_document; -use kb_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; +use kebab_normalize::build_canonical_document; +use kebab_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; use serde_json::Value; use time::OffsetDateTime; diff --git a/crates/kb-parse-md/Cargo.toml b/crates/kebab-parse-md/Cargo.toml similarity index 92% rename from crates/kb-parse-md/Cargo.toml rename to crates/kebab-parse-md/Cargo.toml index 3b39606..912800a 100644 --- a/crates/kb-parse-md/Cargo.toml +++ b/crates/kebab-parse-md/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-parse-md" +name = "kebab-parse-md" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "Markdown frontmatter and block parsing into kb-core::Metadata / kb-parse-types intermediates" [dependencies] -kb-core = { path = "../kb-core" } -kb-parse-types = { path = "../kb-parse-types" } +kebab-core = { path = "../kebab-core" } +kebab-parse-types = { path = "../kebab-parse-types" } anyhow = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/kb-parse-md/src/blocks.rs b/crates/kebab-parse-md/src/blocks.rs similarity index 99% rename from crates/kb-parse-md/src/blocks.rs rename to crates/kebab-parse-md/src/blocks.rs index e2d7daa..19b5001 100644 --- a/crates/kb-parse-md/src/blocks.rs +++ b/crates/kebab-parse-md/src/blocks.rs @@ -1,10 +1,10 @@ -//! Markdown body → flat `Vec` (§3.4 / §3.7b). +//! Markdown body → flat `Vec` (§3.4 / §3.7b). //! //! Uses `pulldown-cmark` (with GFM tables enabled at runtime via //! `Options::ENABLE_TABLES`) to walk the body once and emit a flat list of //! parsed blocks. Heading paths are computed by tracking the most-recent //! heading text at each level. Source spans are reported as -//! [`kb_core::SourceSpan::Line`] in 1-indexed file-line coordinates by +//! [`kebab_core::SourceSpan::Line`] in 1-indexed file-line coordinates by //! converting `pulldown-cmark`'s byte offsets to line numbers and adding the //! caller-supplied `body_offset_lines`. //! @@ -19,10 +19,10 @@ //! //! ## Inline filter //! -//! [`kb_core::Inline`] only models `Text | Code | Link | Strong | Emph`. +//! [`kebab_core::Inline`] only models `Text | Code | Link | Strong | Emph`. //! Inline images, footnotes, hard breaks, etc. are dropped silently per //! design §3.4. Block-level `![alt](src)` (an image as the sole content of a -//! paragraph) is lifted to [`kb_parse_types::ParsedPayload::ImageRef`]. +//! paragraph) is lifted to [`kebab_parse_types::ParsedPayload::ImageRef`]. //! //! ## CRLF //! @@ -33,8 +33,8 @@ use std::ops::Range; -use kb_core::{Inline, SourceSpan}; -use kb_parse_types::{ParsedBlock, ParsedBlockKind, ParsedPayload, Warning, WarningKind}; +use kebab_core::{Inline, SourceSpan}; +use kebab_parse_types::{ParsedBlock, ParsedBlockKind, ParsedPayload, Warning, WarningKind}; use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, TagEnd}; /// Parse a Markdown body into a flat `Vec` plus any warnings. @@ -1595,7 +1595,7 @@ mod tests { let (blocks, _) = parse(body, 1); assert_eq!(blocks.len(), 1, "expected single list block"); match &blocks[0].kind { - kb_parse_types::ParsedBlockKind::List => {} + kebab_parse_types::ParsedBlockKind::List => {} other => panic!("expected list, got {other:?}"), } } diff --git a/crates/kb-parse-md/src/frontmatter.rs b/crates/kebab-parse-md/src/frontmatter.rs similarity index 99% rename from crates/kb-parse-md/src/frontmatter.rs rename to crates/kebab-parse-md/src/frontmatter.rs index 93230da..86d3f80 100644 --- a/crates/kb-parse-md/src/frontmatter.rs +++ b/crates/kebab-parse-md/src/frontmatter.rs @@ -1,4 +1,4 @@ -//! Markdown frontmatter parsing → `kb_core::Metadata`. +//! Markdown frontmatter parsing → `kebab_core::Metadata`. //! //! Implements the contract pinned in design §0 Q9 (frontmatter derive table) //! and §3.6 (Metadata shape). Produces structured warnings via @@ -18,8 +18,8 @@ use std::ops::Range; use std::sync::OnceLock; -use kb_core::{Metadata, SourceType, TrustLevel}; -use kb_parse_types::{Warning, WarningKind}; +use kebab_core::{Metadata, SourceType, TrustLevel}; +use kebab_parse_types::{Warning, WarningKind}; use lingua::{IsoCode639_1, Language, LanguageDetector, LanguageDetectorBuilder}; use serde::Deserialize; use serde_json::{Map, Value}; @@ -59,7 +59,7 @@ pub struct FrontmatterSpan { } /// Parse the frontmatter (if any) from a Markdown byte slice into a -/// `kb_core::Metadata`, applying the §0 Q9 derive table for missing fields. +/// `kebab_core::Metadata`, applying the §0 Q9 derive table for missing fields. /// /// On a malformed frontmatter the function still returns `Ok` — the /// frontmatter contents are discarded and the caller is told via a @@ -589,7 +589,7 @@ fn iso_code(lang: Language) -> &'static str { #[cfg(test)] mod tests { use super::*; - use kb_core::{ + use kebab_core::{ AssetId, WorkspacePath, ids::id_for_doc, versions::ParserVersion, diff --git a/crates/kb-parse-md/src/lib.rs b/crates/kebab-parse-md/src/lib.rs similarity index 100% rename from crates/kb-parse-md/src/lib.rs rename to crates/kebab-parse-md/src/lib.rs diff --git a/crates/kb-parse-md/tests/blocks_snapshots.rs b/crates/kebab-parse-md/tests/blocks_snapshots.rs similarity index 95% rename from crates/kb-parse-md/tests/blocks_snapshots.rs rename to crates/kebab-parse-md/tests/blocks_snapshots.rs index 1c569ee..2de7bee 100644 --- a/crates/kb-parse-md/tests/blocks_snapshots.rs +++ b/crates/kebab-parse-md/tests/blocks_snapshots.rs @@ -10,13 +10,13 @@ //! env-var pattern. Migrating kb-parse-md to the env-var style is out of //! scope; both styles are intentional for now. //! -//! Following the kb_core::Inline schema migration (struct-variant shape), +//! Following the kebab_core::Inline schema migration (struct-variant shape), //! `ParsedBlock` now serializes directly through serde — no projection //! shim is required. Inlines surface as structured objects, e.g. //! `[{"kind":"text","text":"…"},{"kind":"code","code":"…"}]`. -use kb_parse_md::parse_blocks; -use kb_parse_types::{ParsedBlock, Warning}; +use kebab_parse_md::parse_blocks; +use kebab_parse_types::{ParsedBlock, Warning}; use serde::Serialize; use serde_json::Value; use std::fs; diff --git a/crates/kb-parse-md/tests/frontmatter_snapshots.rs b/crates/kebab-parse-md/tests/frontmatter_snapshots.rs similarity index 96% rename from crates/kb-parse-md/tests/frontmatter_snapshots.rs rename to crates/kebab-parse-md/tests/frontmatter_snapshots.rs index 84c6bcc..da22976 100644 --- a/crates/kb-parse-md/tests/frontmatter_snapshots.rs +++ b/crates/kebab-parse-md/tests/frontmatter_snapshots.rs @@ -5,7 +5,7 @@ //! and therefore stable; lingua autodetect over our fixtures is also //! stable for the language set we configured. -use kb_parse_md::{BodyHints, parse_frontmatter}; +use kebab_parse_md::{BodyHints, parse_frontmatter}; use serde::Serialize; use serde_json::Value; use std::fs; @@ -18,9 +18,9 @@ use time::macros::datetime; /// snapshot focuses on the §0 Q9 derive contract. #[derive(Serialize)] struct Snapshot { - metadata: kb_core::Metadata, + metadata: kebab_core::Metadata, span_present: bool, - warnings: Vec, + warnings: Vec, } fn fixtures_dir() -> PathBuf { diff --git a/crates/kb-parse-types/Cargo.toml b/crates/kebab-parse-types/Cargo.toml similarity index 82% rename from crates/kb-parse-types/Cargo.toml rename to crates/kebab-parse-types/Cargo.toml index 6f79453..58bc504 100644 --- a/crates/kb-parse-types/Cargo.toml +++ b/crates/kebab-parse-types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-parse-types" +name = "kebab-parse-types" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,5 +8,5 @@ repository = { workspace = true } description = "Parser intermediate representations (no parser libs allowed)" [dependencies] -kb-core = { path = "../kb-core" } +kebab-core = { path = "../kebab-core" } serde = { workspace = true } diff --git a/crates/kb-parse-types/src/lib.rs b/crates/kebab-parse-types/src/lib.rs similarity index 92% rename from crates/kb-parse-types/src/lib.rs rename to crates/kebab-parse-types/src/lib.rs index e09016f..1b06c5c 100644 --- a/crates/kb-parse-types/src/lib.rs +++ b/crates/kebab-parse-types/src/lib.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; pub struct ParsedBlock { pub kind: ParsedBlockKind, pub heading_path: Vec, - pub source_span: kb_core::SourceSpan, + pub source_span: kebab_core::SourceSpan, pub payload: ParsedPayload, } @@ -36,11 +36,11 @@ pub enum ParsedPayload { }, Paragraph { text: String, - inlines: Vec, + inlines: Vec, }, List { ordered: bool, - items: Vec>, + items: Vec>, }, Code { lang: Option, @@ -52,7 +52,7 @@ pub enum ParsedPayload { }, Quote { text: String, - inlines: Vec, + inlines: Vec, }, ImageRef { src: String, diff --git a/crates/kb-rag/Cargo.toml b/crates/kebab-rag/Cargo.toml similarity index 70% rename from crates/kb-rag/Cargo.toml rename to crates/kebab-rag/Cargo.toml index c255008..7afcfad 100644 --- a/crates/kb-rag/Cargo.toml +++ b/crates/kebab-rag/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-rag" +name = "kebab-rag" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,11 +8,11 @@ repository = { workspace = true } description = "RAG pipeline: retrieve → gate → pack → generate → cite-validate" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-search = { path = "../kb-search" } -kb-llm = { path = "../kb-llm" } -kb-store-sqlite = { path = "../kb-store-sqlite" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-search = { path = "../kebab-search" } +kebab-llm = { path = "../kebab-llm" } +kebab-store-sqlite = { path = "../kebab-store-sqlite" } serde = { workspace = true } serde_json = { workspace = true } regex = { workspace = true } @@ -23,7 +23,7 @@ anyhow = { workspace = true } blake3 = { workspace = true } [dev-dependencies] -kb-llm = { path = "../kb-llm", features = ["mock"] } +kebab-llm = { path = "../kebab-llm", features = ["mock"] } tempfile = { workspace = true } rusqlite = { workspace = true } serde_json = { workspace = true } diff --git a/crates/kb-rag/src/lib.rs b/crates/kebab-rag/src/lib.rs similarity index 92% rename from crates/kb-rag/src/lib.rs rename to crates/kebab-rag/src/lib.rs index c019862..a883dae 100644 --- a/crates/kb-rag/src/lib.rs +++ b/crates/kebab-rag/src/lib.rs @@ -18,7 +18,7 @@ //! reachable via `Retriever`), `kb-embed*` (only via `Retriever`), //! `kb-llm-local` (only via `LanguageModel`), `kb-tui`, `kb-desktop`. -pub use kb_core::{Answer, AnswerCitation, AnswerRetrievalSummary, RefusalReason}; +pub use kebab_core::{Answer, AnswerCitation, AnswerRetrievalSummary, RefusalReason}; mod pipeline; diff --git a/crates/kb-rag/src/pipeline.rs b/crates/kebab-rag/src/pipeline.rs similarity index 98% rename from crates/kb-rag/src/pipeline.rs rename to crates/kebab-rag/src/pipeline.rs index 015d979..fe9fbe3 100644 --- a/crates/kb-rag/src/pipeline.rs +++ b/crates/kebab-rag/src/pipeline.rs @@ -33,13 +33,13 @@ use std::sync::Arc; use anyhow::{Context, Result}; -use kb_core::{ +use kebab_core::{ Answer, AnswerCitation, AnswerRetrievalSummary, Citation, FinishReason, GenerateRequest, LanguageModel, ModelRef, RefusalReason, Retriever, SearchFilters, SearchHit, SearchMode, SearchQuery, TokenChunk, TokenUsage, TraceId, }; -use kb_core::versions::PromptTemplateVersion; -use kb_store_sqlite::SqliteStore; +use kebab_core::versions::PromptTemplateVersion; +use kebab_store_sqlite::SqliteStore; use regex::Regex; use std::sync::OnceLock; use time::OffsetDateTime; @@ -86,7 +86,7 @@ pub struct AskOpts { /// Single-threaded RAG orchestrator. See module docs for the stage list. pub struct RagPipeline { - config: kb_config::Config, + config: kebab_config::Config, retriever: Arc, llm: Arc, docs: Arc, @@ -98,7 +98,7 @@ impl RagPipeline { /// `Arc`'d trait objects (kb-app builds them from config; tests /// inject mocks). pub fn new( - config: kb_config::Config, + config: kebab_config::Config, retriever: Arc, llm: Arc, docs: Arc, @@ -380,7 +380,7 @@ impl RagPipeline { for hit in hits { let chunk_full = - ::get_chunk(&self.docs, &hit.chunk_id) + ::get_chunk(&self.docs, &hit.chunk_id) .context("kb-rag: docs.get_chunk")?; let chunk_text = match chunk_full { Some(c) => c.text, @@ -542,7 +542,7 @@ impl RagPipeline { /// paths attach the configured embedding model so `kb explain` can /// later identify which embedder shaped the retrieval (even on /// refusals — see `refuse_score_gate`). -fn embedding_ref_for(mode: SearchMode, cfg: &kb_config::Config) -> Option { +fn embedding_ref_for(mode: SearchMode, cfg: &kebab_config::Config) -> Option { match mode { SearchMode::Lexical => None, SearchMode::Vector | SearchMode::Hybrid => Some(ModelRef { diff --git a/crates/kb-rag/tests/common/mod.rs b/crates/kebab-rag/tests/common/mod.rs similarity index 97% rename from crates/kb-rag/tests/common/mod.rs rename to crates/kebab-rag/tests/common/mod.rs index 1e6a3c1..95892c2 100644 --- a/crates/kb-rag/tests/common/mod.rs +++ b/crates/kebab-rag/tests/common/mod.rs @@ -14,12 +14,12 @@ use std::sync::Arc; -use kb_config::Config; -use kb_core::{ +use kebab_config::Config; +use kebab_core::{ ChunkerVersion, ChunkId, Citation, DocumentId, IndexVersion, RetrievalDetail, Retriever, SearchHit, SearchMode, SearchQuery, WorkspacePath, }; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use rusqlite::params; use tempfile::TempDir; @@ -176,7 +176,7 @@ impl Retriever for MockRetriever { } } -/// Pad a short prefix to the 32-hex shape `kb_core` newtypes expect. +/// Pad a short prefix to the 32-hex shape `kebab_core` newtypes expect. pub fn id32(prefix: &str) -> String { let mut s = prefix.to_string(); while s.len() < 32 { diff --git a/crates/kb-rag/tests/pipeline.rs b/crates/kebab-rag/tests/pipeline.rs similarity index 99% rename from crates/kb-rag/tests/pipeline.rs rename to crates/kebab-rag/tests/pipeline.rs index cd49da6..874cbf9 100644 --- a/crates/kb-rag/tests/pipeline.rs +++ b/crates/kebab-rag/tests/pipeline.rs @@ -10,11 +10,11 @@ use std::sync::Arc; use std::sync::atomic::Ordering; use common::{MockRetriever, RagEnv, id32, mk_hit}; -use kb_core::{ +use kebab_core::{ FinishReason, LanguageModel, Retriever, SearchMode, TokenChunk, TokenUsage, }; -use kb_llm::MockLanguageModel; -use kb_rag::{AskOpts, RagPipeline, RefusalReason}; +use kebab_llm::MockLanguageModel; +use kebab_rag::{AskOpts, RagPipeline, RefusalReason}; /// LM ID used everywhere — kept short so snapshots stay stable. const TEST_LM_ID: &str = "mock-lm"; @@ -49,7 +49,7 @@ impl CountingLm { } impl LanguageModel for CountingLm { - fn model_ref(&self) -> kb_core::ModelRef { + fn model_ref(&self) -> kebab_core::ModelRef { self.inner.model_ref() } fn context_tokens(&self) -> usize { @@ -57,7 +57,7 @@ impl LanguageModel for CountingLm { } fn generate_stream( &self, - req: kb_core::GenerateRequest, + req: kebab_core::GenerateRequest, ) -> anyhow::Result> + Send>> { self.calls.fetch_add(1, Ordering::SeqCst); self.inner.generate_stream(req) diff --git a/crates/kb-search/Cargo.toml b/crates/kebab-search/Cargo.toml similarity index 79% rename from crates/kb-search/Cargo.toml rename to crates/kebab-search/Cargo.toml index b5a8295..f195b81 100644 --- a/crates/kb-search/Cargo.toml +++ b/crates/kebab-search/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-search" +name = "kebab-search" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,17 +8,17 @@ repository = { workspace = true } description = "Retriever implementations for kb (P2-2 lexical FTS5; P3 vector / hybrid will follow)" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } -kb-store-sqlite = { path = "../kb-store-sqlite" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } +kebab-store-sqlite = { path = "../kebab-store-sqlite" } # P3-4 hybrid retriever wraps a `dyn VectorStore` (typically backed by # `kb-store-vector::LanceVectorStore`) and a `dyn Embedder` (any P3-2 # adapter). Listed as a runtime dep so callers can construct # `VectorRetriever::new` against the trait objects without a concrete # adapter — the concrete adapter (`kb-embed-local`) stays out of this # crate per the spec's Forbidden deps list. -kb-store-vector = { path = "../kb-store-vector" } -kb-embed = { path = "../kb-embed" } +kebab-store-vector = { path = "../kebab-store-vector" } +kebab-embed = { path = "../kebab-embed" } rusqlite = { workspace = true } globset = { workspace = true } serde_json = { workspace = true } @@ -32,4 +32,4 @@ tempfile = { workspace = true } # feature) and stand up a real `LanceVectorStore` on a tmp directory. # The mock-retriever unit tests (the bulk of the hybrid suite) do not # need either, but the integration / snapshot lane does. -kb-embed = { path = "../kb-embed", features = ["mock"] } +kebab-embed = { path = "../kebab-embed", features = ["mock"] } diff --git a/crates/kb-search/src/citation_helper.rs b/crates/kebab-search/src/citation_helper.rs similarity index 95% rename from crates/kb-search/src/citation_helper.rs rename to crates/kebab-search/src/citation_helper.rs index d0c0068..3001640 100644 --- a/crates/kb-search/src/citation_helper.rs +++ b/crates/kebab-search/src/citation_helper.rs @@ -1,4 +1,4 @@ -//! Shared helpers for building `kb_core::Citation` values from a +//! Shared helpers for building `kebab_core::Citation` values from a //! chunk's first `SourceSpan`. //! //! Both the lexical and vector retrievers join against the same @@ -9,7 +9,7 @@ //! §1.6). Living here means a future PDF / image / audio extractor can //! enrich the mapping in one place rather than two. -use kb_core::{Citation, SourceSpan, WorkspacePath}; +use kebab_core::{Citation, SourceSpan, WorkspacePath}; /// Build a `Citation` from the chunk's first `SourceSpan`. P1 markdown /// only emits `Line`, so the other variants are mostly defensive — we diff --git a/crates/kb-search/src/hybrid.rs b/crates/kebab-search/src/hybrid.rs similarity index 99% rename from crates/kb-search/src/hybrid.rs rename to crates/kebab-search/src/hybrid.rs index 5741566..e1c036b 100644 --- a/crates/kb-search/src/hybrid.rs +++ b/crates/kebab-search/src/hybrid.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; use std::sync::Arc; use anyhow::Result; -use kb_core::{ +use kebab_core::{ IndexVersion, RetrievalDetail, Retriever, SearchHit, SearchMode, SearchQuery, }; @@ -75,7 +75,7 @@ impl HybridRetriever { /// retrievers. Reads `config.search.hybrid_fusion` (only `"rrf"` /// is recognised today) and `config.search.rrf_k`. pub fn new( - config: &kb_config::Config, + config: &kebab_config::Config, lexical: Arc, vector: Arc, ) -> Self { @@ -335,7 +335,7 @@ fn parse_fusion(name: &str, k_rrf: u32) -> FusionPolicy { #[cfg(test)] mod tests { use super::*; - use kb_core::{ + use kebab_core::{ ChunkId, ChunkerVersion, Citation, DocumentId, IndexVersion, SearchFilters, SearchHit, SearchMode, WorkspacePath, }; diff --git a/crates/kb-search/src/lexical.rs b/crates/kebab-search/src/lexical.rs similarity index 98% rename from crates/kb-search/src/lexical.rs rename to crates/kebab-search/src/lexical.rs index 0912348..71d32c6 100644 --- a/crates/kb-search/src/lexical.rs +++ b/crates/kebab-search/src/lexical.rs @@ -1,7 +1,7 @@ //! Lexical (FTS5 + bm25) retriever — design §3.7 / §1.5 / §2.2 / §6.4. //! //! Owns the SQL pattern documented in `tasks/p2/p2-2-lexical-retriever.md` -//! and constructs `kb_core::SearchHit` values directly from the joined +//! and constructs `kebab_core::SearchHit` values directly from the joined //! `chunks_fts` / `chunks` / `documents` rows. Reads only — never mutates //! the underlying SQLite file. @@ -9,12 +9,12 @@ use std::sync::Arc; use anyhow::{Context, Result}; use globset::GlobMatcher; -use kb_core::{ +use kebab_core::{ ChunkId, ChunkerVersion, DocumentId, IndexVersion, RetrievalDetail, Retriever, SearchFilters, SearchHit, SearchMode, SearchQuery, SourceSpan, TrustLevel, WorkspacePath, }; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use rusqlite::{params_from_iter, Connection, Row, ToSql}; use crate::citation_helper::citation_from_first_span; @@ -57,7 +57,7 @@ impl LexicalRetriever { /// Construct with default settings derived from `kb-config`'s defaults. /// Snippet width is computed from `Config::defaults().search.snippet_chars`. pub fn new(store: Arc, index_version: IndexVersion) -> Self { - let cfg = kb_config::Config::defaults(); + let cfg = kebab_config::Config::defaults(); Self::with_settings(store, index_version, cfg.search.snippet_chars) } @@ -297,7 +297,7 @@ fn run_query( params.push(Box::new(lang.0.clone())); } if let Some(trust_min) = &filters.trust_min { - // Mirror `kb_store_sqlite::documents::list_documents` ranking: + // Mirror `kebab_store_sqlite::documents::list_documents` ranking: // Generated < Secondary < Primary. Doing the rank in SQL // (rather than post-filtering) keeps the row stream short // when the workspace contains many low-trust docs. @@ -523,7 +523,7 @@ mod tests { #[test] fn build_citation_line_round_trip() { - use kb_core::Citation; + use kebab_core::Citation; let p = WorkspacePath::new("a/b.md".to_string()).unwrap(); let span = SourceSpan::Line { start: 7, end: 12 }; let c = citation_from_first_span("c1", p.clone(), Some("S1".to_string()), Some(&span)); @@ -545,7 +545,7 @@ mod tests { #[test] fn build_citation_page_forwards_section() { - use kb_core::Citation; + use kebab_core::Citation; let p = WorkspacePath::new("doc.pdf".to_string()).unwrap(); let span = SourceSpan::Page { page: 4, @@ -568,7 +568,7 @@ mod tests { #[test] fn build_citation_none_falls_back_to_line_one() { - use kb_core::Citation; + use kebab_core::Citation; let p = WorkspacePath::new("x.md".to_string()).unwrap(); let c = citation_from_first_span("c1", p, None, None); match c { diff --git a/crates/kb-search/src/lib.rs b/crates/kebab-search/src/lib.rs similarity index 94% rename from crates/kb-search/src/lib.rs rename to crates/kebab-search/src/lib.rs index 63708b6..47f832d 100644 --- a/crates/kb-search/src/lib.rs +++ b/crates/kebab-search/src/lib.rs @@ -1,4 +1,4 @@ -//! `kb-search` — `kb_core::Retriever` implementations. +//! `kb-search` — `kebab_core::Retriever` implementations. //! //! - [`LexicalRetriever`] (P2-2): SQLite-FTS5 + bm25 backed retriever //! for `SearchMode::Lexical`. diff --git a/crates/kb-search/src/vector.rs b/crates/kebab-search/src/vector.rs similarity index 98% rename from crates/kb-search/src/vector.rs rename to crates/kebab-search/src/vector.rs index 8fdd0f9..77ff2a1 100644 --- a/crates/kb-search/src/vector.rs +++ b/crates/kebab-search/src/vector.rs @@ -1,7 +1,7 @@ //! Vector retriever — design §3.7 / §7.2 / §1.6. //! //! Wraps a `dyn VectorStore` + `dyn Embedder` + the SQLite metadata -//! store into a `kb_core::Retriever`. The vector store knows how to +//! store into a `kebab_core::Retriever`. The vector store knows how to //! find the nearest chunks by cosine on the embedding column; SQLite //! owns the human-readable metadata (heading_path / section_label / //! source_spans / chunker_version / workspace_path) needed for @@ -19,12 +19,12 @@ use std::collections::HashMap; use std::sync::Arc; use anyhow::{Context, Result}; -use kb_core::{ +use kebab_core::{ ChunkId, ChunkerVersion, DocumentId, Embedder, EmbeddingInput, EmbeddingKind, IndexVersion, RetrievalDetail, Retriever, SearchHit, SearchMode, SearchQuery, SourceSpan, VectorHit, VectorStore, WorkspacePath, }; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use rusqlite::params_from_iter; use crate::citation_helper::citation_from_first_span; @@ -67,7 +67,7 @@ impl VectorRetriever { sqlite: Arc, index_version: IndexVersion, ) -> Self { - let cfg = kb_config::Config::defaults(); + let cfg = kebab_config::Config::defaults(); Self::with_settings(store, embed, sqlite, index_version, cfg.search.snippet_chars) } @@ -268,7 +268,7 @@ fn build_hit( meta: &ChunkMeta, rank: u32, index_version: &IndexVersion, - model_id: &kb_core::EmbeddingModelId, + model_id: &kebab_core::EmbeddingModelId, snippet_chars: usize, ) -> Result { let heading_path: Vec = serde_json::from_str(&meta.heading_path_json) diff --git a/crates/kb-search/tests/common/mod.rs b/crates/kebab-search/tests/common/mod.rs similarity index 96% rename from crates/kb-search/tests/common/mod.rs rename to crates/kebab-search/tests/common/mod.rs index 5ff9db8..69b87bd 100644 --- a/crates/kb-search/tests/common/mod.rs +++ b/crates/kebab-search/tests/common/mod.rs @@ -16,15 +16,15 @@ use std::sync::Arc; -use kb_config::Config; -use kb_core::{ +use kebab_config::Config; +use kebab_core::{ ChunkId, DocumentId, EmbeddingId, EmbeddingInput, EmbeddingKind, EmbeddingModelId, EmbeddingVersion, IndexVersion, VectorRecord, VectorStore, }; -use kb_embed::{Embedder, MockEmbedder}; -use kb_search::{LexicalRetriever, VectorRetriever}; -use kb_store_sqlite::SqliteStore; -use kb_store_vector::LanceVectorStore; +use kebab_embed::{Embedder, MockEmbedder}; +use kebab_search::{LexicalRetriever, VectorRetriever}; +use kebab_store_sqlite::SqliteStore; +use kebab_store_vector::LanceVectorStore; use rusqlite::params; use tempfile::TempDir; @@ -205,7 +205,7 @@ impl HybridEnv { } } -/// Pad a short prefix to the 32-hex shape `kb_core` newtypes expect. +/// Pad a short prefix to the 32-hex shape `kebab_core` newtypes expect. pub fn id32(prefix: &str) -> String { let mut s = prefix.to_string(); while s.len() < 32 { diff --git a/crates/kb-search/tests/fixtures/search/hybrid/run-1.json b/crates/kebab-search/tests/fixtures/search/hybrid/run-1.json similarity index 100% rename from crates/kb-search/tests/fixtures/search/hybrid/run-1.json rename to crates/kebab-search/tests/fixtures/search/hybrid/run-1.json diff --git a/crates/kb-search/tests/fixtures/search/lexical/run-1.json b/crates/kebab-search/tests/fixtures/search/lexical/run-1.json similarity index 100% rename from crates/kb-search/tests/fixtures/search/lexical/run-1.json rename to crates/kebab-search/tests/fixtures/search/lexical/run-1.json diff --git a/crates/kb-search/tests/hybrid.rs b/crates/kebab-search/tests/hybrid.rs similarity index 99% rename from crates/kb-search/tests/hybrid.rs rename to crates/kebab-search/tests/hybrid.rs index b9279a6..af62bfc 100644 --- a/crates/kb-search/tests/hybrid.rs +++ b/crates/kebab-search/tests/hybrid.rs @@ -14,10 +14,10 @@ use std::sync::Arc; use common::{ HybridEnv, id32, require_avx_or_panic, TEST_LEX_INDEX_VERSION, TEST_VEC_INDEX_VERSION, }; -use kb_core::{ +use kebab_core::{ Retriever, SearchFilters, SearchHit, SearchMode, SearchQuery, }; -use kb_search::{FusionPolicy, HybridRetriever}; +use kebab_search::{FusionPolicy, HybridRetriever}; use serde_json::json; fn build_hybrid(env: &HybridEnv) -> HybridRetriever { diff --git a/crates/kb-search/tests/lexical.rs b/crates/kebab-search/tests/lexical.rs similarity index 98% rename from crates/kb-search/tests/lexical.rs rename to crates/kebab-search/tests/lexical.rs index 91a2b71..4cb98be 100644 --- a/crates/kb-search/tests/lexical.rs +++ b/crates/kebab-search/tests/lexical.rs @@ -7,10 +7,10 @@ use std::sync::Arc; -use kb_config::Config; -use kb_core::{IndexVersion, Lang, Retriever, SearchFilters, SearchMode, SearchQuery, TrustLevel}; -use kb_search::LexicalRetriever; -use kb_store_sqlite::SqliteStore; +use kebab_config::Config; +use kebab_core::{IndexVersion, Lang, Retriever, SearchFilters, SearchMode, SearchQuery, TrustLevel}; +use kebab_search::LexicalRetriever; +use kebab_store_sqlite::SqliteStore; use rusqlite::Connection; use tempfile::TempDir; @@ -143,7 +143,7 @@ fn insert_chunk( .expect("insert chunk"); } -/// Pad a short ID to the 32-hex shape kb_core newtypes expect. +/// Pad a short ID to the 32-hex shape kebab_core newtypes expect. fn id32(prefix: &str) -> String { let mut s = prefix.to_string(); while s.len() < 32 { @@ -237,7 +237,7 @@ fn lexical_single_doc_match_returns_one_hit_with_citation_round_trip() { // Citation round-trips through `to_uri`/`parse` (line variant). let uri = h.citation.to_uri(); - let parsed = kb_core::Citation::parse(&uri).expect("parse uri"); + let parsed = kebab_core::Citation::parse(&uri).expect("parse uri"); // Reparsed citation has section=None (URI fragment doesn't carry it), // so compare by `to_uri` equivalence rather than struct equality. assert_eq!(parsed.to_uri(), uri); @@ -457,7 +457,7 @@ fn lexical_citation_round_trip_against_first_source_span() { assert_eq!(hits.len(), 1); let uri = hits[0].citation.to_uri(); assert_eq!(uri, "notes/m.md#L12-L34"); - let parsed = kb_core::Citation::parse(&uri).unwrap(); + let parsed = kebab_core::Citation::parse(&uri).unwrap(); assert_eq!(parsed.to_uri(), uri); } diff --git a/crates/kb-source-fs/Cargo.toml b/crates/kebab-source-fs/Cargo.toml similarity index 84% rename from crates/kb-source-fs/Cargo.toml rename to crates/kebab-source-fs/Cargo.toml index 9274c3c..7429315 100644 --- a/crates/kb-source-fs/Cargo.toml +++ b/crates/kebab-source-fs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-source-fs" +name = "kebab-source-fs" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "Local filesystem SourceConnector — walks workspace.root + applies gitignore filters" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } anyhow = { workspace = true } serde = { workspace = true } time = { workspace = true } diff --git a/crates/kb-source-fs/src/connector.rs b/crates/kebab-source-fs/src/connector.rs similarity index 97% rename from crates/kb-source-fs/src/connector.rs rename to crates/kebab-source-fs/src/connector.rs index ed94bc9..f904395 100644 --- a/crates/kb-source-fs/src/connector.rs +++ b/crates/kebab-source-fs/src/connector.rs @@ -3,10 +3,10 @@ //! ```ignore //! pub struct FsSourceConnector { /* internal */ } //! impl FsSourceConnector { -//! pub fn new(config: &kb_config::Config) -> anyhow::Result; +//! pub fn new(config: &kebab_config::Config) -> anyhow::Result; //! } -//! impl kb_core::SourceConnector for FsSourceConnector { -//! fn scan(&self, scope: &kb_core::SourceScope) -> anyhow::Result>; +//! impl kebab_core::SourceConnector for FsSourceConnector { +//! fn scan(&self, scope: &kebab_core::SourceScope) -> anyhow::Result>; //! } //! ``` @@ -15,8 +15,8 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use time::OffsetDateTime; -use kb_config::Config; -use kb_core::{ +use kebab_config::Config; +use kebab_core::{ AssetStorage, Checksum, RawAsset, SourceConnector, SourceScope, SourceUri, id_for_asset, to_posix, }; @@ -194,7 +194,7 @@ fn dirs_home() -> Option { #[cfg(test)] mod tests { use super::*; - use kb_config::Config; + use kebab_config::Config; fn cfg_with_root(root: &str) -> Config { let mut c = Config::defaults(); @@ -353,7 +353,7 @@ mod tests { #[test] fn scan_emits_posix_normalized_paths() { // End-to-end: the connector must produce POSIX-normalized - // workspace paths via `kb_core::to_posix`. We can't construct an + // workspace paths via `kebab_core::to_posix`. We can't construct an // input with literal `./` / `//` segments via the filesystem (the // OS won't let us), so instead we assert the resulting strings // are already POSIX-clean (no leading `./`, no `//`, forward diff --git a/crates/kb-source-fs/src/hash.rs b/crates/kebab-source-fs/src/hash.rs similarity index 98% rename from crates/kb-source-fs/src/hash.rs rename to crates/kebab-source-fs/src/hash.rs index b2b40a1..6ab61d0 100644 --- a/crates/kb-source-fs/src/hash.rs +++ b/crates/kebab-source-fs/src/hash.rs @@ -7,7 +7,7 @@ //! - `full_hex` is the canonical lowercase hex (64 chars) of the full //! blake3 digest. The `kb-core::Checksum` invariant is "full hex"; the //! 32-char prefix is reserved for `AssetId` derivation via -//! `kb_core::id_for_asset`. +//! `kebab_core::id_for_asset`. use std::fs::File; use std::io::{self, Read}; diff --git a/crates/kb-source-fs/src/lib.rs b/crates/kebab-source-fs/src/lib.rs similarity index 100% rename from crates/kb-source-fs/src/lib.rs rename to crates/kebab-source-fs/src/lib.rs diff --git a/crates/kb-source-fs/src/media.rs b/crates/kebab-source-fs/src/media.rs similarity index 98% rename from crates/kb-source-fs/src/media.rs rename to crates/kebab-source-fs/src/media.rs index ecaf6e3..b2d8663 100644 --- a/crates/kb-source-fs/src/media.rs +++ b/crates/kebab-source-fs/src/media.rs @@ -5,7 +5,7 @@ use std::path::Path; -use kb_core::{AudioType, ImageType, MediaType}; +use kebab_core::{AudioType, ImageType, MediaType}; /// Return `MediaType` for `path` based purely on its lowercased extension. /// `.md` → Markdown, `.pdf` → Pdf, image and audio extensions map onto diff --git a/crates/kb-source-fs/src/walker.rs b/crates/kebab-source-fs/src/walker.rs similarity index 100% rename from crates/kb-source-fs/src/walker.rs rename to crates/kebab-source-fs/src/walker.rs diff --git a/crates/kb-source-fs/tests/snapshot_tree1.rs b/crates/kebab-source-fs/tests/snapshot_tree1.rs similarity index 97% rename from crates/kb-source-fs/tests/snapshot_tree1.rs rename to crates/kebab-source-fs/tests/snapshot_tree1.rs index 6eb0ed9..0431faf 100644 --- a/crates/kb-source-fs/tests/snapshot_tree1.rs +++ b/crates/kebab-source-fs/tests/snapshot_tree1.rs @@ -25,9 +25,9 @@ use std::path::PathBuf; -use kb_config::Config; -use kb_core::{SourceConnector, SourceScope}; -use kb_source_fs::FsSourceConnector; +use kebab_config::Config; +use kebab_core::{SourceConnector, SourceScope}; +use kebab_source_fs::FsSourceConnector; use serde_json::Value; /// Repo root, derived from `CARGO_MANIFEST_DIR` (= `crates/kb-source-fs`). diff --git a/crates/kb-source-fs/tests/symlink_cycle.rs b/crates/kebab-source-fs/tests/symlink_cycle.rs similarity index 98% rename from crates/kb-source-fs/tests/symlink_cycle.rs rename to crates/kebab-source-fs/tests/symlink_cycle.rs index bcd00c4..1bd8d50 100644 --- a/crates/kb-source-fs/tests/symlink_cycle.rs +++ b/crates/kebab-source-fs/tests/symlink_cycle.rs @@ -17,9 +17,9 @@ use std::os::unix::fs::symlink; -use kb_config::Config; -use kb_core::{SourceConnector, SourceScope}; -use kb_source_fs::FsSourceConnector; +use kebab_config::Config; +use kebab_core::{SourceConnector, SourceScope}; +use kebab_source_fs::FsSourceConnector; fn cfg_with_root(root: &str) -> Config { let mut c = Config::defaults(); diff --git a/crates/kb-store-sqlite/Cargo.toml b/crates/kebab-store-sqlite/Cargo.toml similarity index 85% rename from crates/kb-store-sqlite/Cargo.toml rename to crates/kebab-store-sqlite/Cargo.toml index 7569405..2cc17ed 100644 --- a/crates/kb-store-sqlite/Cargo.toml +++ b/crates/kebab-store-sqlite/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-store-sqlite" +name = "kebab-store-sqlite" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,8 +8,8 @@ repository = { workspace = true } description = "SQLite-backed DocumentStore + JobRepo for kb (§5 DDL, §7.2 traits)" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } # `bundled` ships SQLite source + builds in-tree (no system libsqlite3). # Explicitly NOT `bundled-sqlcipher` per task allowed-deps list. rusqlite = { version = "0.32", features = ["bundled"] } @@ -34,6 +34,6 @@ serde_json = { workspace = true } # test. Forbidden as regular deps per design §8 (store consumes domain # types from kb-core only); `cargo tree -p kb-store-sqlite --depth 1` # (default scope, excludes dev-deps) confirms this. -kb-parse-md = { path = "../kb-parse-md" } -kb-normalize = { path = "../kb-normalize" } -kb-chunk = { path = "../kb-chunk" } +kebab-parse-md = { path = "../kebab-parse-md" } +kebab-normalize = { path = "../kebab-normalize" } +kebab-chunk = { path = "../kebab-chunk" } diff --git a/crates/kb-store-sqlite/snapshots/ingest_report.snapshot.json b/crates/kebab-store-sqlite/snapshots/ingest_report.snapshot.json similarity index 100% rename from crates/kb-store-sqlite/snapshots/ingest_report.snapshot.json rename to crates/kebab-store-sqlite/snapshots/ingest_report.snapshot.json diff --git a/crates/kb-store-sqlite/src/answers.rs b/crates/kebab-store-sqlite/src/answers.rs similarity index 97% rename from crates/kb-store-sqlite/src/answers.rs rename to crates/kebab-store-sqlite/src/answers.rs index e559980..e4ef25d 100644 --- a/crates/kb-store-sqlite/src/answers.rs +++ b/crates/kebab-store-sqlite/src/answers.rs @@ -2,13 +2,13 @@ //! //! `kb-rag` always persists an `answers` row at the end of every //! `RagPipeline::ask` — including refusal paths (`NoChunks`, -//! `ScoreGate`, `LlmSelfJudge`). The trait `kb_core::DocumentStore` +//! `ScoreGate`, `LlmSelfJudge`). The trait `kebab_core::DocumentStore` //! does not surface this method (answers aren't documents); we add it //! as an inherent method on `SqliteStore` so kb-rag can call //! `self.docs.put_answer(...)` directly. use anyhow::{Context, Result}; -use kb_core::{Answer, RefusalReason, SearchMode}; +use kebab_core::{Answer, RefusalReason, SearchMode}; use rusqlite::params; use crate::error::StoreError; diff --git a/crates/kb-store-sqlite/src/documents.rs b/crates/kebab-store-sqlite/src/documents.rs similarity index 86% rename from crates/kb-store-sqlite/src/documents.rs rename to crates/kebab-store-sqlite/src/documents.rs index 59db371..b321e12 100644 --- a/crates/kb-store-sqlite/src/documents.rs +++ b/crates/kebab-store-sqlite/src/documents.rs @@ -18,10 +18,10 @@ use time::OffsetDateTime; use crate::error::StoreError; use crate::store::{SqliteStore, upsert_asset_row, validate_asset_id}; -impl kb_core::DocumentStore for SqliteStore { - fn put_asset(&self, asset: &kb_core::RawAsset) -> Result<()> { +impl kebab_core::DocumentStore for SqliteStore { + fn put_asset(&self, asset: &kebab_core::RawAsset) -> Result<()> { // Validate the AssetId shape before any row work — defense in - // depth against hand-constructed `kb_core::AssetId` values that + // depth against hand-constructed `kebab_core::AssetId` values that // bypass `FromStr`. See `validate_asset_id` for rationale. validate_asset_id(&asset.asset_id)?; // No bytes here — read storage_kind/storage_path from the @@ -30,10 +30,10 @@ impl kb_core::DocumentStore for SqliteStore { // this branch is for the case where bytes were already persisted // (or referenced) and we just want to record the row. let (storage_kind, storage_path) = match &asset.stored { - kb_core::AssetStorage::Copied { path } => { + kebab_core::AssetStorage::Copied { path } => { ("copied", path.to_string_lossy().into_owned()) } - kb_core::AssetStorage::Reference { path, .. } => { + kebab_core::AssetStorage::Reference { path, .. } => { ("reference", path.to_string_lossy().into_owned()) } }; @@ -41,7 +41,7 @@ impl kb_core::DocumentStore for SqliteStore { upsert_asset_row(&conn, asset, storage_kind, &storage_path) } - fn put_document(&self, doc: &kb_core::CanonicalDocument) -> Result<()> { + fn put_document(&self, doc: &kebab_core::CanonicalDocument) -> Result<()> { let mut conn = self.lock_conn(); let tx = conn.transaction().map_err(StoreError::from)?; upsert_document(&tx, doc)?; @@ -52,8 +52,8 @@ impl kb_core::DocumentStore for SqliteStore { fn put_blocks( &self, - doc: &kb_core::DocumentId, - blocks: &[kb_core::Block], + doc: &kebab_core::DocumentId, + blocks: &[kebab_core::Block], ) -> Result<()> { let mut conn = self.lock_conn(); let tx = conn.transaction().map_err(StoreError::from)?; @@ -95,8 +95,8 @@ impl kb_core::DocumentStore for SqliteStore { fn put_chunks( &self, - doc: &kb_core::DocumentId, - chunks: &[kb_core::Chunk], + doc: &kebab_core::DocumentId, + chunks: &[kebab_core::Chunk], ) -> Result<()> { let now = OffsetDateTime::now_utc() .format(&time::format_description::well_known::Rfc3339) @@ -149,8 +149,8 @@ impl kb_core::DocumentStore for SqliteStore { fn get_document( &self, - id: &kb_core::DocumentId, - ) -> Result> { + id: &kebab_core::DocumentId, + ) -> Result> { let conn = self.lock_conn(); let row: Option = conn .query_row( @@ -182,30 +182,30 @@ impl kb_core::DocumentStore for SqliteStore { Ok(payload_json) }) .map_err(StoreError::from)?; - let mut blocks: Vec = Vec::new(); + let mut blocks: Vec = Vec::new(); for row in block_rows { let payload_json = row.map_err(StoreError::from)?; - let block: kb_core::Block = serde_json::from_str(&payload_json) + let block: kebab_core::Block = serde_json::from_str(&payload_json) .context("deserialize block payload_json")?; blocks.push(block); } - let metadata: kb_core::Metadata = serde_json::from_str(&row.metadata_json) + let metadata: kebab_core::Metadata = serde_json::from_str(&row.metadata_json) .context("deserialize metadata_json")?; - let provenance: kb_core::Provenance = + let provenance: kebab_core::Provenance = serde_json::from_str(&row.provenance_json) .context("deserialize provenance_json")?; - Ok(Some(kb_core::CanonicalDocument { - doc_id: kb_core::DocumentId(row.doc_id), - source_asset_id: kb_core::AssetId(row.asset_id), - workspace_path: kb_core::WorkspacePath(row.workspace_path), + Ok(Some(kebab_core::CanonicalDocument { + doc_id: kebab_core::DocumentId(row.doc_id), + source_asset_id: kebab_core::AssetId(row.asset_id), + workspace_path: kebab_core::WorkspacePath(row.workspace_path), title: row.title.unwrap_or_default(), - lang: kb_core::Lang(row.lang.unwrap_or_default()), + lang: kebab_core::Lang(row.lang.unwrap_or_default()), blocks, metadata, provenance, - parser_version: kb_core::ParserVersion(row.parser_version), + parser_version: kebab_core::ParserVersion(row.parser_version), // INVARIANT: `doc_version` is bumped by 1 on every re-ingest // (see `upsert_document`). The column is INTEGER (i64) but // CanonicalDocument carries u32; an overflow would require @@ -217,7 +217,7 @@ impl kb_core::DocumentStore for SqliteStore { })) } - fn get_chunk(&self, id: &kb_core::ChunkId) -> Result> { + fn get_chunk(&self, id: &kebab_core::ChunkId) -> Result> { let conn = self.lock_conn(); let row = conn .query_row( @@ -235,29 +235,29 @@ impl kb_core::DocumentStore for SqliteStore { let Some(row) = row else { return Ok(None) }; let heading_path: Vec = serde_json::from_str(&row.heading_path_json) .context("deserialize chunk.heading_path_json")?; - let source_spans: Vec = + let source_spans: Vec = serde_json::from_str(&row.source_spans_json) .context("deserialize chunk.source_spans_json")?; - let block_ids: Vec = + let block_ids: Vec = serde_json::from_str(&row.block_ids_json) .context("deserialize chunk.block_ids_json")?; - Ok(Some(kb_core::Chunk { - chunk_id: kb_core::ChunkId(row.chunk_id), - doc_id: kb_core::DocumentId(row.doc_id), + Ok(Some(kebab_core::Chunk { + chunk_id: kebab_core::ChunkId(row.chunk_id), + doc_id: kebab_core::DocumentId(row.doc_id), block_ids, text: row.text, heading_path, source_spans, token_estimate: row.token_estimate as usize, - chunker_version: kb_core::ChunkerVersion(row.chunker_version), + chunker_version: kebab_core::ChunkerVersion(row.chunker_version), policy_hash: row.policy_hash, })) } fn list_documents( &self, - filter: &kb_core::DocFilter, - ) -> Result> { + filter: &kebab_core::DocFilter, + ) -> Result> { // Build a dynamic WHERE clause from the filter. Each condition // appends one positional `?` placeholder and one `Box` to `params` so order stays in sync. @@ -292,9 +292,9 @@ impl kb_core::DocumentStore for SqliteStore { ELSE 0 END >= ?"); let rank: i64 = match trust_min { - kb_core::TrustLevel::Primary => 3, - kb_core::TrustLevel::Secondary => 2, - kb_core::TrustLevel::Generated => 1, + kebab_core::TrustLevel::Primary => 3, + kebab_core::TrustLevel::Secondary => 2, + kebab_core::TrustLevel::Generated => 1, }; params_dyn.push(Box::new(rank)); } @@ -337,7 +337,7 @@ impl kb_core::DocumentStore for SqliteStore { let tags: Vec = tag_iter .collect::>>() .map_err(StoreError::from)?; - out.push(kb_core::DocSummary { tags, ..summary }); + out.push(kebab_core::DocSummary { tags, ..summary }); } Ok(out) } @@ -405,7 +405,7 @@ fn chunk_row_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result { }) } -fn doc_summary_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result { +fn doc_summary_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result { let doc_id: String = row.get(0)?; let workspace_path: String = row.get(1)?; let title: Option = row.get(2)?; @@ -421,10 +421,10 @@ fn doc_summary_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result) -> rusqlite::Result) -> rusqlite::Result(err: rusqlite::Error) -> rusqlite::Result> { /// UPSERT the documents row and bump `doc_version` on conflict. fn upsert_document( tx: &rusqlite::Transaction<'_>, - doc: &kb_core::CanonicalDocument, + doc: &kebab_core::CanonicalDocument, ) -> Result<()> { let metadata_json = serde_json::to_string(&doc.metadata) .context("serialize metadata")?; @@ -535,27 +535,27 @@ fn upsert_document( Ok(()) } -fn source_type_label(s: &kb_core::SourceType) -> &'static str { +fn source_type_label(s: &kebab_core::SourceType) -> &'static str { match s { - kb_core::SourceType::Markdown => "markdown", - kb_core::SourceType::Note => "note", - kb_core::SourceType::Paper => "paper", - kb_core::SourceType::Reference => "reference", - kb_core::SourceType::Inbox => "inbox", + kebab_core::SourceType::Markdown => "markdown", + kebab_core::SourceType::Note => "note", + kebab_core::SourceType::Paper => "paper", + kebab_core::SourceType::Reference => "reference", + kebab_core::SourceType::Inbox => "inbox", } } -fn trust_level_label(s: &kb_core::TrustLevel) -> &'static str { +fn trust_level_label(s: &kebab_core::TrustLevel) -> &'static str { match s { - kb_core::TrustLevel::Primary => "primary", - kb_core::TrustLevel::Secondary => "secondary", - kb_core::TrustLevel::Generated => "generated", + kebab_core::TrustLevel::Primary => "primary", + kebab_core::TrustLevel::Secondary => "secondary", + kebab_core::TrustLevel::Generated => "generated", } } fn replace_document_tags( tx: &rusqlite::Transaction<'_>, - doc_id: &kb_core::DocumentId, + doc_id: &kebab_core::DocumentId, tags: &[String], ) -> Result<()> { tx.execute("DELETE FROM document_tags WHERE doc_id = ?", params![doc_id.0]) @@ -586,23 +586,23 @@ struct BlockRow { } fn block_to_row( - doc: &kb_core::DocumentId, - block: &kb_core::Block, + doc: &kebab_core::DocumentId, + block: &kebab_core::Block, stream_ordinal: i64, ) -> Result { let (block_id, kind, heading_path_json, source_span_json) = match block { - kb_core::Block::Heading(b) => ( + kebab_core::Block::Heading(b) => ( b.common.block_id.0.clone(), "heading", serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::Paragraph(b) | kb_core::Block::Quote(b) => ( + kebab_core::Block::Paragraph(b) | kebab_core::Block::Quote(b) => ( b.common.block_id.0.clone(), // Discriminate Paragraph vs Quote on the enum tag: payload // round-trip carries the variant, but the column needs a // stable label for filtering. - if matches!(block, kb_core::Block::Paragraph(_)) { + if matches!(block, kebab_core::Block::Paragraph(_)) { "paragraph" } else { "quote" @@ -610,31 +610,31 @@ fn block_to_row( serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::List(b) => ( + kebab_core::Block::List(b) => ( b.common.block_id.0.clone(), "list", serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::Code(b) => ( + kebab_core::Block::Code(b) => ( b.common.block_id.0.clone(), "code", serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::Table(b) => ( + kebab_core::Block::Table(b) => ( b.common.block_id.0.clone(), "table", serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::ImageRef(b) => ( + kebab_core::Block::ImageRef(b) => ( b.common.block_id.0.clone(), "imageref", serde_json::to_string(&b.common.heading_path)?, serde_json::to_string(&b.common.source_span)?, ), - kb_core::Block::AudioRef(b) => ( + kebab_core::Block::AudioRef(b) => ( b.common.block_id.0.clone(), "audioref", serde_json::to_string(&b.common.heading_path)?, diff --git a/crates/kb-store-sqlite/src/embeddings.rs b/crates/kebab-store-sqlite/src/embeddings.rs similarity index 99% rename from crates/kb-store-sqlite/src/embeddings.rs rename to crates/kebab-store-sqlite/src/embeddings.rs index 3fd51f1..0ef76a2 100644 --- a/crates/kb-store-sqlite/src/embeddings.rs +++ b/crates/kebab-store-sqlite/src/embeddings.rs @@ -137,7 +137,7 @@ impl SqliteStore { #[cfg(test)] mod tests { use super::*; - use kb_config::Config; + use kebab_config::Config; use tempfile::TempDir; use time::OffsetDateTime; diff --git a/crates/kb-store-sqlite/src/error.rs b/crates/kebab-store-sqlite/src/error.rs similarity index 100% rename from crates/kb-store-sqlite/src/error.rs rename to crates/kebab-store-sqlite/src/error.rs diff --git a/crates/kb-store-sqlite/src/eval.rs b/crates/kebab-store-sqlite/src/eval.rs similarity index 99% rename from crates/kb-store-sqlite/src/eval.rs rename to crates/kebab-store-sqlite/src/eval.rs index 34a29b0..007e01f 100644 --- a/crates/kb-store-sqlite/src/eval.rs +++ b/crates/kebab-store-sqlite/src/eval.rs @@ -2,7 +2,7 @@ //! //! `kb-eval` calls these directly via the inherent methods on //! [`SqliteStore`]. The pattern mirrors [`crate::answers`]: the trait -//! `kb_core::DocumentStore` is the document surface, and run-level +//! `kebab_core::DocumentStore` is the document surface, and run-level //! audit rows (jobs, ingest_runs, answers, eval_runs) are inherent //! methods so the trait surface stays small. diff --git a/crates/kb-store-sqlite/src/filters.rs b/crates/kebab-store-sqlite/src/filters.rs similarity index 97% rename from crates/kb-store-sqlite/src/filters.rs rename to crates/kebab-store-sqlite/src/filters.rs index be56201..2b1ff00 100644 --- a/crates/kb-store-sqlite/src/filters.rs +++ b/crates/kebab-store-sqlite/src/filters.rs @@ -34,7 +34,7 @@ impl SqliteStore { /// The result preserves the input order so the caller can feed it /// back to a Lance distance-asc result list and `take(k)` directly. /// - /// `filters` semantics mirror `kb_core::SearchFilters`: + /// `filters` semantics mirror `kebab_core::SearchFilters`: /// /// - `tags_any`: doc must own at least one of the listed tags /// (empty vec ⇒ no tag constraint). @@ -52,9 +52,9 @@ impl SqliteStore { /// search callers (spec §5.6). pub fn filter_chunks( &self, - chunk_ids: &[kb_core::ChunkId], - filters: &kb_core::SearchFilters, - ) -> Result> { + chunk_ids: &[kebab_core::ChunkId], + filters: &kebab_core::SearchFilters, + ) -> Result> { if chunk_ids.is_empty() { return Ok(Vec::new()); } @@ -110,9 +110,9 @@ impl SqliteStore { ELSE 0 END >= ?", ); let rank: i64 = match min { - kb_core::TrustLevel::Primary => 3, - kb_core::TrustLevel::Secondary => 2, - kb_core::TrustLevel::Generated => 1, + kebab_core::TrustLevel::Primary => 3, + kebab_core::TrustLevel::Secondary => 2, + kebab_core::TrustLevel::Generated => 1, }; bind.push(Box::new(rank)); } @@ -187,8 +187,8 @@ impl SqliteStore { #[cfg(test)] mod tests { use super::*; - use kb_config::Config; - use kb_core::{ChunkId, Lang, SearchFilters, TrustLevel}; + use kebab_config::Config; + use kebab_core::{ChunkId, Lang, SearchFilters, TrustLevel}; use rusqlite::params; use tempfile::TempDir; use time::OffsetDateTime; diff --git a/crates/kb-store-sqlite/src/fts.rs b/crates/kebab-store-sqlite/src/fts.rs similarity index 100% rename from crates/kb-store-sqlite/src/fts.rs rename to crates/kebab-store-sqlite/src/fts.rs diff --git a/crates/kb-store-sqlite/src/jobs.rs b/crates/kebab-store-sqlite/src/jobs.rs similarity index 88% rename from crates/kb-store-sqlite/src/jobs.rs rename to crates/kebab-store-sqlite/src/jobs.rs index 745ffb7..dbeb4be 100644 --- a/crates/kb-store-sqlite/src/jobs.rs +++ b/crates/kebab-store-sqlite/src/jobs.rs @@ -85,12 +85,12 @@ impl SqliteStore { } } -impl kb_core::JobRepo for SqliteStore { +impl kebab_core::JobRepo for SqliteStore { fn create( &self, - kind: kb_core::JobKind, + kind: kebab_core::JobKind, payload: Value, - ) -> Result { + ) -> Result { let now_dt = OffsetDateTime::now_utc(); let now = now_dt .format(&time::format_description::well_known::Rfc3339) @@ -116,7 +116,7 @@ impl kb_core::JobRepo for SqliteStore { fn update_progress( &self, - id: &kb_core::JobId, + id: &kebab_core::JobId, progress: Value, ) -> Result<()> { let progress_json = serde_json::to_string(&progress) @@ -141,8 +141,8 @@ impl kb_core::JobRepo for SqliteStore { fn finish( &self, - id: &kb_core::JobId, - status: kb_core::JobStatus, + id: &kebab_core::JobId, + status: kebab_core::JobStatus, error: Option<&str>, ) -> Result<()> { let now = OffsetDateTime::now_utc() @@ -169,8 +169,8 @@ impl kb_core::JobRepo for SqliteStore { fn list( &self, - filter: &kb_core::JobFilter, - ) -> Result> { + filter: &kebab_core::JobFilter, + ) -> Result> { let conn = self.lock_conn(); let mut sql = String::from( "SELECT job_id, kind, status, payload_json, progress_json, @@ -204,13 +204,13 @@ impl kb_core::JobRepo for SqliteStore { } /// Mint a JobId over (kind, canonical(payload), nanos). The 32-hex -/// invariant on `kb_core::JobId` is honored by taking the first 32 chars +/// invariant on `kebab_core::JobId` is honored by taking the first 32 chars /// of the blake3 hex. fn mint_job_id( - kind: &kb_core::JobKind, + kind: &kebab_core::JobKind, payload: &Value, at: OffsetDateTime, -) -> kb_core::JobId { +) -> kebab_core::JobId { // Plain serde_json::to_vec is enough — JobIds are not part of the // §4.2 ID family and don't need canonical-JSON parity with other IDs. // The nanosecond suffix is what guarantees uniqueness, not stable @@ -222,32 +222,32 @@ fn mint_job_id( } hasher.update(&at.unix_timestamp_nanos().to_be_bytes()); let hex = hasher.finalize().to_hex().to_string(); - kb_core::JobId(hex[..32].to_string()) + kebab_core::JobId(hex[..32].to_string()) } -fn job_kind_label(k: &kb_core::JobKind) -> &'static str { +fn job_kind_label(k: &kebab_core::JobKind) -> &'static str { match k { - kb_core::JobKind::Ingest => "ingest", - kb_core::JobKind::Chunk => "chunk", - kb_core::JobKind::Embed => "embed", - kb_core::JobKind::Ocr => "ocr", - kb_core::JobKind::Transcribe => "transcribe", - kb_core::JobKind::Reindex => "reindex", - kb_core::JobKind::Doctor => "doctor", + kebab_core::JobKind::Ingest => "ingest", + kebab_core::JobKind::Chunk => "chunk", + kebab_core::JobKind::Embed => "embed", + kebab_core::JobKind::Ocr => "ocr", + kebab_core::JobKind::Transcribe => "transcribe", + kebab_core::JobKind::Reindex => "reindex", + kebab_core::JobKind::Doctor => "doctor", } } -fn job_status_label(s: &kb_core::JobStatus) -> &'static str { +fn job_status_label(s: &kebab_core::JobStatus) -> &'static str { match s { - kb_core::JobStatus::Pending => "pending", - kb_core::JobStatus::Running => "running", - kb_core::JobStatus::Succeeded => "succeeded", - kb_core::JobStatus::Failed => "failed", - kb_core::JobStatus::Canceled => "canceled", + kebab_core::JobStatus::Pending => "pending", + kebab_core::JobStatus::Running => "running", + kebab_core::JobStatus::Succeeded => "succeeded", + kebab_core::JobStatus::Failed => "failed", + kebab_core::JobStatus::Canceled => "canceled", } } -fn job_row_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result { +fn job_row_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result { let job_id: String = row.get(0)?; let kind_raw: String = row.get(1)?; let status_raw: String = row.get(2)?; @@ -258,10 +258,10 @@ fn job_row_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result = row.get(8)?; - let kind: kb_core::JobKind = + let kind: kebab_core::JobKind = serde_json::from_value(serde_json::Value::String(kind_raw)) .map_err(conv_err(1))?; - let status: kb_core::JobStatus = + let status: kebab_core::JobStatus = serde_json::from_value(serde_json::Value::String(status_raw)) .map_err(conv_err(2))?; let payload: Value = serde_json::from_str(&payload_json).map_err(conv_err(3))?; @@ -303,8 +303,8 @@ fn job_row_from_sql(row: &rusqlite::Row<'_>) -> rusqlite::Result None, }; - Ok(kb_core::JobRow { - job_id: kb_core::JobId(job_id), + Ok(kebab_core::JobRow { + job_id: kebab_core::JobId(job_id), kind, status, payload, diff --git a/crates/kb-store-sqlite/src/lib.rs b/crates/kebab-store-sqlite/src/lib.rs similarity index 94% rename from crates/kb-store-sqlite/src/lib.rs rename to crates/kebab-store-sqlite/src/lib.rs index 640e5b9..23290c1 100644 --- a/crates/kb-store-sqlite/src/lib.rs +++ b/crates/kebab-store-sqlite/src/lib.rs @@ -1,5 +1,5 @@ //! `kb-store-sqlite` — SQLite-backed implementations of -//! [`kb_core::DocumentStore`] and [`kb_core::JobRepo`] (§7.2), plus the +//! [`kebab_core::DocumentStore`] and [`kebab_core::JobRepo`] (§7.2), plus the //! asset writer that copies (or references) raw bytes per design §5.2. //! //! Schema is owned by `migrations/V001__init.sql` (workspace root), which diff --git a/crates/kb-store-sqlite/src/schema.rs b/crates/kebab-store-sqlite/src/schema.rs similarity index 100% rename from crates/kb-store-sqlite/src/schema.rs rename to crates/kebab-store-sqlite/src/schema.rs diff --git a/crates/kb-store-sqlite/src/store.rs b/crates/kebab-store-sqlite/src/store.rs similarity index 94% rename from crates/kb-store-sqlite/src/store.rs rename to crates/kebab-store-sqlite/src/store.rs index f0c1560..2ee3636 100644 --- a/crates/kb-store-sqlite/src/store.rs +++ b/crates/kebab-store-sqlite/src/store.rs @@ -21,7 +21,7 @@ use crate::schema; /// collide on `.tmp..`. static TEMP_SUFFIX_COUNTER: AtomicU64 = AtomicU64::new(0); -/// Length, in hex chars, of a valid `kb_core::AssetId`. blake3 first-half +/// Length, in hex chars, of a valid `kebab_core::AssetId`. blake3 first-half /// truncated, mirrored from `kb-core`'s newtype invariant. const ASSET_ID_HEX_LEN: usize = 32; @@ -63,8 +63,8 @@ impl SqliteStore { /// apply pragmas (foreign_keys / WAL / synchronous=NORMAL / /// temp_store=MEMORY), and create parent directories as needed. /// **Does not run migrations** — call [`Self::run_migrations`] next. - pub fn open(config: &kb_config::Config) -> Result { - let data_dir = kb_config::expand_path(&config.storage.data_dir, ""); + pub fn open(config: &kebab_config::Config) -> Result { + let data_dir = kebab_config::expand_path(&config.storage.data_dir, ""); std::fs::create_dir_all(&data_dir) .with_context(|| format!("create data_dir {}", data_dir.display()))?; let db_path = data_dir.join(SQLITE_FILE); @@ -135,10 +135,10 @@ impl SqliteStore { /// `StoreError::Conflict` wrapped in `anyhow::Error`. pub fn put_asset_with_bytes( &self, - asset: &kb_core::RawAsset, + asset: &kebab_core::RawAsset, bytes: &[u8], ) -> Result<()> { - // 0. Validate the AssetId shape before any I/O. `kb_core::AssetId` + // 0. Validate the AssetId shape before any I/O. `kebab_core::AssetId` // is a `pub String` newtype: `FromStr` enforces the 32-hex-char // invariant, but a hand-constructed `AssetId("../etc/passwd…")` // can bypass that and reach `assets_path_for`. Refuse such IDs at @@ -239,8 +239,8 @@ impl SqliteStore { // latter stores the raw `kb://...` string. No file I/O ⇒ no // orphan risk; just UPSERT the row. let storage_path = match &asset.source_uri { - kb_core::SourceUri::File(p) => p.to_string_lossy().into_owned(), - kb_core::SourceUri::Kb(u) => u.clone(), + kebab_core::SourceUri::File(p) => p.to_string_lossy().into_owned(), + kebab_core::SourceUri::Kb(u) => u.clone(), }; let conn = self.lock_conn(); upsert_asset_row(&conn, asset, "reference", &storage_path)?; @@ -255,7 +255,7 @@ impl SqliteStore { /// invoke [`validate_asset_id`] (already enforced at every store /// entry that takes a `RawAsset`). The `id.len() >= ASSET_SHARD_LEN` /// guard below is a defense-in-depth fallback only. - pub(crate) fn assets_path_for(&self, asset_id: &kb_core::AssetId) -> PathBuf { + pub(crate) fn assets_path_for(&self, asset_id: &kebab_core::AssetId) -> PathBuf { let id = &asset_id.0; let shard = if id.len() >= ASSET_SHARD_LEN { &id[..ASSET_SHARD_LEN] @@ -267,10 +267,10 @@ impl SqliteStore { } /// Reject an `AssetId` whose shape would let a malicious caller escape -/// the `data_dir/assets//` shard tree. `kb_core::AssetId(pub String)` +/// the `data_dir/assets//` shard tree. `kebab_core::AssetId(pub String)` /// permits hand-construction, so any function that turns an `AssetId` /// into a filesystem path must call this first. -pub(crate) fn validate_asset_id(asset_id: &kb_core::AssetId) -> Result<()> { +pub(crate) fn validate_asset_id(asset_id: &kebab_core::AssetId) -> Result<()> { if asset_id.0.len() != ASSET_ID_HEX_LEN || !asset_id.0.bytes().all(|b| b.is_ascii_hexdigit()) { @@ -304,13 +304,13 @@ fn temp_path_for(dest: &Path) -> PathBuf { /// reads `storage_kind/path` from `asset.stored`). pub(crate) fn upsert_asset_row( conn: &Connection, - asset: &kb_core::RawAsset, + asset: &kebab_core::RawAsset, storage_kind: &str, storage_path: &str, ) -> Result<()> { let source_uri = match &asset.source_uri { - kb_core::SourceUri::File(p) => format!("file://{}", p.to_string_lossy()), - kb_core::SourceUri::Kb(u) => u.clone(), + kebab_core::SourceUri::File(p) => format!("file://{}", p.to_string_lossy()), + kebab_core::SourceUri::Kb(u) => u.clone(), }; let media_type = serde_json::to_string(&asset.media_type) .context("serialize media_type")?; diff --git a/crates/kb-store-sqlite/tests/asset_writer.rs b/crates/kebab-store-sqlite/tests/asset_writer.rs similarity index 97% rename from crates/kb-store-sqlite/tests/asset_writer.rs rename to crates/kebab-store-sqlite/tests/asset_writer.rs index 43112a7..54f6268 100644 --- a/crates/kb-store-sqlite/tests/asset_writer.rs +++ b/crates/kebab-store-sqlite/tests/asset_writer.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; -use kb_core::{AssetId, AssetStorage, Checksum, MediaType, RawAsset, SourceUri, WorkspacePath}; -use kb_store_sqlite::SqliteStore; +use kebab_core::{AssetId, AssetStorage, Checksum, MediaType, RawAsset, SourceUri, WorkspacePath}; +use kebab_store_sqlite::SqliteStore; use time::OffsetDateTime; mod common; @@ -174,7 +174,7 @@ fn put_asset_with_bytes_orphan_cleanup_on_upsert_failure() { #[test] fn put_asset_with_bytes_rejects_invalid_asset_id() { - // `kb_core::AssetId(pub String)` lets a hand-construction bypass the + // `kebab_core::AssetId(pub String)` lets a hand-construction bypass the // 32-hex `FromStr` invariant. The store boundary must reject any ID // whose shape would let path construction escape `data_dir/assets/`. let env = common::TestEnv::with_threshold(100); diff --git a/crates/kb-store-sqlite/tests/common/mod.rs b/crates/kebab-store-sqlite/tests/common/mod.rs similarity index 98% rename from crates/kb-store-sqlite/tests/common/mod.rs rename to crates/kebab-store-sqlite/tests/common/mod.rs index 742ce0b..e65c18f 100644 --- a/crates/kb-store-sqlite/tests/common/mod.rs +++ b/crates/kebab-store-sqlite/tests/common/mod.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; -use kb_config::Config; +use kebab_config::Config; use rusqlite::Connection; use tempfile::TempDir; diff --git a/crates/kb-store-sqlite/tests/contract_roundtrip.rs b/crates/kebab-store-sqlite/tests/contract_roundtrip.rs similarity index 96% rename from crates/kb-store-sqlite/tests/contract_roundtrip.rs rename to crates/kebab-store-sqlite/tests/contract_roundtrip.rs index 2ea6080..ba56601 100644 --- a/crates/kb-store-sqlite/tests/contract_roundtrip.rs +++ b/crates/kebab-store-sqlite/tests/contract_roundtrip.rs @@ -8,14 +8,14 @@ use std::path::PathBuf; -use kb_chunk::MdHeadingV1Chunker; -use kb_core::{ +use kebab_chunk::MdHeadingV1Chunker; +use kebab_core::{ AssetId, AssetStorage, Checksum, ChunkPolicy, ChunkerVersion, Chunker, DocumentStore, MediaType, ParserVersion, RawAsset, SourceUri, WorkspacePath, }; -use kb_normalize::build_canonical_document; -use kb_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; -use kb_store_sqlite::SqliteStore; +use kebab_normalize::build_canonical_document; +use kebab_parse_md::{BodyHints, parse_blocks, parse_frontmatter}; +use kebab_store_sqlite::SqliteStore; use time::OffsetDateTime; mod common; diff --git a/crates/kb-store-sqlite/tests/fts.rs b/crates/kebab-store-sqlite/tests/fts.rs similarity index 99% rename from crates/kb-store-sqlite/tests/fts.rs rename to crates/kebab-store-sqlite/tests/fts.rs index 2b7faa7..350c16d 100644 --- a/crates/kb-store-sqlite/tests/fts.rs +++ b/crates/kebab-store-sqlite/tests/fts.rs @@ -13,7 +13,7 @@ //! that bypasses the `SqliteStore` mutex; that's fine because each test //! gets its own tempdir and no concurrent mutator is in flight. -use kb_store_sqlite::{SqliteStore, rebuild_chunks_fts}; +use kebab_store_sqlite::{SqliteStore, rebuild_chunks_fts}; use rusqlite::Connection; mod common; diff --git a/crates/kb-store-sqlite/tests/idempotency.rs b/crates/kebab-store-sqlite/tests/idempotency.rs similarity index 95% rename from crates/kb-store-sqlite/tests/idempotency.rs rename to crates/kebab-store-sqlite/tests/idempotency.rs index 56f45bd..2484852 100644 --- a/crates/kb-store-sqlite/tests/idempotency.rs +++ b/crates/kebab-store-sqlite/tests/idempotency.rs @@ -4,13 +4,13 @@ use std::path::PathBuf; -use kb_core::{ +use kebab_core::{ AssetId, AssetStorage, Block, CanonicalDocument, Checksum, Chunk, ChunkerVersion, CommonBlock, DocumentId, DocumentStore, HeadingBlock, Lang, MediaType, Metadata, ParserVersion, Provenance, RawAsset, SourceSpan, SourceType, SourceUri, TextBlock, TrustLevel, WorkspacePath, }; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use time::OffsetDateTime; mod common; @@ -50,7 +50,7 @@ fn make_doc() -> CanonicalDocument { let span = SourceSpan::Line { start: 1, end: 1 }; let block = Block::Heading(HeadingBlock { common: CommonBlock { - block_id: kb_core::BlockId("b".repeat(32)), + block_id: kebab_core::BlockId("b".repeat(32)), heading_path: vec![], source_span: span.clone(), }, @@ -59,7 +59,7 @@ fn make_doc() -> CanonicalDocument { }); let para = Block::Paragraph(TextBlock { common: CommonBlock { - block_id: kb_core::BlockId("c".repeat(32)), + block_id: kebab_core::BlockId("c".repeat(32)), heading_path: vec!["Title".into()], source_span: span, }, @@ -83,9 +83,9 @@ fn make_doc() -> CanonicalDocument { fn make_chunks(doc_id: &DocumentId) -> Vec { vec![Chunk { - chunk_id: kb_core::ChunkId("e".repeat(32)), + chunk_id: kebab_core::ChunkId("e".repeat(32)), doc_id: doc_id.clone(), - block_ids: vec![kb_core::BlockId("b".repeat(32))], + block_ids: vec![kebab_core::BlockId("b".repeat(32))], text: "Title\n\nbody".into(), heading_path: vec!["Title".into()], source_spans: vec![SourceSpan::Line { start: 1, end: 1 }], @@ -221,7 +221,7 @@ fn put_blocks_transactional_rollback_on_fk_violation() { let phantom = DocumentId("0".repeat(32)); let phantom_blocks = vec![Block::Heading(HeadingBlock { common: CommonBlock { - block_id: kb_core::BlockId("9".repeat(32)), + block_id: kebab_core::BlockId("9".repeat(32)), heading_path: vec![], source_span: SourceSpan::Line { start: 1, end: 1 }, }, diff --git a/crates/kb-store-sqlite/tests/ingest_report_snapshot.rs b/crates/kebab-store-sqlite/tests/ingest_report_snapshot.rs similarity index 97% rename from crates/kb-store-sqlite/tests/ingest_report_snapshot.rs rename to crates/kebab-store-sqlite/tests/ingest_report_snapshot.rs index 2e6a563..acfd72d 100644 --- a/crates/kb-store-sqlite/tests/ingest_report_snapshot.rs +++ b/crates/kebab-store-sqlite/tests/ingest_report_snapshot.rs @@ -1,4 +1,4 @@ -//! Snapshot test pinning the JSON wire form of `kb_core::IngestReport` +//! Snapshot test pinning the JSON wire form of `kebab_core::IngestReport` //! for an inline fixture run. The store crate doesn't (yet) write //! IngestReports — that's `kb-app`'s job — but the wire schema lives in //! `kb-core`, and we want a determinism pin that fails loudly if the @@ -8,7 +8,7 @@ use std::path::PathBuf; -use kb_core::{ +use kebab_core::{ AssetId, ChunkerVersion, DocumentId, IngestItem, IngestItemKind, IngestReport, ParserVersion, SourceScope, WorkspacePath, }; diff --git a/crates/kb-store-sqlite/tests/jobs.rs b/crates/kebab-store-sqlite/tests/jobs.rs similarity index 96% rename from crates/kb-store-sqlite/tests/jobs.rs rename to crates/kebab-store-sqlite/tests/jobs.rs index a5c5c32..7aa9381 100644 --- a/crates/kb-store-sqlite/tests/jobs.rs +++ b/crates/kebab-store-sqlite/tests/jobs.rs @@ -1,7 +1,7 @@ //! `JobRepo` smoke tests: create → progress → finish, list filters. -use kb_core::{JobFilter, JobKind, JobRepo, JobStatus}; -use kb_store_sqlite::SqliteStore; +use kebab_core::{JobFilter, JobKind, JobRepo, JobStatus}; +use kebab_store_sqlite::SqliteStore; use serde_json::json; mod common; diff --git a/crates/kb-store-sqlite/tests/list_docs.rs b/crates/kebab-store-sqlite/tests/list_docs.rs similarity index 97% rename from crates/kb-store-sqlite/tests/list_docs.rs rename to crates/kebab-store-sqlite/tests/list_docs.rs index f690f6b..53df3ac 100644 --- a/crates/kb-store-sqlite/tests/list_docs.rs +++ b/crates/kebab-store-sqlite/tests/list_docs.rs @@ -2,12 +2,12 @@ use std::path::PathBuf; -use kb_core::{ +use kebab_core::{ AssetId, AssetStorage, Block, CanonicalDocument, Checksum, CommonBlock, DocFilter, DocumentId, DocumentStore, HeadingBlock, Lang, MediaType, Metadata, ParserVersion, Provenance, RawAsset, SourceSpan, SourceType, SourceUri, TrustLevel, WorkspacePath, }; -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; use time::OffsetDateTime; mod common; @@ -38,7 +38,7 @@ fn make_doc( let doc_id = DocumentId(format!("d{suffix}").repeat(16)); let block = Block::Heading(HeadingBlock { common: CommonBlock { - block_id: kb_core::BlockId(format!("b{suffix}").repeat(16)), + block_id: kebab_core::BlockId(format!("b{suffix}").repeat(16)), heading_path: vec![], source_span: SourceSpan::Line { start: 1, end: 1 }, }, diff --git a/crates/kb-store-sqlite/tests/migration.rs b/crates/kebab-store-sqlite/tests/migration.rs similarity index 98% rename from crates/kb-store-sqlite/tests/migration.rs rename to crates/kebab-store-sqlite/tests/migration.rs index ad6ab54..c16c876 100644 --- a/crates/kb-store-sqlite/tests/migration.rs +++ b/crates/kebab-store-sqlite/tests/migration.rs @@ -1,7 +1,7 @@ //! Migration test: a fresh DB, after `run_migrations`, exposes every //! table and index P1 needs (per §5.1–§5.7). -use kb_store_sqlite::SqliteStore; +use kebab_store_sqlite::SqliteStore; mod common; diff --git a/crates/kb-store-vector/Cargo.toml b/crates/kebab-store-vector/Cargo.toml similarity index 93% rename from crates/kb-store-vector/Cargo.toml rename to crates/kebab-store-vector/Cargo.toml index 3bccdbc..d40ac0e 100644 --- a/crates/kb-store-vector/Cargo.toml +++ b/crates/kebab-store-vector/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kb-store-vector" +name = "kebab-store-vector" version = { workspace = true } edition = { workspace = true } rust-version = { workspace = true } @@ -8,13 +8,13 @@ repository = { workspace = true } description = "LanceDB-backed VectorStore for kb (§5.6 embedding_records, §6.3 lancedb tables, §7.2 VectorStore)" [dependencies] -kb-core = { path = "../kb-core" } -kb-config = { path = "../kb-config" } +kebab-core = { path = "../kebab-core" } +kebab-config = { path = "../kebab-config" } # kb-store-sqlite is allowed for the embedding_records writers only # (P3-3 spec: "Allowed dep `kb-store-sqlite` for writing/reading rows in # embedding_records"). The Two-phase upsert flow uses # `put_embedding_records_pending` + `mark_embedding_records_committed`. -kb-store-sqlite = { path = "../kb-store-sqlite" } +kebab-store-sqlite = { path = "../kebab-store-sqlite" } # LanceDB embedded vector store. `default-features=false` opts out of # the cloud object-store integrations (aws / gcs / azure / dynamodb / diff --git a/crates/kb-store-vector/src/arrow_batch.rs b/crates/kebab-store-vector/src/arrow_batch.rs similarity index 98% rename from crates/kb-store-vector/src/arrow_batch.rs rename to crates/kebab-store-vector/src/arrow_batch.rs index f17587b..1e1910e 100644 --- a/crates/kb-store-vector/src/arrow_batch.rs +++ b/crates/kebab-store-vector/src/arrow_batch.rs @@ -27,7 +27,7 @@ use arrow_array::{ TimestampMicrosecondArray, }; use arrow_schema::{DataType, Field, Schema, SchemaRef, TimeUnit}; -use kb_core::VectorRecord; +use kebab_core::VectorRecord; use time::OffsetDateTime; /// Arrow schema for a Lance table whose vector column is FixedSizeList @@ -184,7 +184,7 @@ fn descriptor_bytes(v: &serde_json::Value) -> Vec { #[cfg(test)] mod tests { use super::*; - use kb_core::{ChunkId, DocumentId, EmbeddingId, EmbeddingModelId, EmbeddingVersion}; + use kebab_core::{ChunkId, DocumentId, EmbeddingId, EmbeddingModelId, EmbeddingVersion}; use time::OffsetDateTime; fn make_rec(chunk_idx: u8, dim: usize) -> VectorRecord { diff --git a/crates/kb-store-vector/src/lib.rs b/crates/kebab-store-vector/src/lib.rs similarity index 94% rename from crates/kb-store-vector/src/lib.rs rename to crates/kebab-store-vector/src/lib.rs index 94c5f70..53b30a9 100644 --- a/crates/kb-store-vector/src/lib.rs +++ b/crates/kebab-store-vector/src/lib.rs @@ -1,4 +1,4 @@ -//! `kb-store-vector` — LanceDB-backed [`kb_core::VectorStore`] for kb. +//! `kb-store-vector` — LanceDB-backed [`kebab_core::VectorStore`] for kb. //! //! Stores per-model Lance tables under `config.storage.vector_dir/` //! (`chunk_embeddings__.lance/`). `upsert` runs the diff --git a/crates/kb-store-vector/src/paths.rs b/crates/kebab-store-vector/src/paths.rs similarity index 98% rename from crates/kb-store-vector/src/paths.rs rename to crates/kebab-store-vector/src/paths.rs index 40508f2..a236595 100644 --- a/crates/kb-store-vector/src/paths.rs +++ b/crates/kebab-store-vector/src/paths.rs @@ -4,7 +4,7 @@ //! `kb-store-sqlite`, `kb-embed-local`, and `kb-eval` all resolve //! `${XDG_DATA_HOME:-…}` / leading `~` / `{data_dir}` identically. This //! module re-exports nothing; consumers within the crate `use -//! kb_config::expand_path` directly. +//! kebab_config::expand_path` directly. /// Build the per-model Lance table name. Per design §6.3: /// `chunk_embeddings__.lance`. Model IDs may contain diff --git a/crates/kb-store-vector/src/store.rs b/crates/kebab-store-vector/src/store.rs similarity index 98% rename from crates/kb-store-vector/src/store.rs rename to crates/kebab-store-vector/src/store.rs index 99e3c6f..dbb857c 100644 --- a/crates/kb-store-vector/src/store.rs +++ b/crates/kebab-store-vector/src/store.rs @@ -1,4 +1,4 @@ -//! `LanceVectorStore` — `kb_core::VectorStore` impl over LanceDB. +//! `LanceVectorStore` — `kebab_core::VectorStore` impl over LanceDB. //! //! See module-level docs in `lib.rs` for the high-level shape (two-phase //! upsert, sync/async bridge, table layout). @@ -11,18 +11,18 @@ use anyhow::{Context, Result}; use arrow_array::{Array, Float32Array, RecordBatch, StringArray}; use arrow_schema::SchemaRef; use futures::TryStreamExt; -use kb_core::{ +use kebab_core::{ ChunkId, DocumentId, EmbeddingModelId, IndexId, SearchFilters, VectorHit, VectorRecord, VectorStore, }; -use kb_store_sqlite::{EmbeddingRecordRow, SqliteStore}; +use kebab_store_sqlite::{EmbeddingRecordRow, SqliteStore}; use lancedb::Connection; use lancedb::query::{ExecutableQuery, QueryBase}; use serde_json::json; use time::OffsetDateTime; use tokio::runtime::{Builder as RuntimeBuilder, Runtime}; -use kb_config::expand_path; +use kebab_config::expand_path; use crate::arrow_batch::{build_batch, schema_for, schema_params_hash}; use crate::paths::lance_table_name; @@ -87,7 +87,7 @@ impl LanceVectorStore { /// runtime context will panic with `"Cannot start a runtime from /// within a runtime"`. See the struct-level `# Async context` /// section. - pub fn new(config: &kb_config::Config, sqlite: Arc) -> Result { + pub fn new(config: &kebab_config::Config, sqlite: Arc) -> Result { let data_dir = expand_path(&config.storage.data_dir, ""); let vector_dir = expand_path(&config.storage.vector_dir, &data_dir.to_string_lossy()); @@ -194,11 +194,11 @@ impl VectorStore for LanceVectorStore { })?; let params_hash = schema_params_hash(dim); - let id = kb_core::id_for_index( + let id = kebab_core::id_for_index( INDEX_COLLECTION, model, dim, - &kb_core::IndexVersion(INDEX_VERSION.to_string()), + &kebab_core::IndexVersion(INDEX_VERSION.to_string()), INDEX_KIND, ¶ms_hash, ); diff --git a/crates/kb-store-vector/tests/common/mod.rs b/crates/kebab-store-vector/tests/common/mod.rs similarity index 98% rename from crates/kb-store-vector/tests/common/mod.rs rename to crates/kebab-store-vector/tests/common/mod.rs index 00031b1..a05b68c 100644 --- a/crates/kb-store-vector/tests/common/mod.rs +++ b/crates/kebab-store-vector/tests/common/mod.rs @@ -59,12 +59,12 @@ pub fn require_avx_or_panic() { } } -use kb_config::Config; -use kb_core::{ +use kebab_config::Config; +use kebab_core::{ ChunkId, DocumentId, EmbeddingId, EmbeddingModelId, EmbeddingVersion, VectorRecord, }; -use kb_store_sqlite::SqliteStore; -use kb_store_vector::LanceVectorStore; +use kebab_store_sqlite::SqliteStore; +use kebab_store_vector::LanceVectorStore; use rusqlite::params; use tempfile::TempDir; diff --git a/crates/kb-store-vector/tests/fixtures/vector/run-1.json b/crates/kebab-store-vector/tests/fixtures/vector/run-1.json similarity index 100% rename from crates/kb-store-vector/tests/fixtures/vector/run-1.json rename to crates/kebab-store-vector/tests/fixtures/vector/run-1.json diff --git a/crates/kb-store-vector/tests/snapshot.rs b/crates/kebab-store-vector/tests/snapshot.rs similarity index 98% rename from crates/kb-store-vector/tests/snapshot.rs rename to crates/kebab-store-vector/tests/snapshot.rs index 5ac7e0d..1d3d9e1 100644 --- a/crates/kb-store-vector/tests/snapshot.rs +++ b/crates/kebab-store-vector/tests/snapshot.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; -use kb_core::{SearchFilters, VectorStore}; +use kebab_core::{SearchFilters, VectorStore}; use serde_json::json; mod common; diff --git a/crates/kb-store-vector/tests/upsert_search.rs b/crates/kebab-store-vector/tests/upsert_search.rs similarity index 98% rename from crates/kb-store-vector/tests/upsert_search.rs rename to crates/kebab-store-vector/tests/upsert_search.rs index 4e16f4c..2dab53b 100644 --- a/crates/kb-store-vector/tests/upsert_search.rs +++ b/crates/kebab-store-vector/tests/upsert_search.rs @@ -11,8 +11,8 @@ //! //! See `tests/common/mod.rs` for the full rationale. -use kb_core::{EmbeddingModelId, SearchFilters, VectorStore}; -use kb_store_sqlite::EmbeddingRecordRow; +use kebab_core::{EmbeddingModelId, SearchFilters, VectorStore}; +use kebab_store_sqlite::EmbeddingRecordRow; use rusqlite::params; use time::OffsetDateTime; @@ -221,7 +221,7 @@ fn model_isolation_two_models_two_directories() { // Same chunk_id, different model — should land in a separate table. let mut r2 = make_record(0xaa, 0xaa, dir(0), "alpha", &[], "model-B"); - r2.embedding_id = kb_core::EmbeddingId( + r2.embedding_id = kebab_core::EmbeddingId( "ee01ee01ee01ee01ee01ee01ee01ee01".to_string(), ); env.vector.upsert(&[r2]).unwrap(); diff --git a/tasks/phase-5-evaluation.md b/tasks/phase-5-evaluation.md index bf57c39..4688132 100644 --- a/tasks/phase-5-evaluation.md +++ b/tasks/phase-5-evaluation.md @@ -1,7 +1,7 @@ --- phase: P5 title: "Golden query / regression eval" -status: planned +status: completed depends_on: [P4] source: kb_local_rust_report.md §17 Phase 5, §18 ---