coderag-ai 0.3.4__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/PKG-INFO +46 -8
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/README.md +45 -7
- coderag_ai-0.4.0/examples/mcp/.mcp.json +23 -0
- coderag_ai-0.4.0/examples/mcp/CLAUDE.md +23 -0
- coderag_ai-0.4.0/examples/mcp/README.md +152 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/pyproject.toml +1 -1
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/cli/main.py +91 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/core/config.py +16 -3
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/git/repo.py +15 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/indexing/indexer.py +21 -1
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/localdb.py +19 -0
- coderag_ai-0.4.0/src/coderag/setup_flow.py +101 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase14_localdb.py +27 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase8_incremental.py +51 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/.env.example +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/.gitignore +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/CODE_OF_CONDUCT.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/CONTRIBUTING.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/Dockerfile +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/LICENSE +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/Makefile +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/NOTICE +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/SECURITY.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/alembic.ini +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docker/entrypoint.sh +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docker-compose.yml +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adding-language.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/001-postgres-pgvector.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/002-structural-chunks.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/003-hybrid-search.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/004-tree-sitter.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/005-llm-provider-independence.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/006-repository-authorization.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/adr/007-token-budgeting.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/architecture.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/claude-code.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/evaluation.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/install-macos.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/licenses.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/llm-providers.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/retrieval.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/security.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/docs/vscode-extension.md +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/api.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/auth.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/checkout_service.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/models.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/payment_repository.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/payment_service.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/retry_policy.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/payments/users.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/tests/test_payment_service.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/demo-repository/tests/test_retry_policy.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/examples/eval/eval_dataset.json +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/scripts/gen_initial_migration.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/analyzers/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/analyzers/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/analyzers/flake8_adapter.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/analyzers/pylint_adapter.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/analyzers/workflow.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/api/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/api/app.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/api/dashboard.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/api/schemas.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/cli/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/context/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/context/builder.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/context/package.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/core/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/core/logging.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/core/text.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/core/tokens.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrate.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrations/env.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrations/script.py.mako +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrations/versions/.gitkeep +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrations/versions/9a973932b94d_measure_baseline_tokens.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/migrations/versions/c8a40feeda41_initial_schema.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/db/models.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/hashing.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/pipeline.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/registry.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/embeddings/sentence_transformer.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/evaluation/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/evaluation/datasets.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/evaluation/harness.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/git/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/indexing/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/indexing/ignore.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/llm/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/llm/anthropic.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/llm/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/llm/null.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/llm/registry.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/mcp_server.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/parsing/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/parsing/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/parsing/python.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/parsing/registry.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/base.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/engine.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/fusion.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/graph.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/lexical.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/reranking.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/semantic.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/retrieval/symbol.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/security/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/security/authz.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/security/secrets.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/service.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/src/coderag/telemetry.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/__init__.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/conftest.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase0_db.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase0_migrations.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase10_analyzers.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase11_dashboard.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase12_mcp.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase13_measure.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase15_migrate.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase1_indexer.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase1_parser.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase2_cli.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase2_retrieval.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase3_embeddings.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase3_semantic.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase3_sentence_transformer.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase4_graph.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase5_context_db.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase5_context_unit.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase6_api.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase6_ask.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase6_llm.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase7_eval.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_phase9_rerank.py +0 -0
- {coderag_ai-0.3.4 → coderag_ai-0.4.0}/tests/test_security_secrets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coderag-ai
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Token-efficient, structure-aware Code Intelligence + RAG for private source repositories.
|
|
5
5
|
Project-URL: Homepage, https://github.com/galacticsurfer/coderag
|
|
6
6
|
Project-URL: Repository, https://github.com/galacticsurfer/coderag
|
|
@@ -109,15 +109,21 @@ Full details: [`docs/architecture.md`](docs/architecture.md), plus ADRs in
|
|
|
109
109
|
|
|
110
110
|
## Install
|
|
111
111
|
|
|
112
|
-
**
|
|
113
|
-
Homebrew, or `sudo`:
|
|
112
|
+
**Two commands.** Ships a rootless bundled PostgreSQL+pgvector — no Docker, Homebrew, or `sudo`:
|
|
114
113
|
|
|
115
114
|
```bash
|
|
116
115
|
pipx install "coderag-ai[mcp,localdb]"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
coderag
|
|
116
|
+
cd /path/to/your-project && coderag setup
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`coderag setup` starts the database, applies migrations, indexes the repo, registers the MCP
|
|
120
|
+
server with Claude Code, and appends the retrieval nudge to your `CLAUDE.md` — the step that
|
|
121
|
+
makes Claude Code actually call the tools. It records the database URL, so **no
|
|
122
|
+
`export CODERAG_DATABASE_URL` is needed**; later commands find it automatically. Re-running is
|
|
123
|
+
safe (it won't duplicate the nudge). Opt out with `--no-mcp` / `--no-claude-md`.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
coderag search "where is authentication handled?" # works in any new shell
|
|
121
127
|
```
|
|
122
128
|
|
|
123
129
|
Full walkthrough incl. Claude Code wiring: [`docs/install-macos.md`](docs/install-macos.md).
|
|
@@ -345,7 +351,39 @@ claude mcp add coderag \
|
|
|
345
351
|
Tools: `coderag_context`, `coderag_search`, `coderag_symbol`, `coderag_repositories`,
|
|
346
352
|
`coderag_index`. Every call is recorded, so `/dashboard` shows the savings live. No API key
|
|
347
353
|
needed — Claude Code is the LLM. **Full step-by-step:**
|
|
348
|
-
[`docs/claude-code.md`](docs/claude-code.md).
|
|
354
|
+
[`docs/claude-code.md`](docs/claude-code.md). Copy-pasteable `.mcp.json` + `CLAUDE.md` snippet,
|
|
355
|
+
with **real** captured tool output: [`examples/mcp/`](examples/mcp/).
|
|
356
|
+
|
|
357
|
+
## Composes with other token-efficiency tools
|
|
358
|
+
|
|
359
|
+
CodeRAG attacks **one slice** of token spend: the code you'd otherwise read whole files to get.
|
|
360
|
+
It does nothing about output tokens, conversation history, cache placement, or model routing.
|
|
361
|
+
Tools that target those slices stack with it, because the slices are disjoint:
|
|
362
|
+
|
|
363
|
+
| Slice | Billed at | CodeRAG | Tools like [Caveman](https://caveman.so) |
|
|
364
|
+
|---|---|:--:|:--:|
|
|
365
|
+
| File-reading input | 1× input | ✅ retrieval instead of whole files | — |
|
|
366
|
+
| Output tokens | **5× input** | ❌ | ✅ output compression |
|
|
367
|
+
| Conversation history | ~0.1× (cached) | ❌ | ✅ context compression |
|
|
368
|
+
| Cache placement | — | ❌ | ✅ automatic breakpoints |
|
|
369
|
+
| Model routing | varies | ❌ | ✅ cheapest model passing evals |
|
|
370
|
+
|
|
371
|
+
Savings on disjoint slices are near-additive. On a typical mid-session turn, CodeRAG's measured
|
|
372
|
+
file-slice reduction works out to ~26% of cost; adding an output-compression layer takes the
|
|
373
|
+
combined figure to roughly ~38%. **Both halves of that are models, not measurements** — verify
|
|
374
|
+
on your own workload before believing either.
|
|
375
|
+
|
|
376
|
+
**Wiring:** they use different mechanisms and don't conflict — CodeRAG is an MCP server, a
|
|
377
|
+
compression skill is a Skill, a proxy is an `ANTHROPIC_BASE_URL`. CodeRAG's own `ask` can also
|
|
378
|
+
route through such a proxy, since the LLM base URL is configurable:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
export CODERAG_ANTHROPIC_BASE_URL=http://localhost:<proxy-port>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Enable one at a time and compare `/cost` between them — a proxy that compresses context may be
|
|
385
|
+
compressing context CodeRAG already minimised, so the marginal gain can be smaller than the two
|
|
386
|
+
vendors' claims added together.
|
|
349
387
|
|
|
350
388
|
## Editor integration (VS Code)
|
|
351
389
|
|
|
@@ -48,15 +48,21 @@ Full details: [`docs/architecture.md`](docs/architecture.md), plus ADRs in
|
|
|
48
48
|
|
|
49
49
|
## Install
|
|
50
50
|
|
|
51
|
-
**
|
|
52
|
-
Homebrew, or `sudo`:
|
|
51
|
+
**Two commands.** Ships a rootless bundled PostgreSQL+pgvector — no Docker, Homebrew, or `sudo`:
|
|
53
52
|
|
|
54
53
|
```bash
|
|
55
54
|
pipx install "coderag-ai[mcp,localdb]"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
coderag
|
|
55
|
+
cd /path/to/your-project && coderag setup
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`coderag setup` starts the database, applies migrations, indexes the repo, registers the MCP
|
|
59
|
+
server with Claude Code, and appends the retrieval nudge to your `CLAUDE.md` — the step that
|
|
60
|
+
makes Claude Code actually call the tools. It records the database URL, so **no
|
|
61
|
+
`export CODERAG_DATABASE_URL` is needed**; later commands find it automatically. Re-running is
|
|
62
|
+
safe (it won't duplicate the nudge). Opt out with `--no-mcp` / `--no-claude-md`.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
coderag search "where is authentication handled?" # works in any new shell
|
|
60
66
|
```
|
|
61
67
|
|
|
62
68
|
Full walkthrough incl. Claude Code wiring: [`docs/install-macos.md`](docs/install-macos.md).
|
|
@@ -284,7 +290,39 @@ claude mcp add coderag \
|
|
|
284
290
|
Tools: `coderag_context`, `coderag_search`, `coderag_symbol`, `coderag_repositories`,
|
|
285
291
|
`coderag_index`. Every call is recorded, so `/dashboard` shows the savings live. No API key
|
|
286
292
|
needed — Claude Code is the LLM. **Full step-by-step:**
|
|
287
|
-
[`docs/claude-code.md`](docs/claude-code.md).
|
|
293
|
+
[`docs/claude-code.md`](docs/claude-code.md). Copy-pasteable `.mcp.json` + `CLAUDE.md` snippet,
|
|
294
|
+
with **real** captured tool output: [`examples/mcp/`](examples/mcp/).
|
|
295
|
+
|
|
296
|
+
## Composes with other token-efficiency tools
|
|
297
|
+
|
|
298
|
+
CodeRAG attacks **one slice** of token spend: the code you'd otherwise read whole files to get.
|
|
299
|
+
It does nothing about output tokens, conversation history, cache placement, or model routing.
|
|
300
|
+
Tools that target those slices stack with it, because the slices are disjoint:
|
|
301
|
+
|
|
302
|
+
| Slice | Billed at | CodeRAG | Tools like [Caveman](https://caveman.so) |
|
|
303
|
+
|---|---|:--:|:--:|
|
|
304
|
+
| File-reading input | 1× input | ✅ retrieval instead of whole files | — |
|
|
305
|
+
| Output tokens | **5× input** | ❌ | ✅ output compression |
|
|
306
|
+
| Conversation history | ~0.1× (cached) | ❌ | ✅ context compression |
|
|
307
|
+
| Cache placement | — | ❌ | ✅ automatic breakpoints |
|
|
308
|
+
| Model routing | varies | ❌ | ✅ cheapest model passing evals |
|
|
309
|
+
|
|
310
|
+
Savings on disjoint slices are near-additive. On a typical mid-session turn, CodeRAG's measured
|
|
311
|
+
file-slice reduction works out to ~26% of cost; adding an output-compression layer takes the
|
|
312
|
+
combined figure to roughly ~38%. **Both halves of that are models, not measurements** — verify
|
|
313
|
+
on your own workload before believing either.
|
|
314
|
+
|
|
315
|
+
**Wiring:** they use different mechanisms and don't conflict — CodeRAG is an MCP server, a
|
|
316
|
+
compression skill is a Skill, a proxy is an `ANTHROPIC_BASE_URL`. CodeRAG's own `ask` can also
|
|
317
|
+
route through such a proxy, since the LLM base URL is configurable:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
export CODERAG_ANTHROPIC_BASE_URL=http://localhost:<proxy-port>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Enable one at a time and compare `/cost` between them — a proxy that compresses context may be
|
|
324
|
+
compressing context CodeRAG already minimised, so the marginal gain can be smaller than the two
|
|
325
|
+
vendors' claims added together.
|
|
288
326
|
|
|
289
327
|
## Editor integration (VS Code)
|
|
290
328
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": [
|
|
3
|
+
"Copy this file to the ROOT of the project you want Claude Code to search,",
|
|
4
|
+
"rename it to '.mcp.json', and edit the two values below.",
|
|
5
|
+
" command -> absolute path to coderag-mcp ('which coderag-mcp').",
|
|
6
|
+
" An absolute path matters: Claude Code spawns the process",
|
|
7
|
+
" without your shell's PATH.",
|
|
8
|
+
" CODERAG_DATABASE_URL -> the URL 'coderag localdb start' printed",
|
|
9
|
+
" ('echo $CODERAG_DATABASE_URL'). JSON has no shell",
|
|
10
|
+
" expansion, so paste the literal value.",
|
|
11
|
+
" CODERAG_DEFAULT_REPOSITORY -> the --name you indexed with.",
|
|
12
|
+
"Then reload VS Code / restart Claude Code and run /mcp to confirm."
|
|
13
|
+
],
|
|
14
|
+
"mcpServers": {
|
|
15
|
+
"coderag": {
|
|
16
|
+
"command": "/Users/you/.local/bin/coderag-mcp",
|
|
17
|
+
"env": {
|
|
18
|
+
"CODERAG_DATABASE_URL": "postgresql+psycopg://postgres:@/postgres?host=/Users/you/.coderag/pgdata",
|
|
19
|
+
"CODERAG_DEFAULT_REPOSITORY": "myproject"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Append this to your project's CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Registering the MCP server is not enough on its own — Claude Code will keep
|
|
4
|
+
globbing, grepping, and reading whole files unless you tell it not to. This
|
|
5
|
+
snippet is what actually produces the token saving.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Code search
|
|
10
|
+
|
|
11
|
+
This repo is indexed in CodeRAG. To find or understand code, call the
|
|
12
|
+
`coderag_context` or `coderag_search` MCP tools **before** globbing/grepping or
|
|
13
|
+
reading whole files — they return only the relevant symbols and cost far fewer
|
|
14
|
+
input tokens.
|
|
15
|
+
|
|
16
|
+
- `coderag_search` — locate symbols ("where is X defined?"). Returns ranked
|
|
17
|
+
symbols with file, line range, and why each matched.
|
|
18
|
+
- `coderag_context` — understand behaviour ("why does X happen?"). Returns the
|
|
19
|
+
target symbol plus its dependencies, callers, and tests, under a token budget.
|
|
20
|
+
- `coderag_symbol` — fetch one symbol's full source by qualified name.
|
|
21
|
+
|
|
22
|
+
Read whole files only when the retrieved context is genuinely insufficient. If
|
|
23
|
+
the index looks stale, call `coderag_index` with `incremental: true`.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# MCP setup example (Claude Code)
|
|
2
|
+
|
|
3
|
+
A complete, copy-pasteable setup, plus **real** tool output captured from the
|
|
4
|
+
bundled demo repository — not illustrative JSON.
|
|
5
|
+
|
|
6
|
+
Files here:
|
|
7
|
+
|
|
8
|
+
| File | Use |
|
|
9
|
+
|---|---|
|
|
10
|
+
| [`.mcp.json`](.mcp.json) | Drop in your project root (project-scoped server, committable) |
|
|
11
|
+
| [`CLAUDE.md`](CLAUDE.md) | Append to your project's `CLAUDE.md` — **this is what makes Claude Code actually use the tools** |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Install and start the database
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pipx install "coderag-ai[mcp,localdb]"
|
|
19
|
+
coderag localdb start # prints the DB URL; applies migrations
|
|
20
|
+
export CODERAG_DATABASE_URL='…' # paste what it printed; add to ~/.zshrc
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 2. Index the repo you want to search
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd /path/to/your-project
|
|
27
|
+
coderag index . --name myproject
|
|
28
|
+
coderag search "where is authentication handled?" # sanity check, no LLM needed
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 3. Register the server — pick one
|
|
32
|
+
|
|
33
|
+
**A. One command, all projects:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
claude mcp add coderag \
|
|
37
|
+
-e CODERAG_DATABASE_URL="$CODERAG_DATABASE_URL" \
|
|
38
|
+
-e CODERAG_DEFAULT_REPOSITORY=myproject \
|
|
39
|
+
-s user \
|
|
40
|
+
-- "$(which coderag-mcp)"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**B. Committed `.mcp.json`** (see the file in this directory) so your team gets it
|
|
44
|
+
automatically. Note JSON has no shell expansion — paste literal values.
|
|
45
|
+
|
|
46
|
+
Either way: use the **absolute path** to `coderag-mcp`. Claude Code spawns it
|
|
47
|
+
without your shell's PATH, so a bare command name fails with "command not found".
|
|
48
|
+
|
|
49
|
+
## 4. Add the `CLAUDE.md` nudge
|
|
50
|
+
|
|
51
|
+
Append [`CLAUDE.md`](CLAUDE.md) to your project's `CLAUDE.md`. **Without it the
|
|
52
|
+
saving is zero** — the tools are registered but unused.
|
|
53
|
+
|
|
54
|
+
## 5. Verify
|
|
55
|
+
|
|
56
|
+
Run `/mcp` in Claude Code. You should see `coderag` **connected** with five tools.
|
|
57
|
+
Then just ask a question normally — MCP tools are not slash commands, so there is
|
|
58
|
+
no `/coderag`:
|
|
59
|
+
|
|
60
|
+
> Where are CPQ filter options defined across this repo?
|
|
61
|
+
|
|
62
|
+
To force a tool on the first try, name it: *"Use the coderag_search tool to …"*.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## What the tools actually return
|
|
67
|
+
|
|
68
|
+
Captured by running the tools against `examples/demo-repository`.
|
|
69
|
+
|
|
70
|
+
### `coderag_search("where are failed payments retried?", limit=3)`
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
[
|
|
74
|
+
{
|
|
75
|
+
"symbol": "payments.payment_service.PaymentService.retry_payment",
|
|
76
|
+
"type": "method",
|
|
77
|
+
"file": "payments/payment_service.py",
|
|
78
|
+
"lines": "33-54",
|
|
79
|
+
"score": 0.04672,
|
|
80
|
+
"why": ["graph_callee", "graph_caller", "graph_child", "lexical", "semantic"],
|
|
81
|
+
"tokens": 215
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Every hit explains **why** it was retrieved — `lexical` (full-text), `semantic`
|
|
87
|
+
(vector), `exact_symbol`, or a graph reason such as `graph_caller` / `graph_test`.
|
|
88
|
+
|
|
89
|
+
### `coderag_context("why can retry_payment leave an invoice pending?")`
|
|
90
|
+
|
|
91
|
+
Returns `context` (the code text), `selected_symbols`, `token_accounting`, and a
|
|
92
|
+
plain-language `measurement`:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Returned 1449 tokens of context instead of the 1654 tokens it would take to read
|
|
96
|
+
the 8 file(s) containing this code (12.4% fewer input tokens).
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"candidates_found": 38,
|
|
102
|
+
"candidates_selected": 27,
|
|
103
|
+
"candidate_tokens": 2240,
|
|
104
|
+
"context_tokens": 1449,
|
|
105
|
+
"token_reduction_from_candidates": 35.3,
|
|
106
|
+
"baseline_tokens": 1654,
|
|
107
|
+
"baseline_files": 8,
|
|
108
|
+
"tokens_saved_vs_files": 205,
|
|
109
|
+
"reduction_vs_files": 12.4
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Symbols come back grouped by priority — target first, then dependencies, callers,
|
|
114
|
+
tests:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
[
|
|
118
|
+
{"category": "target", "symbol": "…PaymentService.retry_payment", "lines": "33-54", "tokens": 215},
|
|
119
|
+
{"category": "implementation", "symbol": "…PaymentService.__init__", "lines": "13-21", "tokens": 51},
|
|
120
|
+
{"category": "dependencies", "symbol": "…PaymentService.process_payment", "lines": "23-31", "tokens": 75}
|
|
121
|
+
]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
> **Note the 12.4%.** That's the *demo* repo — 11 tiny files, where retrieval
|
|
125
|
+
> reaches most of the codebase and the saving is small. On a real backend the
|
|
126
|
+
> same measurement runs far higher (75% has been observed). Run
|
|
127
|
+
> `coderag context "…" --measure` on your own repo for your number; don't trust
|
|
128
|
+
> either figure as representative.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## The five tools
|
|
133
|
+
|
|
134
|
+
| Tool | Use |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `coderag_context` | Budgeted context for a question (target + deps + callers + tests) |
|
|
137
|
+
| `coderag_search` | Ranked symbols with file/lines/score/why |
|
|
138
|
+
| `coderag_symbol` | Full source of one symbol by qualified name |
|
|
139
|
+
| `coderag_repositories` | Which repos are indexed, and at which commit |
|
|
140
|
+
| `coderag_index` | Index / re-index (`incremental: true` for a fast update) |
|
|
141
|
+
|
|
142
|
+
## Troubleshooting
|
|
143
|
+
|
|
144
|
+
| Symptom | Cause / fix |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `/mcp` doesn't list `coderag` | Server not registered, or `.mcp.json` is not in the project root. Reload the window. |
|
|
147
|
+
| Listed but **failed** | Run the command by hand: `CODERAG_DATABASE_URL=… /abs/path/coderag-mcp`. A healthy server sits silently on stdin (Ctrl-C to exit); a traceback tells you what's wrong. |
|
|
148
|
+
| "command not found" | Use the absolute path from `which coderag-mcp`. |
|
|
149
|
+
| "no repositories indexed yet" | Run `coderag index . --name myproject`. |
|
|
150
|
+
| "multiple repositories; pass --repo" | Set `CODERAG_DEFAULT_REPOSITORY`. |
|
|
151
|
+
| Tools connected but never called | The `CLAUDE.md` nudge is missing — see step 4. |
|
|
152
|
+
| Results reference code that moved | Re-index: `coderag index . --name myproject --incremental`. |
|
|
@@ -7,7 +7,7 @@ build-backend = "hatchling.build"
|
|
|
7
7
|
# command are both `coderag`. Plain "coderag" is taken on PyPI by an unrelated project,
|
|
8
8
|
# and "code-rag" is rejected as too similar (PyPI collapses -/_/. when comparing names).
|
|
9
9
|
name = "coderag-ai"
|
|
10
|
-
version = "0.
|
|
10
|
+
version = "0.4.0"
|
|
11
11
|
description = "Token-efficient, structure-aware Code Intelligence + RAG for private source repositories."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.12"
|
|
@@ -80,6 +80,97 @@ def localdb_status(
|
|
|
80
80
|
console.print(f"[green]running (pid {pid})[/]" if pid else "[yellow]not running[/]")
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
@app.command()
|
|
84
|
+
def setup(
|
|
85
|
+
path: str = typer.Argument(".", help="Repository to index (default: current dir)."),
|
|
86
|
+
name: str | None = typer.Option(None, "--name", help="Repository name."),
|
|
87
|
+
mcp: bool = typer.Option(True, "--mcp/--no-mcp", help="Register with Claude Code."),
|
|
88
|
+
claude_md: bool = typer.Option(True, "--claude-md/--no-claude-md",
|
|
89
|
+
help="Append the retrieval nudge to CLAUDE.md."),
|
|
90
|
+
) -> None:
|
|
91
|
+
"""One command: start the database, migrate, index, and wire up Claude Code."""
|
|
92
|
+
from pathlib import Path
|
|
93
|
+
|
|
94
|
+
from coderag import localdb
|
|
95
|
+
from coderag.core.config import get_settings
|
|
96
|
+
from coderag.db.migrate import upgrade_to_head
|
|
97
|
+
from coderag.setup_flow import (
|
|
98
|
+
SetupResult,
|
|
99
|
+
append_nudge,
|
|
100
|
+
claude_md_needs_nudge,
|
|
101
|
+
register_mcp,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
repo_path = Path(path).expanduser().resolve()
|
|
105
|
+
repo_name = name or repo_path.name
|
|
106
|
+
res = SetupResult(repository=repo_name)
|
|
107
|
+
|
|
108
|
+
# 1. database — reuse an already-configured one, else start the local server
|
|
109
|
+
import os
|
|
110
|
+
|
|
111
|
+
if os.environ.get("CODERAG_DATABASE_URL"):
|
|
112
|
+
res.database_url = get_settings().database_url
|
|
113
|
+
res.skip("using CODERAG_DATABASE_URL from the environment")
|
|
114
|
+
else:
|
|
115
|
+
try:
|
|
116
|
+
res.database_url = localdb.start()
|
|
117
|
+
res.ok("local PostgreSQL running (URL recorded; no export needed)")
|
|
118
|
+
except RuntimeError as exc:
|
|
119
|
+
res.warn(str(exc).splitlines()[0])
|
|
120
|
+
console.print(f"[red]{exc}[/]")
|
|
121
|
+
raise typer.Exit(1) from None
|
|
122
|
+
|
|
123
|
+
# 2. schema
|
|
124
|
+
try:
|
|
125
|
+
upgrade_to_head(res.database_url)
|
|
126
|
+
res.ok("database schema up to date")
|
|
127
|
+
except Exception as exc:
|
|
128
|
+
res.warn(f"migrations failed: {str(exc)[:120]}")
|
|
129
|
+
console.print(f"[red]Migrations failed:[/] {exc}")
|
|
130
|
+
raise typer.Exit(1) from None
|
|
131
|
+
|
|
132
|
+
# 3. index
|
|
133
|
+
get_settings.cache_clear()
|
|
134
|
+
os.environ["CODERAG_DATABASE_URL"] = res.database_url or ""
|
|
135
|
+
with session_scope() as session:
|
|
136
|
+
from coderag.service import run_index
|
|
137
|
+
|
|
138
|
+
repo, stats = run_index(session, str(repo_path), repo_name)
|
|
139
|
+
res.symbols = stats.symbols_indexed
|
|
140
|
+
res.ok(f"indexed {repo_name}: {stats.files_indexed} files, "
|
|
141
|
+
f"{stats.symbols_indexed} symbols, {stats.relationships_created} relationships")
|
|
142
|
+
|
|
143
|
+
# 4. Claude Code MCP registration
|
|
144
|
+
if mcp:
|
|
145
|
+
done, msg = register_mcp("coderag", res.database_url or "", repo_name)
|
|
146
|
+
(res.ok if done else res.warn)(msg)
|
|
147
|
+
else:
|
|
148
|
+
res.skip("MCP registration skipped (--no-mcp)")
|
|
149
|
+
|
|
150
|
+
# 5. the nudge — without it Claude Code never calls the tools
|
|
151
|
+
if claude_md:
|
|
152
|
+
target = repo_path / "CLAUDE.md"
|
|
153
|
+
if claude_md_needs_nudge(target):
|
|
154
|
+
append_nudge(target)
|
|
155
|
+
res.ok(f"added the retrieval nudge to {target}")
|
|
156
|
+
else:
|
|
157
|
+
res.skip("CLAUDE.md already contains the nudge")
|
|
158
|
+
else:
|
|
159
|
+
res.skip("CLAUDE.md untouched (--no-claude-md)")
|
|
160
|
+
|
|
161
|
+
icons = {"ok": "[green]✓[/]", "skip": "[dim]·[/]", "warn": "[yellow]![/]"}
|
|
162
|
+
console.print("\n[bold]CodeRAG setup[/]")
|
|
163
|
+
for status, msg in res.steps:
|
|
164
|
+
console.print(f" {icons[status]} {msg}")
|
|
165
|
+
console.print(
|
|
166
|
+
"\nTry it: [bold]coderag search \"where is X handled?\"[/]\n"
|
|
167
|
+
"In Claude Code: run [bold]/mcp[/] (expect 'coderag' connected), then ask a "
|
|
168
|
+
"question normally — MCP tools are not slash commands.\n"
|
|
169
|
+
"Dashboard: [bold]uvicorn coderag.api.app:app --port 8000[/] "
|
|
170
|
+
"-> http://localhost:8000/dashboard"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
83
174
|
@app.command()
|
|
84
175
|
def migrate(
|
|
85
176
|
database_url: str | None = typer.Option(
|
|
@@ -12,6 +12,19 @@ from functools import lru_cache
|
|
|
12
12
|
from pydantic import Field
|
|
13
13
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
14
14
|
|
|
15
|
+
COMPOSE_DATABASE_URL = "postgresql+psycopg://coderag:coderag@localhost:5432/coderag"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _default_database_url() -> str:
|
|
19
|
+
"""Fall back to the URL written by `coderag localdb start`, else compose's."""
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
value = (Path.home() / ".coderag" / "database_url").read_text().strip()
|
|
24
|
+
except OSError:
|
|
25
|
+
return COMPOSE_DATABASE_URL
|
|
26
|
+
return value or COMPOSE_DATABASE_URL
|
|
27
|
+
|
|
15
28
|
|
|
16
29
|
class Settings(BaseSettings):
|
|
17
30
|
model_config = SettingsConfigDict(
|
|
@@ -23,9 +36,9 @@ class Settings(BaseSettings):
|
|
|
23
36
|
|
|
24
37
|
# ---- Database -------------------------------------------------------
|
|
25
38
|
# A libpq URL. In dev/test the pgserver fixture overrides this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
39
|
+
# Resolution order: CODERAG_DATABASE_URL env/.env -> the URL recorded by
|
|
40
|
+
# `coderag localdb start` -> the docker-compose default.
|
|
41
|
+
database_url: str = Field(default_factory=lambda: _default_database_url())
|
|
29
42
|
db_echo: bool = False
|
|
30
43
|
|
|
31
44
|
# Default repository name used when a request doesn't name one (handy for the
|
|
@@ -44,6 +44,21 @@ class GitRepo:
|
|
|
44
44
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
45
45
|
return False
|
|
46
46
|
|
|
47
|
+
def commit_exists(self, sha: str) -> bool:
|
|
48
|
+
"""True if ``sha`` resolves to a commit object in this repository.
|
|
49
|
+
|
|
50
|
+
A rebase, amend, or force-push can orphan a previously-indexed commit;
|
|
51
|
+
diffing against it would fail, so callers check first and fall back to a
|
|
52
|
+
full index.
|
|
53
|
+
"""
|
|
54
|
+
if not sha:
|
|
55
|
+
return False
|
|
56
|
+
try:
|
|
57
|
+
self._git("cat-file", "-e", f"{sha}^{{commit}}")
|
|
58
|
+
return True
|
|
59
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
60
|
+
return False
|
|
61
|
+
|
|
47
62
|
def current_commit(self) -> str | None:
|
|
48
63
|
try:
|
|
49
64
|
return self._git("rev-parse", "HEAD").strip()
|
|
@@ -9,6 +9,7 @@ Phase 8.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import hashlib
|
|
12
|
+
import subprocess
|
|
12
13
|
import time
|
|
13
14
|
from dataclasses import dataclass
|
|
14
15
|
|
|
@@ -155,6 +156,14 @@ class Indexer:
|
|
|
155
156
|
head = git.current_commit()
|
|
156
157
|
if not git.is_git() or not base:
|
|
157
158
|
return self.full_index(repo)
|
|
159
|
+
if not git.commit_exists(base):
|
|
160
|
+
# The previously-indexed commit is gone (rebase / amend / force-push),
|
|
161
|
+
# so there is no valid diff base. Rebuild instead of failing.
|
|
162
|
+
log.warning(
|
|
163
|
+
"incremental_index.base_missing", repository=repo.name,
|
|
164
|
+
base=base[:12], action="falling back to full index",
|
|
165
|
+
)
|
|
166
|
+
return self.full_index(repo)
|
|
158
167
|
|
|
159
168
|
started = time.perf_counter()
|
|
160
169
|
run = IndexingRun(repository_id=repo.id, mode="incremental", status="running",
|
|
@@ -166,7 +175,18 @@ class Indexer:
|
|
|
166
175
|
self._pending_rels = []
|
|
167
176
|
stats = IndexStats(commit_sha=head)
|
|
168
177
|
try:
|
|
169
|
-
|
|
178
|
+
try:
|
|
179
|
+
diff = git.diff(base, head or "HEAD")
|
|
180
|
+
except subprocess.CalledProcessError:
|
|
181
|
+
# Unexpected diff failure (shallow clone, corrupt object, …):
|
|
182
|
+
# a full rebuild is always correct, so prefer it over erroring.
|
|
183
|
+
log.warning(
|
|
184
|
+
"incremental_index.diff_failed", repository=repo.name,
|
|
185
|
+
action="falling back to full index",
|
|
186
|
+
)
|
|
187
|
+
run.status = "superseded"
|
|
188
|
+
self.session.flush()
|
|
189
|
+
return self.full_index(repo)
|
|
170
190
|
changed = [p for p in diff.changed if should_index(p, self.extra_ignore)]
|
|
171
191
|
gone = set(changed) | set(diff.deleted)
|
|
172
192
|
for path in gone:
|
|
@@ -13,6 +13,18 @@ from __future__ import annotations
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
DEFAULT_PGDATA = Path.home() / ".coderag" / "pgdata"
|
|
16
|
+
# `localdb start` records its URL here so other commands (and the MCP server)
|
|
17
|
+
# find the database automatically, without the user exporting anything.
|
|
18
|
+
URL_FILE = Path.home() / ".coderag" / "database_url"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def saved_url() -> str | None:
|
|
22
|
+
"""The URL recorded by the most recent `localdb start`, if any."""
|
|
23
|
+
try:
|
|
24
|
+
value = URL_FILE.read_text().strip()
|
|
25
|
+
except OSError:
|
|
26
|
+
return None
|
|
27
|
+
return value or None
|
|
16
28
|
|
|
17
29
|
|
|
18
30
|
def _require_pgserver():
|
|
@@ -51,6 +63,13 @@ def start(pgdata: Path | str | None = None) -> str:
|
|
|
51
63
|
server = pgserver.get_server(str(path), cleanup_mode=None)
|
|
52
64
|
url = sqlalchemy_url(server.get_uri())
|
|
53
65
|
_ensure_vector_extension(url)
|
|
66
|
+
# Record it (default location only) so later commands need no env var.
|
|
67
|
+
if Path(pgdata or DEFAULT_PGDATA).expanduser() == DEFAULT_PGDATA:
|
|
68
|
+
try:
|
|
69
|
+
URL_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
URL_FILE.write_text(url + "\n")
|
|
71
|
+
except OSError: # pragma: no cover - convenience only, never fatal
|
|
72
|
+
pass
|
|
54
73
|
return url
|
|
55
74
|
|
|
56
75
|
|