codecortex 0.16.0__tar.gz → 0.18.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.
- {codecortex-0.16.0/src/codecortex.egg-info → codecortex-0.18.0}/PKG-INFO +7 -7
- {codecortex-0.16.0 → codecortex-0.18.0}/README.md +6 -6
- {codecortex-0.16.0 → codecortex-0.18.0/src/codecortex.egg-info}/PKG-INFO +7 -7
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codecortex.egg-info/SOURCES.txt +7 -0
- codecortex-0.18.0/src/codeintel/__init__.py +1 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/__main__.py +7 -1
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/auth.py +8 -1
- codecortex-0.18.0/src/codeintel/commands/index.py +144 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/reset.py +2 -2
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/config.py +24 -2
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/containment.py +15 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/gateway.py +3 -3
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/indexer.py +205 -33
- codecortex-0.18.0/src/codeintel/paths.py +31 -0
- codecortex-0.18.0/src/codeintel/progress.py +78 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/providers/semantic.py +108 -7
- codecortex-0.18.0/src/codeintel/redact.py +187 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/reset.py +47 -1
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/searcher.py +125 -32
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/semantic_db.py +59 -16
- codecortex-0.18.0/src/codeintel/term.py +394 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_chunking.py +6 -6
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_cli_commands.py +20 -2
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_config.py +63 -3
- codecortex-0.18.0/tests/test_index_progress.py +263 -0
- codecortex-0.18.0/tests/test_indexer_binary.py +42 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_rbac.py +73 -0
- codecortex-0.18.0/tests/test_redaction_boundary.py +217 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_reset.py +62 -0
- codecortex-0.18.0/tests/test_staleness.py +265 -0
- codecortex-0.18.0/tests/test_symbol_context.py +172 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_treesitter.py +5 -5
- codecortex-0.16.0/src/codeintel/__init__.py +0 -1
- codecortex-0.16.0/src/codeintel/commands/index.py +0 -76
- codecortex-0.16.0/src/codeintel/redact.py +0 -113
- codecortex-0.16.0/src/codeintel/term.py +0 -161
- {codecortex-0.16.0 → codecortex-0.18.0}/LICENSE +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/pyproject.toml +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/setup.cfg +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codecortex.egg-info/entry_points.txt +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codecortex.egg-info/requires.txt +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codecortex.egg-info/top_level.txt +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/agent_prompt.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/cache.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/__init__.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/_common.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/doctor.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/gen_token.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/graph.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/install.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/map.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/prompt.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/query.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/serve.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/serve_http.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/setup.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/commands/status.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/doctor.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/graph_backend.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/graph_render.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/graph_resolution.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/grapher.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/http_server.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/injector.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/installer.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/loc.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/logconfig.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/mapper.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/metrics.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/onboarding.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/outcome.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/policy.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/provider.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/providers/__init__.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/providers/graph.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/providers/lsp.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/providers/none.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/py.typed +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/reindexer.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/server.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/source_kind.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/verify.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/viewer/__init__.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/src/codeintel/viewer/graph_template.html +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_agent_prompt.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_backend_reaper.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_cache.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_cli_help.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_cold_process.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_corpus.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_docs_ci_claims.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_docs_deadcode_withdrawal.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_doctor.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_e2e.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_engine_adoption.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_enterprise.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_gateway.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_graph_failure_population.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_graph_provider.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_graph_real.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_graph_stdin.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_grapher.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_hardening.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_http_auth.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_http_server.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_incompleteness.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_installer.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_integration.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_loc_census.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_lsp_provider.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_lsp_real.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_mapper.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_mcp_handshake.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_mcp_server.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_model_dimension.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_never_raise.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_onboarding.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_reindexer.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_rerank.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_semantic_provider.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_source_kind.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_term.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_verify.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_verify_call.py +0 -0
- {codecortex-0.16.0 → codecortex-0.18.0}/tests/test_version_skew.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -279,7 +279,7 @@ Then it launches the exact command it registered and drives a real MCP handshake
|
|
|
279
279
|
```text
|
|
280
280
|
v claude: registered at /Users/you/.claude.json
|
|
281
281
|
|
|
282
|
-
v verified: codeintel 0.
|
|
282
|
+
v verified: codeintel 0.18.0 — 4 tools (code.query, code.status, code.doctor, code.map)
|
|
283
283
|
```
|
|
284
284
|
|
|
285
285
|
If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
|
|
@@ -396,7 +396,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
396
396
|
| `codeintel install [--agent auto\|claude\|codex\|gemini\|zed\|all] [--no-verify] [--relative-command]` | Register codeintel with the agents installed on this machine (`auto`, the default), then prove it by completing a real MCP handshake against the registered command |
|
|
397
397
|
| `codeintel setup [project_root] [--all] [--index] [--warm] [--install-uv] [--install-deps] [--json]` | Prepare backends + index this repo (`--all` = one command: do everything automatable, idempotent); ends with a health report + **Next:** steps |
|
|
398
398
|
| `codeintel prompt [project_root] [--agent auto\|claude\|codex\|gemini\|zed] [--fresh] [--deep]` | Print a paste-to-your-agent setup prompt; probes health and emits only the outstanding steps (or "just restart me" when already healthy). `--fresh` = the full sequence from `pip install`, to send a friend |
|
|
399
|
-
| `codeintel index [project_root]` | Index a
|
|
399
|
+
| `codeintel index [project_root] [--quiet]` | Index a repo (semantic embeddings + best-effort graph & map refresh), with a live progress display; `--quiet` prints only the result line |
|
|
400
400
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
401
401
|
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
402
402
|
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
|
|
@@ -404,10 +404,10 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
404
404
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
405
405
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
406
406
|
| `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
|
|
407
|
-
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear
|
|
407
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear this repo's index — **both** semantic and graph — so it's as if never indexed; `--all` wipes every repo. Recovers from a corrupt/stale DB |
|
|
408
408
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
409
409
|
|
|
410
|
-
Human-facing commands (`doctor`, `
|
|
410
|
+
Human-facing commands (`doctor`, `setup`, `prompt`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
411
411
|
|
|
412
412
|
**Exit codes**, so a `make` target or CI step can gate on `$?`:
|
|
413
413
|
|
|
@@ -444,7 +444,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
|
|
|
444
444
|
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
445
445
|
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
446
446
|
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
447
|
-
| `CODEINTEL_HOME` | Where
|
|
447
|
+
| `CODEINTEL_HOME` | Where per-machine state lives (default `~/.codeintel`) — the index cache, the global `config.toml`, and `auth.toml`. Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
|
|
448
448
|
|
|
449
449
|
## Privacy & dependencies
|
|
450
450
|
|
|
@@ -617,7 +617,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
|
|
|
617
617
|
cd codeintel
|
|
618
618
|
pip install -e .[dev]
|
|
619
619
|
|
|
620
|
-
pytest tests/ -q # ~
|
|
620
|
+
pytest tests/ -q # ~740 tests, ~30s; fails under 83% coverage
|
|
621
621
|
ruff check src tests # lint
|
|
622
622
|
mypy # types (src/ only)
|
|
623
623
|
```
|
|
@@ -242,7 +242,7 @@ Then it launches the exact command it registered and drives a real MCP handshake
|
|
|
242
242
|
```text
|
|
243
243
|
v claude: registered at /Users/you/.claude.json
|
|
244
244
|
|
|
245
|
-
v verified: codeintel 0.
|
|
245
|
+
v verified: codeintel 0.18.0 — 4 tools (code.query, code.status, code.doctor, code.map)
|
|
246
246
|
```
|
|
247
247
|
|
|
248
248
|
If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
|
|
@@ -359,7 +359,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
359
359
|
| `codeintel install [--agent auto\|claude\|codex\|gemini\|zed\|all] [--no-verify] [--relative-command]` | Register codeintel with the agents installed on this machine (`auto`, the default), then prove it by completing a real MCP handshake against the registered command |
|
|
360
360
|
| `codeintel setup [project_root] [--all] [--index] [--warm] [--install-uv] [--install-deps] [--json]` | Prepare backends + index this repo (`--all` = one command: do everything automatable, idempotent); ends with a health report + **Next:** steps |
|
|
361
361
|
| `codeintel prompt [project_root] [--agent auto\|claude\|codex\|gemini\|zed] [--fresh] [--deep]` | Print a paste-to-your-agent setup prompt; probes health and emits only the outstanding steps (or "just restart me" when already healthy). `--fresh` = the full sequence from `pip install`, to send a friend |
|
|
362
|
-
| `codeintel index [project_root]` | Index a
|
|
362
|
+
| `codeintel index [project_root] [--quiet]` | Index a repo (semantic embeddings + best-effort graph & map refresh), with a live progress display; `--quiet` prints only the result line |
|
|
363
363
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
364
364
|
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
365
365
|
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
|
|
@@ -367,10 +367,10 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
367
367
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
368
368
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
369
369
|
| `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
|
|
370
|
-
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear
|
|
370
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear this repo's index — **both** semantic and graph — so it's as if never indexed; `--all` wipes every repo. Recovers from a corrupt/stale DB |
|
|
371
371
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
372
372
|
|
|
373
|
-
Human-facing commands (`doctor`, `
|
|
373
|
+
Human-facing commands (`doctor`, `setup`, `prompt`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
374
374
|
|
|
375
375
|
**Exit codes**, so a `make` target or CI step can gate on `$?`:
|
|
376
376
|
|
|
@@ -407,7 +407,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
|
|
|
407
407
|
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
408
408
|
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
409
409
|
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
410
|
-
| `CODEINTEL_HOME` | Where
|
|
410
|
+
| `CODEINTEL_HOME` | Where per-machine state lives (default `~/.codeintel`) — the index cache, the global `config.toml`, and `auth.toml`. Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
|
|
411
411
|
|
|
412
412
|
## Privacy & dependencies
|
|
413
413
|
|
|
@@ -580,7 +580,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
|
|
|
580
580
|
cd codeintel
|
|
581
581
|
pip install -e .[dev]
|
|
582
582
|
|
|
583
|
-
pytest tests/ -q # ~
|
|
583
|
+
pytest tests/ -q # ~740 tests, ~30s; fails under 83% coverage
|
|
584
584
|
ruff check src tests # lint
|
|
585
585
|
mypy # types (src/ only)
|
|
586
586
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -279,7 +279,7 @@ Then it launches the exact command it registered and drives a real MCP handshake
|
|
|
279
279
|
```text
|
|
280
280
|
v claude: registered at /Users/you/.claude.json
|
|
281
281
|
|
|
282
|
-
v verified: codeintel 0.
|
|
282
|
+
v verified: codeintel 0.18.0 — 4 tools (code.query, code.status, code.doctor, code.map)
|
|
283
283
|
```
|
|
284
284
|
|
|
285
285
|
If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
|
|
@@ -396,7 +396,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
396
396
|
| `codeintel install [--agent auto\|claude\|codex\|gemini\|zed\|all] [--no-verify] [--relative-command]` | Register codeintel with the agents installed on this machine (`auto`, the default), then prove it by completing a real MCP handshake against the registered command |
|
|
397
397
|
| `codeintel setup [project_root] [--all] [--index] [--warm] [--install-uv] [--install-deps] [--json]` | Prepare backends + index this repo (`--all` = one command: do everything automatable, idempotent); ends with a health report + **Next:** steps |
|
|
398
398
|
| `codeintel prompt [project_root] [--agent auto\|claude\|codex\|gemini\|zed] [--fresh] [--deep]` | Print a paste-to-your-agent setup prompt; probes health and emits only the outstanding steps (or "just restart me" when already healthy). `--fresh` = the full sequence from `pip install`, to send a friend |
|
|
399
|
-
| `codeintel index [project_root]` | Index a
|
|
399
|
+
| `codeintel index [project_root] [--quiet]` | Index a repo (semantic embeddings + best-effort graph & map refresh), with a live progress display; `--quiet` prints only the result line |
|
|
400
400
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
401
401
|
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
402
402
|
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
|
|
@@ -404,10 +404,10 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
404
404
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
405
405
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
406
406
|
| `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
|
|
407
|
-
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear
|
|
407
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear this repo's index — **both** semantic and graph — so it's as if never indexed; `--all` wipes every repo. Recovers from a corrupt/stale DB |
|
|
408
408
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
409
409
|
|
|
410
|
-
Human-facing commands (`doctor`, `
|
|
410
|
+
Human-facing commands (`doctor`, `setup`, `prompt`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
411
411
|
|
|
412
412
|
**Exit codes**, so a `make` target or CI step can gate on `$?`:
|
|
413
413
|
|
|
@@ -444,7 +444,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
|
|
|
444
444
|
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
445
445
|
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
446
446
|
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
447
|
-
| `CODEINTEL_HOME` | Where
|
|
447
|
+
| `CODEINTEL_HOME` | Where per-machine state lives (default `~/.codeintel`) — the index cache, the global `config.toml`, and `auth.toml`. Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
|
|
448
448
|
|
|
449
449
|
## Privacy & dependencies
|
|
450
450
|
|
|
@@ -617,7 +617,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
|
|
|
617
617
|
cd codeintel
|
|
618
618
|
pip install -e .[dev]
|
|
619
619
|
|
|
620
|
-
pytest tests/ -q # ~
|
|
620
|
+
pytest tests/ -q # ~740 tests, ~30s; fails under 83% coverage
|
|
621
621
|
ruff check src tests # lint
|
|
622
622
|
mypy # types (src/ only)
|
|
623
623
|
```
|
|
@@ -30,7 +30,9 @@ src/codeintel/mapper.py
|
|
|
30
30
|
src/codeintel/metrics.py
|
|
31
31
|
src/codeintel/onboarding.py
|
|
32
32
|
src/codeintel/outcome.py
|
|
33
|
+
src/codeintel/paths.py
|
|
33
34
|
src/codeintel/policy.py
|
|
35
|
+
src/codeintel/progress.py
|
|
34
36
|
src/codeintel/provider.py
|
|
35
37
|
src/codeintel/py.typed
|
|
36
38
|
src/codeintel/redact.py
|
|
@@ -89,6 +91,8 @@ tests/test_hardening.py
|
|
|
89
91
|
tests/test_http_auth.py
|
|
90
92
|
tests/test_http_server.py
|
|
91
93
|
tests/test_incompleteness.py
|
|
94
|
+
tests/test_index_progress.py
|
|
95
|
+
tests/test_indexer_binary.py
|
|
92
96
|
tests/test_installer.py
|
|
93
97
|
tests/test_integration.py
|
|
94
98
|
tests/test_loc_census.py
|
|
@@ -101,11 +105,14 @@ tests/test_model_dimension.py
|
|
|
101
105
|
tests/test_never_raise.py
|
|
102
106
|
tests/test_onboarding.py
|
|
103
107
|
tests/test_rbac.py
|
|
108
|
+
tests/test_redaction_boundary.py
|
|
104
109
|
tests/test_reindexer.py
|
|
105
110
|
tests/test_rerank.py
|
|
106
111
|
tests/test_reset.py
|
|
107
112
|
tests/test_semantic_provider.py
|
|
108
113
|
tests/test_source_kind.py
|
|
114
|
+
tests/test_staleness.py
|
|
115
|
+
tests/test_symbol_context.py
|
|
109
116
|
tests/test_term.py
|
|
110
117
|
tests/test_treesitter.py
|
|
111
118
|
tests/test_verify.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.18.0"
|
|
@@ -131,13 +131,19 @@ def main() -> None:
|
|
|
131
131
|
subparsers.add_parser("serve", help="Start the MCP server")
|
|
132
132
|
|
|
133
133
|
# index subcommand
|
|
134
|
-
index_parser = subparsers.add_parser(
|
|
134
|
+
index_parser = subparsers.add_parser(
|
|
135
|
+
"index", parents=[color_parent],
|
|
136
|
+
help="Index a project for semantic search")
|
|
135
137
|
index_parser.add_argument(
|
|
136
138
|
"project_root",
|
|
137
139
|
nargs="?",
|
|
138
140
|
default=None,
|
|
139
141
|
help="Project root directory (default: cwd)",
|
|
140
142
|
)
|
|
143
|
+
index_parser.add_argument(
|
|
144
|
+
"--quiet", "-q", action="store_true",
|
|
145
|
+
help="Suppress the live progress display and header; keep only the result line",
|
|
146
|
+
)
|
|
141
147
|
|
|
142
148
|
# query subcommand
|
|
143
149
|
query_parser = subparsers.add_parser("query", help="Query the code intelligence engine")
|
|
@@ -26,6 +26,7 @@ import os
|
|
|
26
26
|
import pathlib
|
|
27
27
|
import tomllib # stdlib on Python 3.11+ (the project's minimum)
|
|
28
28
|
|
|
29
|
+
from codeintel.paths import codeintel_home
|
|
29
30
|
from codeintel.policy import TieringPolicy
|
|
30
31
|
|
|
31
32
|
logger = logging.getLogger("codeintel")
|
|
@@ -78,7 +79,13 @@ def _auth_config_path() -> pathlib.Path | None:
|
|
|
78
79
|
if env:
|
|
79
80
|
p = pathlib.Path(env)
|
|
80
81
|
return p if p.is_file() else None
|
|
81
|
-
|
|
82
|
+
try:
|
|
83
|
+
default = codeintel_home() / "auth.toml"
|
|
84
|
+
except Exception:
|
|
85
|
+
# No CODEINTEL_HOME and no resolvable home. `load_auth` promises never to raise, and its
|
|
86
|
+
# whole contract is "absent config → auth disabled"; a host with nowhere to look is the
|
|
87
|
+
# absent case, not a crash. (Previously this read `Path.home()` inline and raised.)
|
|
88
|
+
return None
|
|
82
89
|
return default if default.is_file() else None
|
|
83
90
|
|
|
84
91
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""`codeintel index` — build the semantic index, then best-effort refresh the graph and the map."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from codeintel.commands._common import resolve_root
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _ProgressLogBridge(logging.Handler):
|
|
11
|
+
"""Routes WARNING+ log records from the indexer to a live progress counter's ``notice()``, so a
|
|
12
|
+
per-file skip warning prints as a clean permanent line ABOVE the redrawing status line instead
|
|
13
|
+
of colliding with it (``0 chunksskipping …``). Installed only for a live (TTY) run and removed
|
|
14
|
+
the moment indexing returns — a non-TTY has no live line to protect, and the warnings keep their
|
|
15
|
+
normal stderr path there."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, counter: Any) -> None:
|
|
18
|
+
super().__init__(level=logging.WARNING)
|
|
19
|
+
self._counter = counter
|
|
20
|
+
|
|
21
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
22
|
+
try:
|
|
23
|
+
self._counter.notice(record.getMessage())
|
|
24
|
+
except Exception:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run(args: Any) -> int:
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
import codeintel.term as term
|
|
32
|
+
from codeintel.config import load_config
|
|
33
|
+
from codeintel.indexer import Indexer
|
|
34
|
+
from codeintel.semantic_db import SemanticDb, default_db_path
|
|
35
|
+
|
|
36
|
+
project_root = resolve_root(args)
|
|
37
|
+
# Validate before doing anything. `codeintel index /typo/path` walked nothing, found nothing,
|
|
38
|
+
# and printed "Nothing new to index" at exit 0 — indistinguishable from a correct incremental
|
|
39
|
+
# run, which is the worst possible response to a mistyped path in a script.
|
|
40
|
+
if not os.path.isdir(project_root):
|
|
41
|
+
print(f"index failed: not a directory: {project_root}")
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
# A console bound to the CURRENT stdout (honouring --no-color/--ascii/NO_COLOR), so progress is
|
|
45
|
+
# captured under test and routed correctly whether or not main() already ran configure(). The
|
|
46
|
+
# LiveCounter auto-goes silent on a non-TTY; --quiet suppresses it (and the header) outright.
|
|
47
|
+
console = term.Console(
|
|
48
|
+
stream=sys.stdout,
|
|
49
|
+
no_color=getattr(args, "no_color", False),
|
|
50
|
+
ascii_mode=(True if getattr(args, "ascii", False) else None),
|
|
51
|
+
)
|
|
52
|
+
quiet = getattr(args, "quiet", False)
|
|
53
|
+
counter = None if quiet else term.LiveCounter(console)
|
|
54
|
+
|
|
55
|
+
failed = False
|
|
56
|
+
# Wrap the whole semantic pass so a setup failure (e.g. an unresolvable home dir →
|
|
57
|
+
# Path.home() raising) degrades with a message, like every other subcommand, not a traceback.
|
|
58
|
+
try:
|
|
59
|
+
cfg = load_config(project_root)
|
|
60
|
+
if not quiet:
|
|
61
|
+
# One dim header naming the model + chunk strategy: it says WHAT is running (and that it
|
|
62
|
+
# is all local), and on a cold cache it pre-explains the model-download stall.
|
|
63
|
+
strat = str(cfg.get("chunk_strategy", "syntax"))
|
|
64
|
+
strat_label = "def-aligned" if strat == "syntax" else "line-window"
|
|
65
|
+
model = str(cfg.get("model") or "BAAI/bge-small-en-v1.5")
|
|
66
|
+
sep = "." if console.ascii else "·"
|
|
67
|
+
print(console.header("index", project_root))
|
|
68
|
+
print(f" {console.dim(f'{model} {sep} {strat_label} chunks')}")
|
|
69
|
+
print()
|
|
70
|
+
db_path = default_db_path(str(cfg.get("model") or ""))
|
|
71
|
+
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
|
72
|
+
db = SemanticDb(db_path)
|
|
73
|
+
# While a live status line is on screen, the indexer's skip WARNINGs must not print raw to
|
|
74
|
+
# the terminal (they land mid-line). Route them through the counter's notice() for the span
|
|
75
|
+
# of the index pass only; a non-TTY needs no bridge (its status lines end in \n).
|
|
76
|
+
bridge = _ProgressLogBridge(counter) if (counter is not None and counter.live) else None
|
|
77
|
+
idx_logger = logging.getLogger("codeintel")
|
|
78
|
+
try:
|
|
79
|
+
db.init()
|
|
80
|
+
if bridge is not None:
|
|
81
|
+
idx_logger.addHandler(bridge)
|
|
82
|
+
try:
|
|
83
|
+
count = Indexer(
|
|
84
|
+
db,
|
|
85
|
+
model_name=str(cfg.get("model") or "BAAI/bge-small-en-v1.5"),
|
|
86
|
+
window=int(cfg.get("window", 20)),
|
|
87
|
+
stride=int(cfg.get("stride", 10)),
|
|
88
|
+
max_chunks=int(cfg.get("max_chunks", 500)),
|
|
89
|
+
max_total_chunks=int(cfg.get("max_total_chunks", 100000)),
|
|
90
|
+
chunk_strategy=str(cfg.get("chunk_strategy", "syntax")),
|
|
91
|
+
progress=counter,
|
|
92
|
+
).index(project_root)
|
|
93
|
+
finally:
|
|
94
|
+
if bridge is not None:
|
|
95
|
+
idx_logger.removeHandler(bridge)
|
|
96
|
+
# Seal the live line before the plain summary: commit the ✓ row on real work, else erase
|
|
97
|
+
# it so "Nothing new" / the failure message stands on its own clean line.
|
|
98
|
+
if counter:
|
|
99
|
+
counter.finish(commit=count > 0)
|
|
100
|
+
if count > 0:
|
|
101
|
+
print(f"Indexed {count} chunks")
|
|
102
|
+
elif count < 0:
|
|
103
|
+
# Indexer.index() returns -1 for an unrecoverable failure. `> 0` sent that into
|
|
104
|
+
# the "Nothing new to index" branch, so a total failure read as a clean no-op.
|
|
105
|
+
print("index failed — the indexer could not complete (see the warnings above)")
|
|
106
|
+
failed = True
|
|
107
|
+
else:
|
|
108
|
+
print("Nothing new to index")
|
|
109
|
+
finally:
|
|
110
|
+
db.close()
|
|
111
|
+
except Exception as exc:
|
|
112
|
+
if counter:
|
|
113
|
+
counter.finish(commit=False)
|
|
114
|
+
print(f"index failed: {exc}")
|
|
115
|
+
failed = True
|
|
116
|
+
|
|
117
|
+
# best-effort graph reindex — an opaque subprocess, so a ticking elapsed heartbeat is the honest
|
|
118
|
+
# fidelity (no counts cross the boundary): the number keeps moving so a minute-long reindex never
|
|
119
|
+
# reads as a hang, but we never fabricate a percentage we can't see.
|
|
120
|
+
import shutil
|
|
121
|
+
|
|
122
|
+
if shutil.which("codebase-memory-mcp"):
|
|
123
|
+
beat = None if quiet else term.LiveHeartbeat(console, "graph reindex").start()
|
|
124
|
+
try:
|
|
125
|
+
from codeintel.reindexer import Reindexer
|
|
126
|
+
|
|
127
|
+
Reindexer()._graph_reindex(project_root)
|
|
128
|
+
if beat:
|
|
129
|
+
beat.stop("ok")
|
|
130
|
+
except Exception:
|
|
131
|
+
if beat:
|
|
132
|
+
beat.stop("warn", "skipped")
|
|
133
|
+
|
|
134
|
+
# best-effort map refresh after index (fast, kept silent — not worth a checklist row)
|
|
135
|
+
try:
|
|
136
|
+
from codeintel.mapper import MapGenerator
|
|
137
|
+
from codeintel.providers.graph import GraphProvider
|
|
138
|
+
|
|
139
|
+
gen = MapGenerator(GraphProvider())
|
|
140
|
+
gen.write(project_root, gen.generate(project_root))
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
return 1 if failed else 0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""`codeintel reset` — clear
|
|
1
|
+
"""`codeintel reset` — clear a repo's index (semantic + graph), or the whole cache with --all."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
from typing import Any
|
|
@@ -18,7 +18,7 @@ def run(args: Any) -> int:
|
|
|
18
18
|
return 1
|
|
19
19
|
target = "ALL projects" if args.all else project_root
|
|
20
20
|
count = preview.get("count", 0)
|
|
21
|
-
ans = input(f"Reset
|
|
21
|
+
ans = input(f"Reset index for {target} ({count} entries)? [y/N] ").strip().lower()
|
|
22
22
|
if ans not in ("y", "yes"):
|
|
23
23
|
print("aborted")
|
|
24
24
|
return 0
|
|
@@ -4,6 +4,8 @@ import logging
|
|
|
4
4
|
import pathlib
|
|
5
5
|
import tomllib # stdlib since 3.11, which is this package's floor
|
|
6
6
|
|
|
7
|
+
from codeintel.paths import codeintel_home
|
|
8
|
+
|
|
7
9
|
logger = logging.getLogger("codeintel")
|
|
8
10
|
|
|
9
11
|
_DEFAULTS: dict = {
|
|
@@ -18,7 +20,12 @@ _DEFAULTS: dict = {
|
|
|
18
20
|
"model": "BAAI/bge-small-en-v1.5",
|
|
19
21
|
"chunk_strategy": "syntax", # syntax-aware (def/class boundaries) vs fixed line windows
|
|
20
22
|
"rerank": "on", # hybrid lexical+semantic rerank of search results
|
|
21
|
-
|
|
23
|
+
# Cosine candidates retrieved, fused and re-ranked before the top-k is returned. Was 30, but
|
|
24
|
+
# the semantic provider hardcoded its own `_display_k * 6` widening that overrode it, so the
|
|
25
|
+
# effective breadth has always been 60; 30 only ever described behaviour the code didn't have.
|
|
26
|
+
# The provider now honours this key, so the default is set to the value that was really in use
|
|
27
|
+
# — otherwise "fixing" the wiring would have silently narrowed everyone's retrieval by half.
|
|
28
|
+
"rerank_candidates": 60,
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
# Values restricted to a fixed set — anything else falls back to the default.
|
|
@@ -80,12 +87,27 @@ def _coerce(cfg: dict) -> dict:
|
|
|
80
87
|
return out
|
|
81
88
|
|
|
82
89
|
|
|
90
|
+
def _global_config() -> dict:
|
|
91
|
+
"""The machine-wide ``config.toml``, or ``{}`` when there isn't one.
|
|
92
|
+
|
|
93
|
+
Resolving the directory can RAISE (no ``CODEINTEL_HOME`` and no resolvable home — a container
|
|
94
|
+
UID with no passwd entry). That used to propagate straight out of ``load_config``: the call
|
|
95
|
+
read ``pathlib.Path.home()`` inline, so it blew up before ``_read_toml``'s handler could see
|
|
96
|
+
it, and every query on such a host died as an opaque ``provider-error``. "There is nowhere to
|
|
97
|
+
look for a global config" is not an error — it means there is no global config."""
|
|
98
|
+
try:
|
|
99
|
+
return _read_toml(codeintel_home() / "config.toml")
|
|
100
|
+
except Exception as exc:
|
|
101
|
+
logger.debug("no resolvable config directory (%s) — using defaults + project config", exc)
|
|
102
|
+
return {}
|
|
103
|
+
|
|
104
|
+
|
|
83
105
|
def load_config(project_root: str | None = None) -> dict:
|
|
84
106
|
"""Return the merged, validated config: defaults < global < project. Values that fail
|
|
85
107
|
validation fall back to their default (logged), so a bad config file never breaks a query."""
|
|
86
108
|
root = pathlib.Path(project_root) if project_root is not None else pathlib.Path.cwd()
|
|
87
109
|
|
|
88
|
-
global_cfg =
|
|
110
|
+
global_cfg = _global_config()
|
|
89
111
|
project_cfg = _read_toml(root / ".codeintel.toml")
|
|
90
112
|
|
|
91
113
|
merged = {**_DEFAULTS, **global_cfg, **project_cfg}
|
|
@@ -69,9 +69,24 @@ def open_contained(root_real: str, candidate: str | Path, **kwargs):
|
|
|
69
69
|
Raises ``ContainmentError`` rather than returning a sentinel, so a caller that wraps reads in a
|
|
70
70
|
broad ``except Exception`` cannot quietly downgrade an escape to "file not found" — the escape
|
|
71
71
|
is logged at WARNING here regardless of what the caller does with the exception.
|
|
72
|
+
|
|
73
|
+
A path that simply DOES NOT EXIST is reported as ``FileNotFoundError``, not as an escape.
|
|
74
|
+
``contained_path`` answers the single question "may this be read", and a deleted file is a
|
|
75
|
+
legitimate no — but it is also the most ordinary thing that can happen to an indexed file, and
|
|
76
|
+
reporting it as a refusal meant every deletion between indexing and a query logged a WARNING
|
|
77
|
+
asking whether a symlink had been "planted after indexing" and rendered the hit as
|
|
78
|
+
``[refused: resolves outside the indexed root]``. That is a false security alarm on a routine
|
|
79
|
+
event, and it drowns the real ones. The distinction is made HERE rather than by relaxing
|
|
80
|
+
``contained_path``, whose ``None`` also drives ``_cleanup_deleted``'s reconciliation of deleted
|
|
81
|
+
files — loosening it there would leave their rows in the index forever.
|
|
72
82
|
"""
|
|
73
83
|
safe = contained_path(root_real, candidate)
|
|
74
84
|
if safe is None:
|
|
85
|
+
# lexists(), not exists(): a dangling symlink still EXISTS as a link, and a link whose
|
|
86
|
+
# target vanished is a construct worth refusing loudly rather than calling "not found".
|
|
87
|
+
if not os.path.lexists(candidate):
|
|
88
|
+
logger.debug("indexed file no longer exists: %s", candidate)
|
|
89
|
+
raise FileNotFoundError(str(candidate))
|
|
75
90
|
logger.warning(
|
|
76
91
|
"refusing to read %s — it does not resolve to a file inside %s (symlink or hard link "
|
|
77
92
|
"planted after indexing?)", candidate, root_real,
|
|
@@ -24,7 +24,6 @@ _AUTO_ENGINE: dict[str, str] = {
|
|
|
24
24
|
"overview": "graph",
|
|
25
25
|
"changed": "graph",
|
|
26
26
|
"changes": "graph",
|
|
27
|
-
"deadcode": "graph",
|
|
28
27
|
"hotspots": "graph",
|
|
29
28
|
"symbol": "lsp",
|
|
30
29
|
"search": "semantic",
|
|
@@ -33,8 +32,9 @@ _AUTO_ENGINE: dict[str, str] = {
|
|
|
33
32
|
|
|
34
33
|
# Ops whose answer depends on live, unhashable state (the git worktree) rather than the indexed
|
|
35
34
|
# content the cache key is built from — caching them would serve a stale answer within a freshness
|
|
36
|
-
# generation. `changed` reads uncommitted edits; `
|
|
37
|
-
#
|
|
35
|
+
# generation. `changed` reads uncommitted edits; `hotspots` is a pure function of the index and
|
|
36
|
+
# stays cached (correctly keyed by the freshness generation). A retired/unknown op falls through
|
|
37
|
+
# to the graph engine (the `_AUTO_ENGINE.get(op, "graph")` default), which safe-nulls it.
|
|
38
38
|
_UNCACHED_OPS: frozenset[str] = frozenset({"changed", "changes"})
|
|
39
39
|
|
|
40
40
|
# Engines whose slot can be filled after construction (see `Gateway.adopt_provider`).
|