diff --git a/AGENTS.md b/AGENTS.md index 9997559..d7cda62 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,83 +1,153 @@ -# AGENTS.md — features/cleanup +# AGENTS.md — feature/index-list-fix ## Goal -Remove stale planning documents and old benchmark results that have no value -for contributors or users of the released codebase. These directories were -useful during development but are now clutter. +Fix `codesearch index list` so it actually lists all registered repositories +from `~/.codesearch/repos.json` instead of only checking the current directory. -## Scope +The command currently has two `TODO` comments and prints almost nothing useful +for a user who has registered repos via `serve` or `codesearch index add`. -This branch touches **only** file deletions — no source code, no Cargo.toml, -no tests. `cargo check` is not required (no Rust changes). +## Why this matters ---- +A user who downloads a release runs `codesearch index list` to discover what +is registered. Today they get an empty output unless they happen to be standing +in a registered repo. This is the primary "what do I have?" entry point and it +must work. -## Tasks +## Files to change -### 1. Delete `.docs/` directory (entire tree) +- `src/index/mod.rs` — replace the body of `pub async fn list()` -Remove all files under `.docs/` including the `done/` subdirectory: +No other files need changes. No new dependencies. + +## Required behaviour ``` -.docs/MCP_HELP_SYSTEM.md -.docs/opencode-reload-commands-pr.md -.docs/plan-implementation.md -.docs/plan-testing.md -.docs/done/benchmarks-improvement-plan.md -.docs/done/codesearch-improvement-plan.md -.docs/done/LMDBResilience_GitAware_IndexCompact.md -.docs/done/old-plan-review.md -.docs/done/old-testplan.md -.docs/done/plan-embedding-cache.md -.docs/done/plan-review.md -``` +$ codesearch index list +📚 Indexed Repositories +============================================================ -### 2. Delete `benchmarks/` directory (entire tree) + codesearch-git \\?\C:\WorkArea\AI\codesearch\codesearch.git + 1727 chunks in 54 files -Remove all files under `benchmarks/`: + husq-aprimo \\?\C:\Users\develterf\source\repos\HUSQ.Aprimo + Could not open database (locked by serve) -``` -benchmarks/benchmark-20251124-232718.md -benchmarks/benchmark-20251124-234722.md -benchmarks/benchmark-20251125-103111.md -benchmarks/benchmark-20251125-103719.md -benchmarks/benchmark-20251125-104204.md -benchmarks/BGE-small-en-v1.5.md -benchmarks/demongrep_vs_osgrep.md -benchmarks/external_repo_bat.md -benchmarks/FULL_BENCHMARK_SUMMARY.md -benchmarks/improvement-plan.md -benchmarks/mcp-tool-description-improvements.md -benchmarks/test_external_repo.sh -``` + investing C:\WorkArea\AI\investing + 2369 chunks in 227 files -### 3. Commit + ... (one entry per registered repo, sorted alphabetically by alias) +12 repositories registered. ``` -git rm -r .docs benchmarks -git commit -m "chore: remove stale planning docs and old benchmark results" -git push origin features/cleanup + +If the current directory has a `.codesearch.db` that is **not** registered in +`repos.json`, append a separate "Local (unregistered)" section at the end so +the user sees their loose DB too. + +If `repos.json` does not exist or is empty, print `No repositories registered.` +and continue to the local-DB check. + +## Implementation + +Use `crate::db_discovery::repos::ReposConfig` (already imported elsewhere in +`src/index/mod.rs`). It exposes: + +- `ReposConfig::load() -> Result` — reads `~/.codesearch/repos.json` +- `config.repos: HashMap` — alias → project path + +Pseudocode: + +```rust +pub async fn list() -> Result<()> { + use crate::db_discovery::repos::ReposConfig; + + println!("{}", "📚 Indexed Repositories".bright_cyan().bold()); + println!("{}", "=".repeat(60)); + + let config = ReposConfig::load().unwrap_or_default(); + + if config.repos.is_empty() { + println!("\n No repositories registered."); + } else { + let mut entries: Vec<_> = config.repos.iter().collect(); + entries.sort_by(|a, b| a.0.cmp(b.0)); + + for (alias, project_path) in &entries { + println!(); + println!(" {}", alias.bright_green()); + let db_path = project_path.join(".codesearch.db"); + print_repo_stats(project_path, &db_path)?; + } + + println!(); + println!("{} repositories registered.", entries.len()); + } + + // Also show a loose local DB if the user is standing in one + let current_dir = std::env::current_dir()?; + let current_db = current_dir.join(".codesearch.db"); + let current_alias = config.alias_for_path(¤t_dir); + + if current_db.exists() && current_alias.is_none() { + println!(); + println!("{}", "Local (unregistered):".bright_yellow()); + print_repo_stats(¤t_dir, ¤t_db)?; + } + + Ok(()) +} ``` -### 4. Update CHANGELOG.md +Notes: +- `print_repo_stats` already handles "could not open database" gracefully + (returns the dimmed message). Don't change it. +- `alias_for_path` already exists on `ReposConfig` (chunk 1751 in serve_hub + index — see `src/db_discovery/repos.rs:221`). +- Remove both `TODO` comments — they are now resolved. +- Keep the `#[allow(dead_code)]` on `print_repo_stats` removed if you can — + it's now actively used. If clippy complains, leave the attribute. -Add a line under a new `## [Unreleased]` section — or add to the next release -section if one already exists: +## Quality gates + +- [ ] `cargo check` clean +- [ ] `cargo clippy --all-targets --all-features -- -D warnings` clean +- [ ] `cargo test --lib --bins` — all tests pass (no test changes expected) +- [ ] Manual: `codesearch index list` prints all 12+ registered aliases +- [ ] Manual: standing in a registered repo, that alias is shown (not duplicated + as "Local (unregistered)") +- [ ] Manual: standing in a directory with a stale `.codesearch.db` not in + `repos.json`, it appears under "Local (unregistered)" + +## CHANGELOG + +Add under a new `## [1.0.82] - 2026-05-02` section (or whatever version the +hook bumps to): ```markdown -### Removed +### Fixed -- Stale planning documents (`.docs/`) and old benchmark results (`benchmarks/`) - removed from the repository. These were internal working documents with no - value for contributors. +- `codesearch index list` now actually lists all repositories registered in + `~/.codesearch/repos.json` instead of only checking the current directory. + A loose `.codesearch.db` in an unregistered directory is shown separately + under "Local (unregistered)". ``` ---- +## Branch flow + +When done: + +```powershell +git push origin feature/index-list-fix +# then from claude.ai or similar: open PR feature/index-list-fix → develop +# merge, then run release.ps1 in C:\WorkArea\AI\codesearch +``` ## Done when -- [ ] `.docs/` directory no longer exists in the repository -- [ ] `benchmarks/` directory no longer exists in the repository -- [ ] Commit on `features/cleanup` pushed to origin +- [ ] `pub async fn list()` rewritten and both TODOs removed +- [ ] Quality gates pass +- [ ] Manual smoke tests pass - [ ] CHANGELOG.md updated +- [ ] PR opened against `develop` diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fdf8ce..d794315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +<<<<<<< HEAD +## [1.0.81] - 2026-05-02 + +### Fixed + +- **Idle eviction now covers warmed-but-never-queried repos**: `warmup_repo` + starts the idle timer at warmup so background-warmed aliases (KRKA.Aprimo, + DPS, BAYR.Aprimo and others showing `Last Tool Call = -` indefinitely) are + evicted by the idle reaper instead of holding LMDB envs and embedder state + forever. +- **Ctrl-C no longer quits the TUI**: crossterm's raw mode delivers Ctrl-C as + a key event, bypassing the OS-level handler. Treating it as quit was a + foot-gun: a stray Ctrl-C in the wrong terminal would tear down the whole + serve. Use `q` only. + +### Changed + +- **`unsafe` blocks documented**: SAFETY comments added to the three LMDB + env-open `unsafe` blocks in `src/embed/cache.rs` and `src/vectordb/store.rs`. +======= +## [1.0.82] - 2026-05-02 + +### Fixed + +- `codesearch index list` now actually lists all repositories registered in + `~/.codesearch/repos.json` instead of only checking the current directory. + A loose `.codesearch.db` in an unregistered directory is shown separately + under "Local (unregistered)". Both TODO markers removed. +>>>>>>> b5319c2eb68c6d26c72710aab2ff8d3a36cdef85 + ## [1.0.77] - 2026-05-01 ### Removed diff --git a/Cargo.lock b/Cargo.lock index 00954d4..484a72f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -634,7 +634,7 @@ dependencies = [ [[package]] name = "codesearch" -version = "1.0.81" +version = "1.0.83" dependencies = [ "anyhow", "arroy", diff --git a/Cargo.toml b/Cargo.toml index 686e623..db7ddb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codesearch" -version = "1.0.81" +version = "1.0.83" edition = "2021" authors = ["codesearch contributors"] license = "Apache-2.0" diff --git a/src/index/mod.rs b/src/index/mod.rs index 651dd70..169dbd5 100644 --- a/src/index/mod.rs +++ b/src/index/mod.rs @@ -1024,26 +1024,43 @@ async fn index_with_options( } /// List all indexed repositories -#[allow(dead_code)] // Reserved for 'list' command implementation +#[allow(dead_code)] pub async fn list() -> Result<()> { + use crate::db_discovery::repos::ReposConfig; + println!("{}", "📚 Indexed Repositories".bright_cyan().bold()); println!("{}", "=".repeat(60)); - // TODO: Scan all repositories in ~/.codesearch/repos.json - // For now just check current directory + let config = ReposConfig::load().unwrap_or_default(); + + if config.repos.is_empty() { + println!("\n No repositories registered."); + } else { + let mut entries: Vec<_> = config.repos.iter().collect(); + entries.sort_by(|a, b| a.0.cmp(b.0)); + + for (alias, project_path) in &entries { + println!(); + println!(" {}", alias.bright_green()); + let db_path = project_path.join(".codesearch.db"); + print_repo_stats(project_path, &db_path)?; + } + + println!(); + println!(" {} repositories registered.", entries.len()); + } - // Check current directory + // Show a loose local DB if the user is standing in one that is not registered let current_dir = std::env::current_dir()?; let current_db = current_dir.join(".codesearch.db"); + let current_alias = config.alias_for_path(¤t_dir); - if current_db.exists() { - println!("\n{}", "Current Directory:".bright_green()); + if current_db.exists() && current_alias.is_none() { + println!(); + println!("{}", "Local (unregistered):".bright_yellow()); print_repo_stats(¤t_dir, ¤t_db)?; } - // TODO: Track indexed repositories globally in ~/.codesearch/repos.json - // For now, just show current directory - Ok(()) } @@ -1425,50 +1442,51 @@ pub async fn remove_from_index( Ok(()) } -/// Show index status (local or global) +/// Show index status — lists all repos registered in repos.json plus any +/// loose local DB in the current directory that is not yet registered. pub async fn list_index_status() -> Result<()> { - println!("{}", "📋 Index Status".bright_cyan().bold()); - println!("{}", "=".repeat(60)); + use crate::db_discovery::repos::ReposConfig; - // Try to find the database - let db_info = find_best_database(Some(Path::new(".")))?; + println!("{}", "📚 Indexed Repositories".bright_cyan().bold()); + println!("{}", "=".repeat(60)); - if let Some(db) = db_info { - println!("\n{}", "💾 Database:".cyan()); - println!(" Path: {}", db.db_path.display()); + let config = ReposConfig::load().unwrap_or_default(); - if db.is_global { - println!(" Type: {}", "Global".bright_green()); - } else { - println!(" Type: {}", "Local".bright_green()); + if config.repos.is_empty() { + println!("\n No repositories registered."); + println!("\n Register one with:"); + println!(" codesearch index add # register current directory"); + println!(" codesearch index add # register a specific path"); + } else { + let mut entries: Vec<_> = config.repos.iter().collect(); + entries.sort_by(|a, b| a.0.cmp(b.0)); + + for (alias, project_path) in &entries { + println!(); + println!(" {}", alias.bright_green()); + let db_path = project_path.join(".codesearch.db"); + print_repo_stats(project_path, &db_path)?; } - // Show if this is from a parent directory - if !db.is_current && !db.is_global { - println!(" {}", "(from parent directory)".dimmed()); - } + println!(); + println!(" {} repositories registered.", entries.len()); + } - // Get stats - if let Ok(stats) = get_db_stats(&db.db_path).await { - println!(" Status: {}", "✅ Indexed".green()); - println!(" Chunks: {}", stats.chunk_count); - println!(" Size: {:.2} MB", stats.size_mb); - if stats.bloat_ratio.map(|r| r > 0.0).unwrap_or(false) { - println!(" Bloat Ratio: {:.2}%", stats.bloat_ratio.unwrap_or(0.0)); - } - } else { - println!(" Status: {}", "⚠️ Could not read database".yellow()); - } - } else { - println!("\n{}", "No index found for this project.".dimmed()); - println!("\nCreate an index with:"); - println!(" codesearch index add # Create local index"); - println!(" codesearch index add -g # Create global index"); + // Show a loose local DB if the user is standing in one that is not registered + let current_dir = std::env::current_dir()?; + let current_db = current_dir.join(".codesearch.db"); + let current_alias = config.alias_for_path(¤t_dir); + + if current_db.exists() && current_alias.is_none() { + println!(); + println!("{}", "Local (unregistered):".bright_yellow()); + print_repo_stats(¤t_dir, ¤t_db)?; + println!(" Register with: codesearch index add"); } Ok(()) } - +#[allow(dead_code)] async fn get_db_stats(db_path: &Path) -> Result { use crate::vectordb::VectorStore; @@ -1506,6 +1524,7 @@ async fn get_db_stats(db_path: &Path) -> Result { }) } +#[allow(dead_code)] struct DbStats { chunk_count: usize, size_mb: f64,