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.
Files changed (96) hide show
  1. {codecortex-0.13.0/src/codecortex.egg-info → codecortex-0.13.1}/PKG-INFO +26 -4
  2. {codecortex-0.13.0 → codecortex-0.13.1}/README.md +25 -3
  3. {codecortex-0.13.0 → codecortex-0.13.1/src/codecortex.egg-info}/PKG-INFO +26 -4
  4. codecortex-0.13.1/src/codeintel/__init__.py +1 -0
  5. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/graph.py +73 -11
  6. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/reindexer.py +16 -2
  7. codecortex-0.13.1/tests/test_graph_provider.py +310 -0
  8. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_graph_stdin.py +4 -1
  9. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_reindexer.py +72 -0
  10. codecortex-0.13.0/src/codeintel/__init__.py +0 -1
  11. codecortex-0.13.0/tests/test_graph_provider.py +0 -170
  12. {codecortex-0.13.0 → codecortex-0.13.1}/LICENSE +0 -0
  13. {codecortex-0.13.0 → codecortex-0.13.1}/pyproject.toml +0 -0
  14. {codecortex-0.13.0 → codecortex-0.13.1}/setup.cfg +0 -0
  15. {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/SOURCES.txt +0 -0
  16. {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/dependency_links.txt +0 -0
  17. {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/entry_points.txt +0 -0
  18. {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/requires.txt +0 -0
  19. {codecortex-0.13.0 → codecortex-0.13.1}/src/codecortex.egg-info/top_level.txt +0 -0
  20. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/__main__.py +0 -0
  21. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/auth.py +0 -0
  22. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/cache.py +0 -0
  23. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/__init__.py +0 -0
  24. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/_common.py +0 -0
  25. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/doctor.py +0 -0
  26. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/gen_token.py +0 -0
  27. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/graph.py +0 -0
  28. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/index.py +0 -0
  29. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/install.py +0 -0
  30. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/map.py +0 -0
  31. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/query.py +0 -0
  32. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/reset.py +0 -0
  33. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/serve.py +0 -0
  34. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/serve_http.py +0 -0
  35. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/setup.py +0 -0
  36. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/commands/status.py +0 -0
  37. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/config.py +0 -0
  38. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/doctor.py +0 -0
  39. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/gateway.py +0 -0
  40. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/grapher.py +0 -0
  41. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/http_server.py +0 -0
  42. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/indexer.py +0 -0
  43. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/injector.py +0 -0
  44. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/installer.py +0 -0
  45. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/logconfig.py +0 -0
  46. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/mapper.py +0 -0
  47. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/metrics.py +0 -0
  48. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/onboarding.py +0 -0
  49. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/policy.py +0 -0
  50. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/provider.py +0 -0
  51. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/__init__.py +0 -0
  52. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/lsp.py +0 -0
  53. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/none.py +0 -0
  54. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/providers/semantic.py +0 -0
  55. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/py.typed +0 -0
  56. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/reset.py +0 -0
  57. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/searcher.py +0 -0
  58. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/semantic_db.py +0 -0
  59. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/server.py +0 -0
  60. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/term.py +0 -0
  61. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/verify.py +0 -0
  62. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/viewer/__init__.py +0 -0
  63. {codecortex-0.13.0 → codecortex-0.13.1}/src/codeintel/viewer/graph_template.html +0 -0
  64. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cache.py +0 -0
  65. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_chunking.py +0 -0
  66. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cli_commands.py +0 -0
  67. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_cli_help.py +0 -0
  68. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_config.py +0 -0
  69. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_doctor.py +0 -0
  70. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_e2e.py +0 -0
  71. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_engine_adoption.py +0 -0
  72. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_enterprise.py +0 -0
  73. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_gateway.py +0 -0
  74. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_graph_real.py +0 -0
  75. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_grapher.py +0 -0
  76. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_hardening.py +0 -0
  77. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_http_auth.py +0 -0
  78. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_http_server.py +0 -0
  79. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_installer.py +0 -0
  80. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_integration.py +0 -0
  81. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_lsp_provider.py +0 -0
  82. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_lsp_real.py +0 -0
  83. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mapper.py +0 -0
  84. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mcp_handshake.py +0 -0
  85. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_mcp_server.py +0 -0
  86. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_model_dimension.py +0 -0
  87. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_never_raise.py +0 -0
  88. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_onboarding.py +0 -0
  89. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_rbac.py +0 -0
  90. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_rerank.py +0 -0
  91. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_reset.py +0 -0
  92. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_semantic_provider.py +0 -0
  93. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_term.py +0 -0
  94. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_treesitter.py +0 -0
  95. {codecortex-0.13.0 → codecortex-0.13.1}/tests/test_verify.py +0 -0
  96. {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.0
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 stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
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 stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
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.0
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 stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
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``). Pure + static so ``_resolve_project`` and ``probe`` share it."""
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: str | None = None
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 = entry.get("name")
143
- break
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
- return exact if exact is not None else best_prefix_name
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 = str(raw.get("project") or project)
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
- result_text = self._dispatch(op_str, target_str, project, timeout_ms)
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("detect_changes", {"project_root": project_root}, 120_000)
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 detect_changes failed: %s", exc)
150
+ logger.warning("graph index_repository failed: %s", exc)