Semantic code search for CLI-driven agent workflows. Index your codebases, search with natural language, get precise results.
Built for local agent workflows in Claude Code and similar CLI environments.
LLMs work better with the right context. Grep finds text; this finds meaning. Ask for "authentication middleware" and get the actual auth logic, not every file that mentions "auth".
How it works:
- Index your codebase with tree-sitter AST parsing (functions, classes, methods — not arbitrary line splits)
- Embed chunks with Voyage AI (
voyage-4-largefor documents,voyage-4-litefor queries — same embedding space, asymmetric retrieval) - Search with PostgreSQL/pgvector + pgvectorscale by default when using
.env.example(or embedded LanceDB/SQLite alternatives), thenrerank-2.5for precision - Query indexed content from the CLI with semantic search commands
Agent / CLI
│
├── cc2.sh
└── uv run code-context-manage
│
RetrievalPipeline
│ │
▼ ▼
Voyage AI local vector store
voyage-4-lite Postgres/pgvector
(query embed) LanceDB/SQLite fallback
rerank-2.5
(reranking)
Retrieval pipeline:
- Embed query with
voyage-4-lite(fast, shared space with indexed docs) - Retrieve candidates from embedded LanceDB, experimental SQLite + FTS5 + sqlite-vec (
CC2_CODE_BACKEND=sqlite), or PostgreSQL/pgvector (CC2_CODE_BACKEND=postgres) - Rerank:
rerank-2.5+ relative threshold (max(score_floor, top_score * factor)) - Dedup: overlap/containment + Jaccard similarity filtering
- Return: Markdown-formatted chunks with file paths, line numbers, relevance scores
Code layout:
src/code_context/retrieval/— retrieval facade plus focused helpers for intent resolution, result controls, cross-file context, and quality loggingsrc/code_context/db/—DatabasePoolfacade delegating to domain-specific stores for code, projects, memory, and bookssrc/code_context/chunking/— tree-sitter parsing, chunk models, and post-processing/splitting helperssrc/code_context/indexing/— filesystem and indexing support helpers used byIndexersrc/code_context/cli/— user-facing entrypoints plus shared runtime/search/sync/watcher helpers
The public APIs stay centered on RetrievalPipeline, DatabasePool, Indexer, and the CLI entrypoints; the internal modules are split to keep those surfaces stable while reducing coupling.
- uv (Python package manager)
- Voyage AI API key (free tier available)
- Docker only if you opt into PostgreSQL-backed memory/books/legacy code search
git clone https://github.com/YOUR_USER/code-context-v2.git
cd code-context-v2
cp .env.example .env
# Edit .env — set CC2_VOYAGE_API_KEYThe recommended local backend is PostgreSQL/pgvector + pgvectorscale via Docker (CC2_CODE_BACKEND=postgres), using the compose service on 127.0.0.1:25432. Embedded LanceDB remains available as a no-Docker fallback, and experimental SQLite + FTS5 + sqlite-vec is available with CC2_CODE_BACKEND=sqlite.
PostgreSQL data is stored in the external Docker volume code-context-pgdata, mounted at the image's active PGDATA path (/home/postgres/pgdata/data). This protects the cc2 index from normal compose lifecycle commands, including docker compose down -v. Do not remove the volume with docker volume rm code-context-pgdata unless you intentionally want to delete the index. Run scripts/backup_cc2_postgres.sh to create a compressed pg_dump backup under ~/.local/share/cc2/backups/postgres.
uv syncuv run code-context-manage --index /path/to/your/projectUse either the direct Python entrypoint or the shell wrapper:
# List indexed projects
uv run code-context-manage --list
# Semantic search from inside an indexed repo (project inferred from cwd)
./cc2.sh search "auth middleware"
# Explicit project override when running outside the repo or targeting another project
./cc2.sh search "auth middleware" -p my-project
# Opt in to graph expansion from dense chunk hits
./cc2.sh search "auth middleware" -p my-project --graph
# Search within one file (also infers project from cwd)
./cc2.sh search-file src/auth.ts "token validation"
# Search indexed literature
./cc2.sh search-lit "dependency injection"The CLI search commands support optional output-shaping controls:
| Flag | Default | Effect |
|---|---|---|
--max-tokens |
unset | Per-request budget override. Clamped inside the retrieval pipeline. |
--include-tests |
off | Includes test/spec files when needed. |
--graph |
off | Opts CLI search into graph expansion. By default expansion favors deterministic high-value edges such as CALLS, REFERENCES, TESTS, DOCUMENTS, IMPORTS, and USES_TABLE, and avoids broad SAME_FILE fanout. Can also be enabled for CLI search with CC2_GRAPH_SEARCH_ENABLED=true. |
--file-type |
unset | Restrict to code, docs, or all. |
--directory |
unset | Restrict results to a directory prefix. |
--json |
off | Emit machine-readable output for agent/tool consumption. |
Recommended defaults for agent workflows:
- Keep
include_tests=falseunless the user explicitly asks about tests. - Start with
--max-tokensbetween1800and3200for typical coding tasks. - Use
--jsonwhen another tool or agent will post-process the results.
For CLI code search commands, -p/--project is optional when your current working directory is inside an indexed project root.
cc2.shpasses the caller cwd through to the Python CLI.- cc2 resolves the project by finding the indexed
project_rootthat contains the cwd. - If multiple indexed roots match, cc2 picks the longest matching root.
- Use
-pwhen running outside the repo, targeting another indexed project, or overriding cwd-based resolution.
Use --intent to steer reranking precision:
| Intent | Best for |
|---|---|
implementation |
Concrete runtime logic you will modify to ship a feature |
definition |
Types/interfaces/schemas/contracts/config declarations |
usage |
Call sites, integration points, consumer code |
debug |
Error paths, retries, fallbacks, validation failures, observability clues |
security |
Auth/authz, secret handling, sanitization, injection defenses |
performance |
Hot paths, caching, batching, query shape, contention points |
architecture |
Boundaries, adapters, orchestration, cross-module flow |
Default intent is implementation when omitted.
Retrieval changes should be measured with local benchmark suites when available. Suite definitions live in benchmarks/retrieval/*.json, but those JSON files are ignored because they often reference local indexed project IDs and private repository paths.
# List local benchmark-enabled projects
uv run python -m scripts.benchmark_retrieval --list
# Run one local project suite
uv run python -m scripts.benchmark_retrieval my-project
# Compare against a saved local baseline
uv run python -m scripts.benchmark_retrieval my-project --compare baseline-v1
# Run all local benchmark suites and save a combined baseline
uv run python -m scripts.benchmark_retrieval all --save hybrid-v1
# Run with graph expansion enabled
uv run python -m scripts.benchmark_retrieval my-project --graph
# A/B compare dense-only vs dense + graph expansion in one run
# Prints graph-derived candidate/final counts, added/removed expected files,
# worsened top results, surviving edge types, and token impact.
uv run python -m scripts.benchmark_retrieval my-project --compare-graphSee benchmarks/retrieval/README.md for the local suite schema.
# Index a project (auto-generates ID from folder name)
uv run code-context-manage --index /path/to/project
# Index with custom ID
uv run code-context-manage --index /path/to/project --id my-project
# Check what changed (dry-run)
uv run code-context-manage --check my-project
# Sync only changed files
uv run code-context-manage --sync my-project
# Force full reindex
uv run code-context-manage --index /path/to/project --force
# Show statistics
uv run code-context-manage --stats
# Watch for changes (background daemon)
uv run code-context-manage --watch /path/to/project
# List indexed books
uv run code-context-manage --list-books
# Initialize additive graph tables (does not reindex code)
uv run code-context-manage graph init
# Build the phase-1 graph from the existing code index
uv run code-context-manage graph build --project my-project
uv run code-context-manage graph build --project my-project --phase existing-index
# Add Phase 2 deterministic source relations incrementally (no code reindex)
uv run code-context-manage graph build --project my-project --phase deterministic
# Check graph backfill status and edge counts by type
uv run code-context-manage graph status --project my-project
# Create a project memory root with a MEMORY.md hub
./cc2.sh memory init
# Index a Markdown memory root
./cc2.sh memory index .pi/memory --project my-project
# Search indexed memory
./cc2.sh memory search "refresh token rotation" --project my-projectStandalone memory entrypoint also exists:
uv run code-context-memory init
uv run code-context-memory index .pi/memory --project my-project
uv run code-context-memory search "refresh token rotation" --project my-projectSee docs/memory.md for the memory layout, MEMORY.md hub pattern, and filters.
For the full documentation index, see docs/README.md.
There's also cc2.sh — a bash wrapper with a gum-based TUI plus non-interactive commands like search, search-file, and search-lit.
| Language | Extensions | Parser |
|---|---|---|
| TypeScript | .ts, .tsx |
tree-sitter-typescript |
| JavaScript | .js, .jsx, .mjs, .cjs |
tree-sitter-javascript |
| Python | .py, .pyi |
tree-sitter-python |
| Java | .java |
tree-sitter-java |
Adding a new language requires a tree-sitter grammar and chunk type mappings in src/code_context/chunking/parser.py.
All settings use the CC2_ prefix via environment variables or the repository .env file:
| Variable | Default | Description |
|---|---|---|
CC2_CODE_BACKEND |
postgres in .env.example |
Code index backend: recommended postgres, no-Docker lancedb, or experimental sqlite |
CC2_LANCEDB_URI |
~/.local/share/cc2/lancedb |
Embedded LanceDB storage directory for code and memory index data |
CC2_LANCEDB_LOCK_TIMEOUT_S |
60 |
Seconds to wait for another cc2 process to release the local LanceDB file lock |
CC2_SQLITE_VEC_PATH |
~/.local/share/cc2/sqlite/code.db |
Experimental SQLite + FTS5 + sqlite-vec code index database path |
CC2_SQLITE_LOCK_TIMEOUT_S |
60 |
Seconds to wait for SQLite file locks |
CC2_DATABASE_URL |
postgresql://...@localhost:25432/coderag |
PostgreSQL/pgvector connection string for code, books, graph, and MCP paths |
CC2_VOYAGE_API_KEY |
— | Voyage AI API key (required) |
CC2_EMBEDDING_MODEL_INDEX |
voyage-4-large |
Embedding model for indexing |
CC2_EMBEDDING_MODEL_QUERY |
voyage-4-lite |
Embedding model for queries |
CC2_VOYAGE_MAX_REQUESTS_PER_MINUTE |
1950 |
Global request pacing guardrail for Voyage API |
CC2_VOYAGE_MAX_TOKENS_PER_MINUTE |
2700000 |
Global token pacing guardrail for Voyage API |
CC2_VOYAGE_MAX_IN_FLIGHT_REQUESTS |
32 |
Global max concurrent Voyage API calls |
CC2_INDEX_EMBEDDING_FLUSH_CHUNKS |
5000 |
Chunks to accumulate for cross-file embedding batches during project indexing/sync |
CC2_LANCEDB_WRITE_BATCH_FILES |
500 |
Files per LanceDB write transaction during project indexing/sync |
CC2_VOYAGE_RETRY_MAX_ATTEMPTS |
5 |
Max retries for transient/rate-limit Voyage failures |
CC2_VOYAGE_RETRY_BASE_DELAY_MS |
250 |
Initial exponential backoff delay |
CC2_VOYAGE_RETRY_MAX_DELAY_MS |
5000 |
Retry delay ceiling |
CC2_VOYAGE_RETRY_JITTER_MS |
250 |
Extra random jitter to avoid retry bursts |
CC2_RERANK_MODEL |
rerank-2.5 |
Reranking model |
CC2_RERANK_TOP_K_OUTPUT |
8 |
Max final results returned by code search tools |
CC2_RERANK_RELATIVE_FACTOR |
0.75 |
Relative cutoff factor (threshold = top_score * factor) |
CC2_RERANK_SCORE_FLOOR |
0.40 |
Absolute minimum rerank score floor |
CC2_RERANK_FILE_SUPPORT_WEIGHT |
0.06 |
Small post-rerank boost for files with many strong retrieved chunks |
CC2_RESULT_MAX_TOKENS |
8000 |
Token budget for results |
CC2_SEARCH_LOG_PATH |
unset | Optional JSONL path for retrieval quality logs |
CC2_HYBRID_SEARCH_ENABLED |
true |
Enable dense + LanceDB FTS + exact-symbol candidate fusion before rerank |
CC2_HYBRID_LEXICAL_K |
50 |
Max lexical candidates retrieved for hybrid search |
CC2_HYBRID_RRF_RANK_CONSTANT |
60 |
Reciprocal rank fusion constant used to merge candidate channels |
CC2_HYBRID_DENSE_WEIGHT |
1.0 |
Dense retrieval weight in hybrid fusion |
CC2_HYBRID_LEXICAL_WEIGHT |
0.8 |
Lexical retrieval weight in hybrid fusion |
CC2_HYBRID_EXACT_SYMBOL_WEIGHT |
1.2 |
Exact symbol retrieval weight in hybrid fusion |
CC2_EXACT_SYMBOL_SEARCH_ENABLED |
true |
Allow exact symbol-name candidate retrieval inside hybrid search |
CC2_EXACT_SYMBOL_MIN_LENGTH |
3 |
Minimum identifier length for exact symbol candidate extraction |
CC2_LOG_LEVEL |
INFO |
Logging verbosity |
See src/code_context/config.py for all available settings.
- Vector search: <50ms
- Reranking: <100ms
- Total CLI search response: <200ms
- Initial indexing: ~5-10 min for 1000 files
- Incremental sync: <2s per changed file
- Storage: ~100MB per 100k chunks
- Walk the project tree (skips
node_modules,vendor,.git,dist, Laravel runtime/generated dirs, etc.) - Hash each file with BLAKE3 — skip unchanged files
- Parse with tree-sitter into semantic chunks (functions, classes, methods)
- Small files (<200 lines) still extract symbol-level chunks; generic file chunks are dropped when symbol chunks exist
- Embed chunks with
voyage-4-largein batches - Store chunk metadata and vectors in the selected backend;
.env.exampleuses PostgreSQL/pgvector, with LanceDB and SQLite alternatives available - File reindex operations replace old chunks before adding fresh chunks; rerun
cc2 syncif an interrupted process leaves a project partially indexed
Supported code languages include TypeScript, JavaScript, Python, Java, Go, Rust, SQL, PHP, and Vue single-file components. PHP files get class/function/method chunks; Vue SFCs are indexed as file-level chunks.
Laravel defaults skip Composer dependencies, runtime/cache output, built frontend assets, PHPUnit cache files, Pi/local MCP scratch, and generated Wayfinder route/action files.
This project uses uv for dependency management and Ruff for linting. ty is configured warning-only as a fast type-checking signal while the existing type backlog is migrated.
uv sync --dev
uv run ruff check .
uv run ty check
uv run pytestMaintainer and coding-agent guidance lives in AGENTS.md.
MIT
