codecortex 0.13.0__tar.gz → 0.13.1__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.13.0/src/codecortex.egg-info → codecortex-0.13.1}/PKG-INFO +26 -4
- {codecortex-0.13.0 → codecortex-0.13.1}/README.md +25 -3
- {codecortex-0.13.0 → codecortex-0.13.1/src/codecortex.egg-info}/PKG-INFO +26 -4
- codecortex-0.13.1/src/codeintel/__init__.py +1 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/graph.py +73 -11
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/reindexer.py +16 -2
- codecortex-0.13.1/tests/test_graph_provider.py +310 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_graph_stdin.py +4 -1
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_reindexer.py +72 -0
- codecortex-0.13.0/src/codeintel/__init__.py +0 -1
- codecortex-0.13.0/tests/test_graph_provider.py +0 -170
- {codecortex-0.13.0 → codecortex-0.13.1}/LICENSE +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/pyproject.toml +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/setup.cfg +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/SOURCES.txt +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/dependency_links.txt +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/entry_points.txt +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/requires.txt +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/top_level.txt +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/__main__.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/auth.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/cache.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/__init__.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/_common.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/doctor.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/gen_token.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/graph.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/index.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/install.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/map.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/query.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/reset.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/serve.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/serve_http.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/setup.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/status.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/config.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/doctor.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/gateway.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/grapher.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/http_server.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/indexer.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/injector.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/installer.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/logconfig.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/mapper.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/metrics.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/onboarding.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/policy.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/provider.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/__init__.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/lsp.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/none.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/semantic.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/py.typed +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/reset.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/searcher.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/semantic_db.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/server.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/term.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/verify.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/viewer/__init__.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/viewer/graph_template.html +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cache.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_chunking.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cli_commands.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cli_help.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_config.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_doctor.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_e2e.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_engine_adoption.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_enterprise.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_gateway.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_graph_real.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_grapher.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_hardening.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_http_auth.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_http_server.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_installer.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_integration.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_lsp_provider.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_lsp_real.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mapper.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mcp_handshake.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mcp_server.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_model_dimension.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_never_raise.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_onboarding.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_rbac.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_rerank.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_reset.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_semantic_provider.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_term.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_treesitter.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_verify.py +0 -0
- {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_verify_call.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
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
|
|
@@ -111,7 +111,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
|
|
|
111
111
|
- **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
|
|
112
112
|
- **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
|
|
113
113
|
- **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
|
|
114
|
-
- **Fast on repeat, never
|
|
114
|
+
- **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
|
|
115
115
|
- **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
|
|
116
116
|
- **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
|
|
117
117
|
|
|
@@ -274,6 +274,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
|
|
|
274
274
|
|
|
275
275
|
Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
|
|
276
276
|
|
|
277
|
+
### Keeping answers fresh
|
|
278
|
+
|
|
279
|
+
The three engines have genuinely different freshness models, and it's worth knowing which you're
|
|
280
|
+
reading:
|
|
281
|
+
|
|
282
|
+
| Engine | Freshness |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
|
|
285
|
+
| `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
|
|
286
|
+
| `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
|
|
287
|
+
|
|
288
|
+
So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
|
|
289
|
+
describes code you just changed — or a symbol you just added comes back
|
|
290
|
+
`reason: "not-in-graph"` — that's the signal to re-run:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
codeintel index /path/to/repo
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
|
|
297
|
+
rather than a bare reason.
|
|
298
|
+
|
|
277
299
|
Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
|
|
278
300
|
|
|
279
301
|
## Documentation
|
|
@@ -297,12 +319,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
297
319
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
298
320
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
299
321
|
| `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) |
|
|
300
|
-
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
322
|
+
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
|
|
301
323
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
302
324
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
303
325
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
304
326
|
| `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) |
|
|
305
|
-
| `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
327
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
306
328
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
307
329
|
|
|
308
330
|
Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
@@ -74,7 +74,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
|
|
|
74
74
|
- **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
|
|
75
75
|
- **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
|
|
76
76
|
- **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
|
|
77
|
-
- **Fast on repeat, never
|
|
77
|
+
- **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
|
|
78
78
|
- **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
|
|
79
79
|
- **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
|
|
80
80
|
|
|
@@ -237,6 +237,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
|
|
|
237
237
|
|
|
238
238
|
Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
|
|
239
239
|
|
|
240
|
+
### Keeping answers fresh
|
|
241
|
+
|
|
242
|
+
The three engines have genuinely different freshness models, and it's worth knowing which you're
|
|
243
|
+
reading:
|
|
244
|
+
|
|
245
|
+
| Engine | Freshness |
|
|
246
|
+
|---|---|
|
|
247
|
+
| `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
|
|
248
|
+
| `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
|
|
249
|
+
| `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
|
|
250
|
+
|
|
251
|
+
So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
|
|
252
|
+
describes code you just changed — or a symbol you just added comes back
|
|
253
|
+
`reason: "not-in-graph"` — that's the signal to re-run:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
codeintel index /path/to/repo
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
|
|
260
|
+
rather than a bare reason.
|
|
261
|
+
|
|
240
262
|
Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
|
|
241
263
|
|
|
242
264
|
## Documentation
|
|
@@ -260,12 +282,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
260
282
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
261
283
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
262
284
|
| `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) |
|
|
263
|
-
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
285
|
+
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
|
|
264
286
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
265
287
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
266
288
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
267
289
|
| `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) |
|
|
268
|
-
| `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
290
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
269
291
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
270
292
|
|
|
271
293
|
Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
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
|
|
@@ -111,7 +111,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
|
|
|
111
111
|
- **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
|
|
112
112
|
- **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
|
|
113
113
|
- **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
|
|
114
|
-
- **Fast on repeat, never
|
|
114
|
+
- **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
|
|
115
115
|
- **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
|
|
116
116
|
- **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
|
|
117
117
|
|
|
@@ -274,6 +274,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
|
|
|
274
274
|
|
|
275
275
|
Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
|
|
276
276
|
|
|
277
|
+
### Keeping answers fresh
|
|
278
|
+
|
|
279
|
+
The three engines have genuinely different freshness models, and it's worth knowing which you're
|
|
280
|
+
reading:
|
|
281
|
+
|
|
282
|
+
| Engine | Freshness |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
|
|
285
|
+
| `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
|
|
286
|
+
| `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
|
|
287
|
+
|
|
288
|
+
So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
|
|
289
|
+
describes code you just changed — or a symbol you just added comes back
|
|
290
|
+
`reason: "not-in-graph"` — that's the signal to re-run:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
codeintel index /path/to/repo
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
|
|
297
|
+
rather than a bare reason.
|
|
298
|
+
|
|
277
299
|
Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
|
|
278
300
|
|
|
279
301
|
## Documentation
|
|
@@ -297,12 +319,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
297
319
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
298
320
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
299
321
|
| `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) |
|
|
300
|
-
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
322
|
+
| `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
|
|
301
323
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
302
324
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
303
325
|
| `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
|
|
304
326
|
| `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) |
|
|
305
|
-
| `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
327
|
+
| `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
|
|
306
328
|
| `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
|
|
307
329
|
|
|
308
330
|
Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.13.1"
|
|
@@ -17,6 +17,36 @@ def _cypher_literal(s: Any) -> str:
|
|
|
17
17
|
return str(s).replace("\\", "\\\\").replace('"', '\\"')
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
# Every op _dispatch recognizes. Kept beside it so "unsupported op" and "op found nothing" stay
|
|
21
|
+
# distinguishable — they were the same `None` before, and the resulting `unsupported-op` on a
|
|
22
|
+
# perfectly supported `callers` was the most misleading string the never-raise envelope produced.
|
|
23
|
+
_GRAPH_OPS = frozenset({
|
|
24
|
+
"impact", "context", "callers", "callees", "chain", "pattern",
|
|
25
|
+
"overview", "changed", "changes", "deadcode", "hotspots",
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _repo_display_name(root: str) -> str:
|
|
30
|
+
"""The repo's own directory name, for headings a human will read.
|
|
31
|
+
|
|
32
|
+
Resolves first, because callers routinely pass "." (`codeintel map .`) — the basename of which
|
|
33
|
+
is "." and would title the committed map file with a dot."""
|
|
34
|
+
if not root:
|
|
35
|
+
return ""
|
|
36
|
+
try:
|
|
37
|
+
return os.path.basename(os.path.realpath(root).rstrip(os.sep))
|
|
38
|
+
except Exception:
|
|
39
|
+
return ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _int_or_zero(value: Any) -> int:
|
|
43
|
+
"""A node count from an untrusted backend payload, or 0 when it is missing/not a number."""
|
|
44
|
+
try:
|
|
45
|
+
return int(value)
|
|
46
|
+
except (TypeError, ValueError):
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
|
|
20
50
|
class GraphProvider:
|
|
21
51
|
"""Wraps the codebase-memory-mcp CLI. Never raises.
|
|
22
52
|
|
|
@@ -118,7 +148,14 @@ class GraphProvider:
|
|
|
118
148
|
The real codebase-memory-mcp returns ``{"projects": [...]}``; a bare list is the
|
|
119
149
|
older/mocked shape — accept both. Prefer an exact ``root_path`` match; otherwise the
|
|
120
150
|
LONGEST prefix match (so ``.../project/codeintel`` resolves to codeintel, not its
|
|
121
|
-
parent ``.../project``).
|
|
151
|
+
parent ``.../project``). Static so ``_resolve_project`` and ``probe`` share it.
|
|
152
|
+
|
|
153
|
+
The backend can hold MORE THAN ONE project for the same root — typically one registered
|
|
154
|
+
under a short name and one under a path slug — and the two drift apart independently.
|
|
155
|
+
Returning the first match meant a query could be answered from a months-stale index while
|
|
156
|
+
a complete one sat beside it: observed on this repo as 1475 nodes vs 2631 for the same
|
|
157
|
+
path, which is how `callers` reported a function's pre-refactor shape hours after the
|
|
158
|
+
refactor. Among exact matches, prefer the most complete index."""
|
|
122
159
|
# Normalize the input to an absolute realpath: the backend stores absolute root_paths, so a
|
|
123
160
|
# relative ``project_root`` (e.g. `codeintel map .` passing ".") would otherwise never match
|
|
124
161
|
# — the bug where the map/query silently reported "not indexed" from inside the repo.
|
|
@@ -129,7 +166,7 @@ class GraphProvider:
|
|
|
129
166
|
entries = raw.get("projects", []) if isinstance(raw, dict) else raw
|
|
130
167
|
if not isinstance(entries, list):
|
|
131
168
|
return None
|
|
132
|
-
exact:
|
|
169
|
+
exact: list[dict] = []
|
|
133
170
|
best_prefix_len = -1
|
|
134
171
|
best_prefix_name: str | None = None
|
|
135
172
|
for entry in entries:
|
|
@@ -139,12 +176,22 @@ class GraphProvider:
|
|
|
139
176
|
if not rp:
|
|
140
177
|
continue
|
|
141
178
|
if rp == project_root:
|
|
142
|
-
exact
|
|
143
|
-
|
|
179
|
+
exact.append(entry)
|
|
180
|
+
continue
|
|
144
181
|
if project_root.startswith(rp.rstrip("/") + "/") and len(rp) > best_prefix_len:
|
|
145
182
|
best_prefix_len = len(rp)
|
|
146
183
|
best_prefix_name = entry.get("name")
|
|
147
|
-
|
|
184
|
+
|
|
185
|
+
if exact:
|
|
186
|
+
# Node count is the available completeness signal — list_projects carries no indexed-at
|
|
187
|
+
# timestamp, and head_sha is recorded per registration rather than per index pass, so
|
|
188
|
+
# duplicates routinely report the same SHA with wildly different graphs. `max` keeps
|
|
189
|
+
# the FIRST maximal entry, so with no completeness signal to go on (ties, or a backend
|
|
190
|
+
# that omits `nodes`) this falls back to the original first-listed rule rather than
|
|
191
|
+
# inventing an ordering.
|
|
192
|
+
best = max(exact, key=lambda e: _int_or_zero(e.get("nodes")))
|
|
193
|
+
return best.get("name")
|
|
194
|
+
return best_prefix_name
|
|
148
195
|
|
|
149
196
|
def _resolve_project(self, project_root: str) -> str | None:
|
|
150
197
|
with self._project_cache_lock:
|
|
@@ -400,12 +447,17 @@ class GraphProvider:
|
|
|
400
447
|
except Exception:
|
|
401
448
|
return None
|
|
402
449
|
|
|
403
|
-
def _op_overview(self, target: str, project: str, timeout_ms: int) -> str | None:
|
|
450
|
+
def _op_overview(self, target: str, project: str, timeout_ms: int, root: str = "") -> str | None:
|
|
404
451
|
try:
|
|
405
452
|
raw = self._run("get_architecture", {"project": project}, timeout_ms)
|
|
406
453
|
if not isinstance(raw, dict):
|
|
407
454
|
return None
|
|
408
|
-
name
|
|
455
|
+
# Title with the REPO's own name, not the backend's project id. That id is often a
|
|
456
|
+
# flattened absolute path (`Users-alice-Documents-project-myrepo`), and this heading
|
|
457
|
+
# lands in CODE_INTEL.md — a file that gets committed and pushed, so an internal
|
|
458
|
+
# identifier there leaks the author's home directory layout into the repository.
|
|
459
|
+
name = _repo_display_name(root)
|
|
460
|
+
name = name or str(raw.get("project") or project)
|
|
409
461
|
parts = [f"## Architecture: {name}"]
|
|
410
462
|
tn, te = raw.get("total_nodes"), raw.get("total_edges")
|
|
411
463
|
if tn is not None or te is not None:
|
|
@@ -592,10 +644,20 @@ class GraphProvider:
|
|
|
592
644
|
hint=f"run: codeintel index {root_str} (or: codeintel doctor)",
|
|
593
645
|
)
|
|
594
646
|
|
|
595
|
-
|
|
596
|
-
if result_text is None:
|
|
647
|
+
if op_str not in _GRAPH_OPS:
|
|
597
648
|
return safe_null_result(op_str, target_str, engine="graph", reason="unsupported-op")
|
|
598
649
|
|
|
650
|
+
result_text = self._dispatch(op_str, target_str, project, timeout_ms, root_str)
|
|
651
|
+
if result_text is None:
|
|
652
|
+
# A supported op that matched nothing is NOT an unsupported op, and saying so sends
|
|
653
|
+
# the agent looking for a different tool when the real answer is almost always a
|
|
654
|
+
# stale index. Name the cause and the one command that fixes it.
|
|
655
|
+
return safe_null_result(
|
|
656
|
+
op_str, target_str, engine="graph", reason="not-in-graph",
|
|
657
|
+
hint=f"`{target_str}` is not in the graph index for project {project!r} — if "
|
|
658
|
+
f"you just added or renamed it, refresh with: codeintel index {root_str}",
|
|
659
|
+
)
|
|
660
|
+
|
|
599
661
|
return {
|
|
600
662
|
"ok": True,
|
|
601
663
|
"op": op_str,
|
|
@@ -609,7 +671,7 @@ class GraphProvider:
|
|
|
609
671
|
return safe_null_result(op, target, engine="graph", reason="error")
|
|
610
672
|
|
|
611
673
|
def _dispatch(
|
|
612
|
-
self, op: str, target: str, project: str, timeout_ms: int
|
|
674
|
+
self, op: str, target: str, project: str, timeout_ms: int, root: str = ""
|
|
613
675
|
) -> str | None:
|
|
614
676
|
if op == "impact" or op == "context":
|
|
615
677
|
# `context` (fan-out op) → the graph's richest single-symbol view: callers + callees.
|
|
@@ -623,7 +685,7 @@ class GraphProvider:
|
|
|
623
685
|
if op == "pattern":
|
|
624
686
|
return self._op_pattern(target, project, timeout_ms)
|
|
625
687
|
if op == "overview":
|
|
626
|
-
return self._op_overview(target, project, timeout_ms)
|
|
688
|
+
return self._op_overview(target, project, timeout_ms, root)
|
|
627
689
|
if op == "changed" or op == "changes":
|
|
628
690
|
return self._op_changed(project, timeout_ms)
|
|
629
691
|
if op == "deadcode":
|
|
@@ -126,11 +126,25 @@ class Reindexer:
|
|
|
126
126
|
def _graph_reindex(self, project_root: str) -> None:
|
|
127
127
|
# Route through the graph provider's single subprocess/JSON seam (piped stdin, with a
|
|
128
128
|
# deprecated raw-JSON fallback) rather than duplicating the deprecated raw-JSON call here.
|
|
129
|
+
#
|
|
130
|
+
# This called `detect_changes` with a `project_root` argument, and was wrong twice over:
|
|
131
|
+
# the backend takes `project` (a name from list_projects), so every call returned an
|
|
132
|
+
# argument error that `_run` folded into None — and `detect_changes` only REPORTS
|
|
133
|
+
# uncommitted drift, it never reindexes. The graph therefore never refreshed, silently,
|
|
134
|
+
# and queries answered from whatever the index held when it was first built.
|
|
129
135
|
try:
|
|
130
136
|
from codeintel.providers.graph import GraphProvider
|
|
131
137
|
gp = GraphProvider()
|
|
132
138
|
if not gp.available:
|
|
133
139
|
return
|
|
134
|
-
gp._run("
|
|
140
|
+
result = gp._run("index_repository", {"project_root": project_root}, 300_000)
|
|
141
|
+
# Surface a backend-reported failure. Swallowing it is what let a broken reindex look
|
|
142
|
+
# exactly like a working one for as long as nobody compared the graph to the source.
|
|
143
|
+
if isinstance(result, dict) and result.get("status") == "error":
|
|
144
|
+
logger.warning("graph index_repository reported an error for %s: %s",
|
|
145
|
+
project_root, result.get("hint") or result)
|
|
146
|
+
elif result is None:
|
|
147
|
+
logger.warning("graph index_repository returned nothing for %s "
|
|
148
|
+
"(backend timed out, crashed, or rejected the call)", project_root)
|
|
135
149
|
except Exception as exc:
|
|
136
|
-
logger.warning("graph
|
|
150
|
+
logger.warning("graph index_repository failed: %s", exc)
|