Skip to content

Commit 1ca2a04

Browse files
committed
feat(ck-core): add CK_INDEX_DIR to relocate index directories out of the source tree
By default ck stores each search root's index in an in-tree `.ck/` directory. That is convenient but not always wanted: it clutters repositories, can't be shared between multiple checkouts of the same tree, and is awkward to cache in CI. When `CK_INDEX_DIR` is set, the index for a search root instead lives at `$CK_INDEX_DIR/<basename>-<hash>`, where `<hash>` is the first 8 hex characters of the blake3 hash of the root's absolute path so two roots that share a basename never collide. Unset preserves the existing `<root>/.ck` behavior exactly. Introduce two documented helpers in ck-core: `index_dir(root)` returns the index/sidecar directory for a search root and `index_exists(root)` reports whether it exists. Both the relocation base and the root are absolutized, so the returned path is independent of the current directory (a relative CK_INDEX_DIR is anchored at the launch directory rather than resolved per-process). The environment variable is read on every call (an empty value is treated as unset) and never panics. Every call site that computed `<root>/.ck` — across ck-index, ck-engine, ck-cli, ck-tui, and ck-core's own sidecar and PDF content-cache paths, including the walk-ups that probe for a nearby index — now routes through these helpers. The `.ck` default-exclude pattern and sidecar file extensions are unchanged. Because an 8-hex hash can in principle collide, a relocated index directory records its search root in a `root_path` marker file; indexing refuses to write into a directory claimed by a different root, and searches refuse to serve one, surfacing a clear error instead of silently returning another root's results. Tests covering env-dependent paths are serialized with serial_test (already used elsewhere in ck) and clear the variable so they exercise default behavior deterministically. Documented under "Index Storage" in the README.
1 parent d2cfe11 commit 1ca2a04

15 files changed

Lines changed: 449 additions & 78 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,23 @@ project/
420420

421421
The `.ck/` directory is a cache — safe to delete and rebuild anytime.
422422

423+
#### Relocating the index (`CK_INDEX_DIR`)
424+
425+
Set the `CK_INDEX_DIR` environment variable to keep indexes out of your source
426+
tree entirely. Each search root is then indexed under
427+
`$CK_INDEX_DIR/<basename>-<hash>`, where `<hash>` is derived from the root's
428+
absolute path so roots that share a name don't collide:
429+
430+
```bash
431+
export CK_INDEX_DIR="$HOME/.cache/ck"
432+
ck --index ~/code/project # index lives in ~/.cache/ck/project-<hash>/
433+
```
434+
435+
This is handy for keeping repositories free of in-tree `.ck/` directories,
436+
caching indexes in CI, or sharing one index location across multiple checkouts
437+
of the same tree. When `CK_INDEX_DIR` is unset, indexes are stored in `.ck/` as
438+
above.
439+
423440
## 🧪 Testing
424441

425442
```bash

ck-cli/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ async fn run_index_workflow(
513513
let exclude_patterns = build_exclude_patterns(cli);
514514

515515
if clean_first {
516-
let index_dir = path.join(".ck");
516+
let index_dir = ck_core::index_dir(path);
517517
if index_dir.exists() {
518518
let spinner = status.create_spinner("Removing existing index...");
519519
ck_index::clean_index(path)?;
@@ -978,7 +978,7 @@ async fn run_cli_mode(cli: Cli) -> Result<()> {
978978
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
979979

980980
if !cli.force {
981-
let manifest_path = path.join(".ck").join("manifest.json");
981+
let manifest_path = ck_core::index_dir(&path).join("manifest.json");
982982
if manifest_path.exists()
983983
&& let Ok(data) = std::fs::read(&manifest_path)
984984
&& let Ok(manifest) = serde_json::from_slice::<ck_index::IndexManifest>(&data)
@@ -1143,7 +1143,7 @@ async fn run_cli_mode(cli: Cli) -> Result<()> {
11431143
});
11441144

11451145
// Add model information if available
1146-
let manifest_path = status_path.join(".ck").join("manifest.json");
1146+
let manifest_path = ck_core::index_dir(&status_path).join("manifest.json");
11471147
if let Ok(data) = std::fs::read(&manifest_path)
11481148
&& let Ok(manifest) = serde_json::from_slice::<ck_index::IndexManifest>(&data)
11491149
&& let Some(model_name) = manifest.embedding_model
@@ -1183,7 +1183,7 @@ async fn run_cli_mode(cli: Cli) -> Result<()> {
11831183
status.info(&format!(" Total chunks: {}", stats.total_chunks));
11841184
status.info(&format!(" Embedded chunks: {}", stats.embedded_chunks));
11851185

1186-
let manifest_path = status_path.join(".ck").join("manifest.json");
1186+
let manifest_path = ck_core::index_dir(&status_path).join("manifest.json");
11871187
if let Ok(data) = std::fs::read(&manifest_path)
11881188
&& let Ok(manifest) = serde_json::from_slice::<ck_index::IndexManifest>(&data)
11891189
&& let Some(model_name) = manifest.embedding_model

ck-cli/src/mcp_server.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,7 +1650,7 @@ impl CkMcpServer {
16501650
let _guard = lock.lock().await;
16511651

16521652
// Check if index exists and get stats
1653-
let index_path = path_buf.join(".ck");
1653+
let index_path = ck_core::index_dir(&path_buf);
16541654
let index_exists = index_path.exists();
16551655

16561656
let mut index_info = json!({
@@ -1703,7 +1703,7 @@ impl CkMcpServer {
17031703
index_info["index_size_bytes"] = json!(stats.index_size_bytes);
17041704

17051705
// Add model information if available
1706-
let manifest_path = path_buf.join(".ck").join("manifest.json");
1706+
let manifest_path = ck_core::index_dir(&path_buf).join("manifest.json");
17071707
if let Ok(data) = std::fs::read(&manifest_path)
17081708
&& let Ok(manifest) = serde_json::from_slice::<ck_index::IndexManifest>(&data)
17091709
&& let Some(model_name) = manifest.embedding_model

0 commit comments

Comments
 (0)