codecortex 0.11.0__tar.gz → 0.12.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.
Files changed (80) hide show
  1. {codecortex-0.11.0/src/codecortex.egg-info → codecortex-0.12.0}/PKG-INFO +114 -6
  2. {codecortex-0.11.0 → codecortex-0.12.0}/README.md +113 -5
  3. {codecortex-0.11.0 → codecortex-0.12.0/src/codecortex.egg-info}/PKG-INFO +114 -6
  4. {codecortex-0.11.0 → codecortex-0.12.0}/src/codecortex.egg-info/SOURCES.txt +6 -1
  5. codecortex-0.12.0/src/codeintel/__init__.py +1 -0
  6. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/__main__.py +68 -13
  7. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/cache.py +7 -0
  8. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/doctor.py +172 -5
  9. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/gateway.py +40 -0
  10. codecortex-0.12.0/src/codeintel/installer.py +369 -0
  11. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/providers/lsp.py +35 -11
  12. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/server.py +114 -43
  13. codecortex-0.12.0/src/codeintel/verify.py +316 -0
  14. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/viewer/graph_template.html +75 -10
  15. codecortex-0.12.0/tests/test_engine_adoption.py +206 -0
  16. codecortex-0.12.0/tests/test_installer.py +516 -0
  17. codecortex-0.12.0/tests/test_mcp_handshake.py +215 -0
  18. codecortex-0.12.0/tests/test_verify.py +172 -0
  19. codecortex-0.12.0/tests/test_verify_call.py +141 -0
  20. codecortex-0.11.0/src/codeintel/__init__.py +0 -1
  21. codecortex-0.11.0/src/codeintel/installer.py +0 -120
  22. codecortex-0.11.0/tests/test_installer.py +0 -97
  23. {codecortex-0.11.0 → codecortex-0.12.0}/LICENSE +0 -0
  24. {codecortex-0.11.0 → codecortex-0.12.0}/pyproject.toml +0 -0
  25. {codecortex-0.11.0 → codecortex-0.12.0}/setup.cfg +0 -0
  26. {codecortex-0.11.0 → codecortex-0.12.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
  27. {codecortex-0.11.0 → codecortex-0.12.0}/src/codecortex.egg-info/entry_points.txt +0 -0
  28. {codecortex-0.11.0 → codecortex-0.12.0}/src/codecortex.egg-info/requires.txt +0 -0
  29. {codecortex-0.11.0 → codecortex-0.12.0}/src/codecortex.egg-info/top_level.txt +0 -0
  30. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/auth.py +0 -0
  31. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/config.py +0 -0
  32. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/grapher.py +0 -0
  33. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/http_server.py +0 -0
  34. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/indexer.py +0 -0
  35. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/injector.py +0 -0
  36. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/logconfig.py +0 -0
  37. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/mapper.py +0 -0
  38. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/metrics.py +0 -0
  39. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/onboarding.py +0 -0
  40. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/policy.py +0 -0
  41. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/provider.py +0 -0
  42. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/providers/__init__.py +0 -0
  43. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/providers/graph.py +0 -0
  44. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/providers/none.py +0 -0
  45. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/providers/semantic.py +0 -0
  46. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/reindexer.py +0 -0
  47. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/reset.py +0 -0
  48. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/searcher.py +0 -0
  49. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/semantic_db.py +0 -0
  50. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/term.py +0 -0
  51. {codecortex-0.11.0 → codecortex-0.12.0}/src/codeintel/viewer/__init__.py +0 -0
  52. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_cache.py +0 -0
  53. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_chunking.py +0 -0
  54. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_config.py +0 -0
  55. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_doctor.py +0 -0
  56. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_e2e.py +0 -0
  57. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_enterprise.py +0 -0
  58. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_gateway.py +0 -0
  59. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_graph_provider.py +0 -0
  60. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_graph_real.py +0 -0
  61. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_graph_stdin.py +0 -0
  62. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_grapher.py +0 -0
  63. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_hardening.py +0 -0
  64. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_http_auth.py +0 -0
  65. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_http_server.py +0 -0
  66. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_integration.py +0 -0
  67. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_lsp_provider.py +0 -0
  68. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_lsp_real.py +0 -0
  69. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_mapper.py +0 -0
  70. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_mcp_server.py +0 -0
  71. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_model_dimension.py +0 -0
  72. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_never_raise.py +0 -0
  73. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_onboarding.py +0 -0
  74. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_rbac.py +0 -0
  75. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_reindexer.py +0 -0
  76. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_rerank.py +0 -0
  77. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_reset.py +0 -0
  78. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_semantic_provider.py +0 -0
  79. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_term.py +0 -0
  80. {codecortex-0.11.0 → codecortex-0.12.0}/tests/test_treesitter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -38,6 +38,22 @@ Dynamic: license-file
38
38
 
39
39
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
40
40
 
41
+ ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
42
+
43
+ > *codeintel visualizing its own codebase.* One command — `codeintel graph <repo> --html` — turns any indexed repo into a **self-contained, interactive call graph** you can open offline or share as a file. Layouts, complexity-sized nodes, click-to-inspect metrics, and JSON/Markdown/SVG/PNG export. See **[docs/graph-viewer.md](docs/graph-viewer.md)**.
44
+
45
+ Prefer plain text? `codeintel map` writes a **readable architecture overview** to `CODE_INTEL.md` — node/edge counts, ranked symbols by caller count, and entry points — for skimming or for MCP hosts that can't render a graph:
46
+
47
+ <img src="docs/images/code-intel-map.png" width="520" alt="CODE_INTEL.md — the codeintel map: an architecture overview with node/edge counts and symbols ranked by caller count.">
48
+
49
+ **What `CODE_INTEL.md` is for.** It's a *static, committable* snapshot of a codebase's shape — meant to be read (by a person or an agent) **first**, instead of reconstructing structure by grepping. It covers the cases the live `code.query` tool doesn't:
50
+
51
+ - **Agents & hosts that don't speak MCP.** Not every agent supports MCP, and the server isn't always running. `codeintel map` writes a plain file any agent can read; `codeintel map --inject` also drops a pointer into `CLAUDE.md` / `AGENTS.md`, so an agent picks up the codebase's structure automatically at the start of a session.
52
+ - **A committed, diffable overview.** It lives *in the repo* — reviewable in a PR, browsable on GitHub, available offline. Re-run `codeintel map` after `codeintel index` to refresh it.
53
+ - **The load-bearing code at a glance.** Ranking symbols by caller count surfaces what most of the codebase depends on (the risky-to-change core) plus the entry points — the first things a newcomer, or an agent, should understand before touching anything.
54
+
55
+ See **[docs/map-file.md](docs/map-file.md)** for the format and the `--inject` flow.
56
+
41
57
  ## Why an agent needs it
42
58
 
43
59
  Without structural tools, an agent dropped into unfamiliar code falls back on `grep` and reads whole files to reconstruct relationships by hand — burning tokens, missing call sites, and guessing at blast radius before it edits anything. codeintel answers those questions directly instead:
@@ -94,7 +110,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
94
110
  - **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.
95
111
  - **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.
96
112
  - **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.
97
- - **Honest about its own health.** `codeintel doctor` reports exactly which engines are ready for a repo and the single command to fix each gap no guessing why a query came back empty.
113
+ - **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.
98
114
 
99
115
  ## Quickstart
100
116
 
@@ -128,10 +144,91 @@ pip install -e .
128
144
  Register with your AI agent(s), then query:
129
145
 
130
146
  ```bash
131
- codeintel install # registers with Claude, Codex, Gemini, Zed
147
+ codeintel install # registers with the agents you actually have installed
132
148
  codeintel query --op search --target "authentication middleware"
133
149
  ```
134
150
 
151
+ ### Enable native Codex integration
152
+
153
+ `codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
154
+ After installing the package, explicitly register it with Codex:
155
+
156
+ ```bash
157
+ codeintel install --agent codex
158
+ ```
159
+
160
+ This safely adds a `[mcp_servers.codeintel]` entry to `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`
161
+ when that is set) without changing your other Codex settings. Registration is deliberately opt-in:
162
+ installing a Python package should not silently modify an agent's configuration. Start a new Codex
163
+ task (or restart Codex) after registration; the refreshed task will have native `code.query`,
164
+ `code.status`, `code.doctor`, and `code.map` MCP tools available.
165
+
166
+ For a fully prepared local setup, run:
167
+
168
+ ```bash
169
+ codeintel setup --all /path/to/your/project && codeintel install --agent codex
170
+ ```
171
+
172
+ ### Enable native Claude Code integration
173
+
174
+ After installing the package, explicitly register it with Claude Code:
175
+
176
+ ```bash
177
+ codeintel install --agent claude
178
+ ```
179
+
180
+ This adds the `codeintel` MCP server to `~/.claude.json` (or `$CLAUDE_CONFIG_DIR/.claude.json`)
181
+ while preserving your existing configuration — that is the file Claude Code reads for user-scope
182
+ MCP servers, and you can confirm the entry with `claude mcp list`. Start a new Claude Code session
183
+ after registration so it can load the native `code.query`, `code.status`, `code.doctor`, and
184
+ `code.map` MCP tools.
185
+
186
+ > **Upgrading from ≤ 0.11.1?** Earlier versions wrote this block to `~/.claude/settings.json`,
187
+ > which Claude Code ignores for MCP registration — so codeintel never actually loaded. Re-run
188
+ > `codeintel install --agent claude`; it registers in the right place and points out the stale
189
+ > entry so you can delete it.
190
+
191
+ For a fully prepared local setup, run:
192
+
193
+ ```bash
194
+ codeintel setup --all /path/to/your/project && codeintel install --agent claude
195
+ ```
196
+
197
+ ### Registration is verified, not assumed
198
+
199
+ **It only touches agents you have.** `codeintel install` defaults to `--agent auto`: it registers
200
+ the hosts whose config root already exists on this machine and *names the ones it skipped*.
201
+ Installing a Python package should not create `~/.gemini/` and `~/.config/zed/` for someone who has
202
+ neither. Force a specific host with `--agent claude|codex|gemini|zed`, or every supported host with
203
+ `--agent all`.
204
+
205
+ **It registers an absolute path.** The bare name `codeintel` is resolved by the *host*, not by the
206
+ shell you ran `install` in — and a GUI-launched desktop agent does not source your shell profile, so
207
+ a command your terminal finds is routinely invisible to the app. That is the one failure a handshake
208
+ run in your terminal cannot catch, because it inherits the PATH that works. Pass
209
+ `--relative-command` for the bare name. If a later upgrade moves the binary, re-running
210
+ `codeintel install` repairs the stale path in place, leaving the rest of your config untouched.
211
+
212
+ Then it launches the exact command it registered and drives a real MCP handshake —
213
+ `initialize` → `tools/list` — and reports what came back:
214
+
215
+ ```text
216
+ v claude: registered at /Users/you/.claude.json
217
+
218
+ v verified: codeintel 0.11.2 — 4 tools (code.query, code.status, code.doctor, code.map)
219
+ ```
220
+
221
+ If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
222
+ instead of reporting a success your agent cannot use. Pass `--no-verify` to skip the handshake.
223
+
224
+ The same principle gates releases. Because every result is a safe envelope with `ok: true` and the
225
+ CLI never throws, an exit-code smoke test passes against a build that boots cleanly and answers
226
+ nothing — so **[`scripts/release_canary.py`](scripts/release_canary.py)** runs before every publish
227
+ against the built wheel in a clean environment: it registers Codex and Claude Code into a throwaway
228
+ `HOME`, launches the command those config files name, and asserts on the **answer text** of a real
229
+ `code.query` over a fixture repo. A release that writes a config no host reads, or that returns
230
+ `ok: true` with nothing in it, fails there instead of on your machine.
231
+
135
232
  ## How it works
136
233
 
137
234
  A `Gateway` receives every query and dispatches it to one of three providers — graph (structural relationships), LSP (precise symbol resolution), or semantic (embedding-based search) — based on the operation type. Each provider is fully isolated: if it is unavailable or raises an exception, the gateway catches it and returns a safe-null envelope. The caller always gets a well-formed response with no exception to catch.
@@ -187,7 +284,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
187
284
 
188
285
  | Command | Purpose |
189
286
  |---|---|
190
- | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
287
+ | `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 |
191
288
  | `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 |
192
289
  | `codeintel index [project_root]` | Index a project for semantic search |
193
290
  | `codeintel serve` | Start the MCP server (stdio transport) |
@@ -255,7 +352,7 @@ Register codeintel as an MCP server (`codeintel install`) and the agent gets fou
255
352
  | MCP tool | HTTP equivalent | Purpose |
256
353
  |---|---|---|
257
354
  | `code.query` | `POST /code/query` | The main call — search, trace, understand (the `op` table above) |
258
- | `code.status` | `GET /code/status` | Which engines are live + whether an index exists |
355
+ | `code.status` | `GET /code/status` | Per-engine `installed` / `runnable` / `repo_indexed`, probed against the live engines a query actually hits |
259
356
  | `code.doctor` | `POST /code/doctor` | Per-engine health + repo index status, with a fix for each gap |
260
357
  | `code.map` | — | Generate/refresh `CODE_INTEL.md`, a static orientation file for hosts without MCP |
261
358
 
@@ -312,5 +409,16 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
312
409
  git clone https://github.com/hamilton-sky/codeintel.git
313
410
  cd codeintel
314
411
  pip install -e .[dev]
315
- pytest tests/ -q # full suite (~15s — includes live graph/LSP backend tests)
412
+ pytest tests/ -q # ~390 tests, ~35s (live graph/LSP backend tests skip when absent)
316
413
  ```
414
+
415
+ **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
416
+ missing entry point, a host config written where nobody reads it, or a server that boots and answers
417
+ nothing. Run the canary against the built wheel in a clean environment — the same check CI runs
418
+ before publishing:
419
+
420
+ ```bash
421
+ python -m build && python -m venv /tmp/canary && /tmp/canary/bin/python -m pip install dist/*.whl && /tmp/canary/bin/python scripts/release_canary.py
422
+ ```
423
+
424
+ It exits non-zero on the first failed check and cleans up the temporary `HOME` it installs into.
@@ -4,6 +4,22 @@
4
4
 
5
5
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
6
6
 
7
+ ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
8
+
9
+ > *codeintel visualizing its own codebase.* One command — `codeintel graph <repo> --html` — turns any indexed repo into a **self-contained, interactive call graph** you can open offline or share as a file. Layouts, complexity-sized nodes, click-to-inspect metrics, and JSON/Markdown/SVG/PNG export. See **[docs/graph-viewer.md](docs/graph-viewer.md)**.
10
+
11
+ Prefer plain text? `codeintel map` writes a **readable architecture overview** to `CODE_INTEL.md` — node/edge counts, ranked symbols by caller count, and entry points — for skimming or for MCP hosts that can't render a graph:
12
+
13
+ <img src="docs/images/code-intel-map.png" width="520" alt="CODE_INTEL.md — the codeintel map: an architecture overview with node/edge counts and symbols ranked by caller count.">
14
+
15
+ **What `CODE_INTEL.md` is for.** It's a *static, committable* snapshot of a codebase's shape — meant to be read (by a person or an agent) **first**, instead of reconstructing structure by grepping. It covers the cases the live `code.query` tool doesn't:
16
+
17
+ - **Agents & hosts that don't speak MCP.** Not every agent supports MCP, and the server isn't always running. `codeintel map` writes a plain file any agent can read; `codeintel map --inject` also drops a pointer into `CLAUDE.md` / `AGENTS.md`, so an agent picks up the codebase's structure automatically at the start of a session.
18
+ - **A committed, diffable overview.** It lives *in the repo* — reviewable in a PR, browsable on GitHub, available offline. Re-run `codeintel map` after `codeintel index` to refresh it.
19
+ - **The load-bearing code at a glance.** Ranking symbols by caller count surfaces what most of the codebase depends on (the risky-to-change core) plus the entry points — the first things a newcomer, or an agent, should understand before touching anything.
20
+
21
+ See **[docs/map-file.md](docs/map-file.md)** for the format and the `--inject` flow.
22
+
7
23
  ## Why an agent needs it
8
24
 
9
25
  Without structural tools, an agent dropped into unfamiliar code falls back on `grep` and reads whole files to reconstruct relationships by hand — burning tokens, missing call sites, and guessing at blast radius before it edits anything. codeintel answers those questions directly instead:
@@ -60,7 +76,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
60
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.
61
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.
62
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.
63
- - **Honest about its own health.** `codeintel doctor` reports exactly which engines are ready for a repo and the single command to fix each gap no guessing why a query came back empty.
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.
64
80
 
65
81
  ## Quickstart
66
82
 
@@ -94,10 +110,91 @@ pip install -e .
94
110
  Register with your AI agent(s), then query:
95
111
 
96
112
  ```bash
97
- codeintel install # registers with Claude, Codex, Gemini, Zed
113
+ codeintel install # registers with the agents you actually have installed
98
114
  codeintel query --op search --target "authentication middleware"
99
115
  ```
100
116
 
117
+ ### Enable native Codex integration
118
+
119
+ `codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
120
+ After installing the package, explicitly register it with Codex:
121
+
122
+ ```bash
123
+ codeintel install --agent codex
124
+ ```
125
+
126
+ This safely adds a `[mcp_servers.codeintel]` entry to `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`
127
+ when that is set) without changing your other Codex settings. Registration is deliberately opt-in:
128
+ installing a Python package should not silently modify an agent's configuration. Start a new Codex
129
+ task (or restart Codex) after registration; the refreshed task will have native `code.query`,
130
+ `code.status`, `code.doctor`, and `code.map` MCP tools available.
131
+
132
+ For a fully prepared local setup, run:
133
+
134
+ ```bash
135
+ codeintel setup --all /path/to/your/project && codeintel install --agent codex
136
+ ```
137
+
138
+ ### Enable native Claude Code integration
139
+
140
+ After installing the package, explicitly register it with Claude Code:
141
+
142
+ ```bash
143
+ codeintel install --agent claude
144
+ ```
145
+
146
+ This adds the `codeintel` MCP server to `~/.claude.json` (or `$CLAUDE_CONFIG_DIR/.claude.json`)
147
+ while preserving your existing configuration — that is the file Claude Code reads for user-scope
148
+ MCP servers, and you can confirm the entry with `claude mcp list`. Start a new Claude Code session
149
+ after registration so it can load the native `code.query`, `code.status`, `code.doctor`, and
150
+ `code.map` MCP tools.
151
+
152
+ > **Upgrading from ≤ 0.11.1?** Earlier versions wrote this block to `~/.claude/settings.json`,
153
+ > which Claude Code ignores for MCP registration — so codeintel never actually loaded. Re-run
154
+ > `codeintel install --agent claude`; it registers in the right place and points out the stale
155
+ > entry so you can delete it.
156
+
157
+ For a fully prepared local setup, run:
158
+
159
+ ```bash
160
+ codeintel setup --all /path/to/your/project && codeintel install --agent claude
161
+ ```
162
+
163
+ ### Registration is verified, not assumed
164
+
165
+ **It only touches agents you have.** `codeintel install` defaults to `--agent auto`: it registers
166
+ the hosts whose config root already exists on this machine and *names the ones it skipped*.
167
+ Installing a Python package should not create `~/.gemini/` and `~/.config/zed/` for someone who has
168
+ neither. Force a specific host with `--agent claude|codex|gemini|zed`, or every supported host with
169
+ `--agent all`.
170
+
171
+ **It registers an absolute path.** The bare name `codeintel` is resolved by the *host*, not by the
172
+ shell you ran `install` in — and a GUI-launched desktop agent does not source your shell profile, so
173
+ a command your terminal finds is routinely invisible to the app. That is the one failure a handshake
174
+ run in your terminal cannot catch, because it inherits the PATH that works. Pass
175
+ `--relative-command` for the bare name. If a later upgrade moves the binary, re-running
176
+ `codeintel install` repairs the stale path in place, leaving the rest of your config untouched.
177
+
178
+ Then it launches the exact command it registered and drives a real MCP handshake —
179
+ `initialize` → `tools/list` — and reports what came back:
180
+
181
+ ```text
182
+ v claude: registered at /Users/you/.claude.json
183
+
184
+ v verified: codeintel 0.11.2 — 4 tools (code.query, code.status, code.doctor, code.map)
185
+ ```
186
+
187
+ If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
188
+ instead of reporting a success your agent cannot use. Pass `--no-verify` to skip the handshake.
189
+
190
+ The same principle gates releases. Because every result is a safe envelope with `ok: true` and the
191
+ CLI never throws, an exit-code smoke test passes against a build that boots cleanly and answers
192
+ nothing — so **[`scripts/release_canary.py`](scripts/release_canary.py)** runs before every publish
193
+ against the built wheel in a clean environment: it registers Codex and Claude Code into a throwaway
194
+ `HOME`, launches the command those config files name, and asserts on the **answer text** of a real
195
+ `code.query` over a fixture repo. A release that writes a config no host reads, or that returns
196
+ `ok: true` with nothing in it, fails there instead of on your machine.
197
+
101
198
  ## How it works
102
199
 
103
200
  A `Gateway` receives every query and dispatches it to one of three providers — graph (structural relationships), LSP (precise symbol resolution), or semantic (embedding-based search) — based on the operation type. Each provider is fully isolated: if it is unavailable or raises an exception, the gateway catches it and returns a safe-null envelope. The caller always gets a well-formed response with no exception to catch.
@@ -153,7 +250,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
153
250
 
154
251
  | Command | Purpose |
155
252
  |---|---|
156
- | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
253
+ | `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 |
157
254
  | `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 |
158
255
  | `codeintel index [project_root]` | Index a project for semantic search |
159
256
  | `codeintel serve` | Start the MCP server (stdio transport) |
@@ -221,7 +318,7 @@ Register codeintel as an MCP server (`codeintel install`) and the agent gets fou
221
318
  | MCP tool | HTTP equivalent | Purpose |
222
319
  |---|---|---|
223
320
  | `code.query` | `POST /code/query` | The main call — search, trace, understand (the `op` table above) |
224
- | `code.status` | `GET /code/status` | Which engines are live + whether an index exists |
321
+ | `code.status` | `GET /code/status` | Per-engine `installed` / `runnable` / `repo_indexed`, probed against the live engines a query actually hits |
225
322
  | `code.doctor` | `POST /code/doctor` | Per-engine health + repo index status, with a fix for each gap |
226
323
  | `code.map` | — | Generate/refresh `CODE_INTEL.md`, a static orientation file for hosts without MCP |
227
324
 
@@ -278,5 +375,16 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
278
375
  git clone https://github.com/hamilton-sky/codeintel.git
279
376
  cd codeintel
280
377
  pip install -e .[dev]
281
- pytest tests/ -q # full suite (~15s — includes live graph/LSP backend tests)
378
+ pytest tests/ -q # ~390 tests, ~35s (live graph/LSP backend tests skip when absent)
282
379
  ```
380
+
381
+ **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
382
+ missing entry point, a host config written where nobody reads it, or a server that boots and answers
383
+ nothing. Run the canary against the built wheel in a clean environment — the same check CI runs
384
+ before publishing:
385
+
386
+ ```bash
387
+ python -m build && python -m venv /tmp/canary && /tmp/canary/bin/python -m pip install dist/*.whl && /tmp/canary/bin/python scripts/release_canary.py
388
+ ```
389
+
390
+ It exits non-zero on the first failed check and cleans up the temporary `HOME` it installs into.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -38,6 +38,22 @@ Dynamic: license-file
38
38
 
39
39
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
40
40
 
41
+ ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
42
+
43
+ > *codeintel visualizing its own codebase.* One command — `codeintel graph <repo> --html` — turns any indexed repo into a **self-contained, interactive call graph** you can open offline or share as a file. Layouts, complexity-sized nodes, click-to-inspect metrics, and JSON/Markdown/SVG/PNG export. See **[docs/graph-viewer.md](docs/graph-viewer.md)**.
44
+
45
+ Prefer plain text? `codeintel map` writes a **readable architecture overview** to `CODE_INTEL.md` — node/edge counts, ranked symbols by caller count, and entry points — for skimming or for MCP hosts that can't render a graph:
46
+
47
+ <img src="docs/images/code-intel-map.png" width="520" alt="CODE_INTEL.md — the codeintel map: an architecture overview with node/edge counts and symbols ranked by caller count.">
48
+
49
+ **What `CODE_INTEL.md` is for.** It's a *static, committable* snapshot of a codebase's shape — meant to be read (by a person or an agent) **first**, instead of reconstructing structure by grepping. It covers the cases the live `code.query` tool doesn't:
50
+
51
+ - **Agents & hosts that don't speak MCP.** Not every agent supports MCP, and the server isn't always running. `codeintel map` writes a plain file any agent can read; `codeintel map --inject` also drops a pointer into `CLAUDE.md` / `AGENTS.md`, so an agent picks up the codebase's structure automatically at the start of a session.
52
+ - **A committed, diffable overview.** It lives *in the repo* — reviewable in a PR, browsable on GitHub, available offline. Re-run `codeintel map` after `codeintel index` to refresh it.
53
+ - **The load-bearing code at a glance.** Ranking symbols by caller count surfaces what most of the codebase depends on (the risky-to-change core) plus the entry points — the first things a newcomer, or an agent, should understand before touching anything.
54
+
55
+ See **[docs/map-file.md](docs/map-file.md)** for the format and the `--inject` flow.
56
+
41
57
  ## Why an agent needs it
42
58
 
43
59
  Without structural tools, an agent dropped into unfamiliar code falls back on `grep` and reads whole files to reconstruct relationships by hand — burning tokens, missing call sites, and guessing at blast radius before it edits anything. codeintel answers those questions directly instead:
@@ -94,7 +110,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
94
110
  - **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.
95
111
  - **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.
96
112
  - **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.
97
- - **Honest about its own health.** `codeintel doctor` reports exactly which engines are ready for a repo and the single command to fix each gap no guessing why a query came back empty.
113
+ - **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.
98
114
 
99
115
  ## Quickstart
100
116
 
@@ -128,10 +144,91 @@ pip install -e .
128
144
  Register with your AI agent(s), then query:
129
145
 
130
146
  ```bash
131
- codeintel install # registers with Claude, Codex, Gemini, Zed
147
+ codeintel install # registers with the agents you actually have installed
132
148
  codeintel query --op search --target "authentication middleware"
133
149
  ```
134
150
 
151
+ ### Enable native Codex integration
152
+
153
+ `codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
154
+ After installing the package, explicitly register it with Codex:
155
+
156
+ ```bash
157
+ codeintel install --agent codex
158
+ ```
159
+
160
+ This safely adds a `[mcp_servers.codeintel]` entry to `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`
161
+ when that is set) without changing your other Codex settings. Registration is deliberately opt-in:
162
+ installing a Python package should not silently modify an agent's configuration. Start a new Codex
163
+ task (or restart Codex) after registration; the refreshed task will have native `code.query`,
164
+ `code.status`, `code.doctor`, and `code.map` MCP tools available.
165
+
166
+ For a fully prepared local setup, run:
167
+
168
+ ```bash
169
+ codeintel setup --all /path/to/your/project && codeintel install --agent codex
170
+ ```
171
+
172
+ ### Enable native Claude Code integration
173
+
174
+ After installing the package, explicitly register it with Claude Code:
175
+
176
+ ```bash
177
+ codeintel install --agent claude
178
+ ```
179
+
180
+ This adds the `codeintel` MCP server to `~/.claude.json` (or `$CLAUDE_CONFIG_DIR/.claude.json`)
181
+ while preserving your existing configuration — that is the file Claude Code reads for user-scope
182
+ MCP servers, and you can confirm the entry with `claude mcp list`. Start a new Claude Code session
183
+ after registration so it can load the native `code.query`, `code.status`, `code.doctor`, and
184
+ `code.map` MCP tools.
185
+
186
+ > **Upgrading from ≤ 0.11.1?** Earlier versions wrote this block to `~/.claude/settings.json`,
187
+ > which Claude Code ignores for MCP registration — so codeintel never actually loaded. Re-run
188
+ > `codeintel install --agent claude`; it registers in the right place and points out the stale
189
+ > entry so you can delete it.
190
+
191
+ For a fully prepared local setup, run:
192
+
193
+ ```bash
194
+ codeintel setup --all /path/to/your/project && codeintel install --agent claude
195
+ ```
196
+
197
+ ### Registration is verified, not assumed
198
+
199
+ **It only touches agents you have.** `codeintel install` defaults to `--agent auto`: it registers
200
+ the hosts whose config root already exists on this machine and *names the ones it skipped*.
201
+ Installing a Python package should not create `~/.gemini/` and `~/.config/zed/` for someone who has
202
+ neither. Force a specific host with `--agent claude|codex|gemini|zed`, or every supported host with
203
+ `--agent all`.
204
+
205
+ **It registers an absolute path.** The bare name `codeintel` is resolved by the *host*, not by the
206
+ shell you ran `install` in — and a GUI-launched desktop agent does not source your shell profile, so
207
+ a command your terminal finds is routinely invisible to the app. That is the one failure a handshake
208
+ run in your terminal cannot catch, because it inherits the PATH that works. Pass
209
+ `--relative-command` for the bare name. If a later upgrade moves the binary, re-running
210
+ `codeintel install` repairs the stale path in place, leaving the rest of your config untouched.
211
+
212
+ Then it launches the exact command it registered and drives a real MCP handshake —
213
+ `initialize` → `tools/list` — and reports what came back:
214
+
215
+ ```text
216
+ v claude: registered at /Users/you/.claude.json
217
+
218
+ v verified: codeintel 0.11.2 — 4 tools (code.query, code.status, code.doctor, code.map)
219
+ ```
220
+
221
+ If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
222
+ instead of reporting a success your agent cannot use. Pass `--no-verify` to skip the handshake.
223
+
224
+ The same principle gates releases. Because every result is a safe envelope with `ok: true` and the
225
+ CLI never throws, an exit-code smoke test passes against a build that boots cleanly and answers
226
+ nothing — so **[`scripts/release_canary.py`](scripts/release_canary.py)** runs before every publish
227
+ against the built wheel in a clean environment: it registers Codex and Claude Code into a throwaway
228
+ `HOME`, launches the command those config files name, and asserts on the **answer text** of a real
229
+ `code.query` over a fixture repo. A release that writes a config no host reads, or that returns
230
+ `ok: true` with nothing in it, fails there instead of on your machine.
231
+
135
232
  ## How it works
136
233
 
137
234
  A `Gateway` receives every query and dispatches it to one of three providers — graph (structural relationships), LSP (precise symbol resolution), or semantic (embedding-based search) — based on the operation type. Each provider is fully isolated: if it is unavailable or raises an exception, the gateway catches it and returns a safe-null envelope. The caller always gets a well-formed response with no exception to catch.
@@ -187,7 +284,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
187
284
 
188
285
  | Command | Purpose |
189
286
  |---|---|
190
- | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
287
+ | `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 |
191
288
  | `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 |
192
289
  | `codeintel index [project_root]` | Index a project for semantic search |
193
290
  | `codeintel serve` | Start the MCP server (stdio transport) |
@@ -255,7 +352,7 @@ Register codeintel as an MCP server (`codeintel install`) and the agent gets fou
255
352
  | MCP tool | HTTP equivalent | Purpose |
256
353
  |---|---|---|
257
354
  | `code.query` | `POST /code/query` | The main call — search, trace, understand (the `op` table above) |
258
- | `code.status` | `GET /code/status` | Which engines are live + whether an index exists |
355
+ | `code.status` | `GET /code/status` | Per-engine `installed` / `runnable` / `repo_indexed`, probed against the live engines a query actually hits |
259
356
  | `code.doctor` | `POST /code/doctor` | Per-engine health + repo index status, with a fix for each gap |
260
357
  | `code.map` | — | Generate/refresh `CODE_INTEL.md`, a static orientation file for hosts without MCP |
261
358
 
@@ -312,5 +409,16 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
312
409
  git clone https://github.com/hamilton-sky/codeintel.git
313
410
  cd codeintel
314
411
  pip install -e .[dev]
315
- pytest tests/ -q # full suite (~15s — includes live graph/LSP backend tests)
412
+ pytest tests/ -q # ~390 tests, ~35s (live graph/LSP backend tests skip when absent)
316
413
  ```
414
+
415
+ **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
416
+ missing entry point, a host config written where nobody reads it, or a server that boots and answers
417
+ nothing. Run the canary against the built wheel in a clean environment — the same check CI runs
418
+ before publishing:
419
+
420
+ ```bash
421
+ python -m build && python -m venv /tmp/canary && /tmp/canary/bin/python -m pip install dist/*.whl && /tmp/canary/bin/python scripts/release_canary.py
422
+ ```
423
+
424
+ It exits non-zero on the first failed check and cleans up the temporary `HOME` it installs into.
@@ -31,6 +31,7 @@ src/codeintel/searcher.py
31
31
  src/codeintel/semantic_db.py
32
32
  src/codeintel/server.py
33
33
  src/codeintel/term.py
34
+ src/codeintel/verify.py
34
35
  src/codeintel/providers/__init__.py
35
36
  src/codeintel/providers/graph.py
36
37
  src/codeintel/providers/lsp.py
@@ -43,6 +44,7 @@ tests/test_chunking.py
43
44
  tests/test_config.py
44
45
  tests/test_doctor.py
45
46
  tests/test_e2e.py
47
+ tests/test_engine_adoption.py
46
48
  tests/test_enterprise.py
47
49
  tests/test_gateway.py
48
50
  tests/test_graph_provider.py
@@ -57,6 +59,7 @@ tests/test_integration.py
57
59
  tests/test_lsp_provider.py
58
60
  tests/test_lsp_real.py
59
61
  tests/test_mapper.py
62
+ tests/test_mcp_handshake.py
60
63
  tests/test_mcp_server.py
61
64
  tests/test_model_dimension.py
62
65
  tests/test_never_raise.py
@@ -67,4 +70,6 @@ tests/test_rerank.py
67
70
  tests/test_reset.py
68
71
  tests/test_semantic_provider.py
69
72
  tests/test_term.py
70
- tests/test_treesitter.py
73
+ tests/test_treesitter.py
74
+ tests/test_verify.py
75
+ tests/test_verify_call.py
@@ -0,0 +1 @@
1
+ __version__ = "0.12.0"