codecortex 0.22.0__tar.gz → 0.23.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 (150) hide show
  1. {codecortex-0.22.0/src/codecortex.egg-info → codecortex-0.23.1}/PKG-INFO +170 -124
  2. {codecortex-0.22.0 → codecortex-0.23.1}/README.md +166 -123
  3. {codecortex-0.22.0 → codecortex-0.23.1}/pyproject.toml +20 -0
  4. {codecortex-0.22.0 → codecortex-0.23.1/src/codecortex.egg-info}/PKG-INFO +170 -124
  5. {codecortex-0.22.0 → codecortex-0.23.1}/src/codecortex.egg-info/SOURCES.txt +12 -0
  6. {codecortex-0.22.0 → codecortex-0.23.1}/src/codecortex.egg-info/requires.txt +3 -0
  7. codecortex-0.23.1/src/codeintel/__init__.py +1 -0
  8. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/__main__.py +60 -1
  9. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/c4.py +174 -31
  10. codecortex-0.23.1/src/codeintel/c4_check.py +438 -0
  11. codecortex-0.23.1/src/codeintel/c4_layers.py +657 -0
  12. codecortex-0.23.1/src/codeintel/commands/c4.py +349 -0
  13. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/index.py +26 -11
  14. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/setup.py +1 -0
  15. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/status.py +38 -14
  16. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/doctor.py +16 -3
  17. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/gateway.py +9 -1
  18. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/indexer.py +38 -5
  19. codecortex-0.23.1/src/codeintel/lang_config.py +291 -0
  20. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/mapper.py +115 -28
  21. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/onboarding.py +41 -3
  22. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/provider.py +24 -1
  23. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/providers/graph.py +132 -8
  24. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/providers/lsp.py +126 -8
  25. codecortex-0.23.1/src/codeintel/providers/semantic.py +793 -0
  26. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/reindexer.py +16 -2
  27. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/searcher.py +56 -8
  28. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/semantic_db.py +238 -0
  29. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/server.py +18 -6
  30. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/verify.py +53 -7
  31. codecortex-0.23.1/tests/test_background_index_failure.py +546 -0
  32. codecortex-0.23.1/tests/test_bench_oracle.py +211 -0
  33. codecortex-0.23.1/tests/test_bench_oracle_ts.py +218 -0
  34. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_c4.py +198 -0
  35. codecortex-0.23.1/tests/test_c4_check.py +458 -0
  36. codecortex-0.23.1/tests/test_c4_layers.py +331 -0
  37. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_c4_output.py +2 -1
  38. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cli_commands.py +56 -3
  39. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cli_help.py +11 -1
  40. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cold_process.py +10 -0
  41. codecortex-0.23.1/tests/test_could_not_ask_semantic.py +289 -0
  42. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_doctor.py +39 -0
  43. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_graph_failure_population.py +18 -0
  44. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_graph_provider.py +121 -0
  45. codecortex-0.23.1/tests/test_hard_exit.py +132 -0
  46. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_incompleteness.py +37 -10
  47. codecortex-0.23.1/tests/test_index_failure_reason.py +493 -0
  48. codecortex-0.23.1/tests/test_lang_config.py +260 -0
  49. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_lsp_provider.py +230 -1
  50. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_lsp_real.py +3 -2
  51. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_mapper.py +238 -4
  52. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_mcp_server.py +10 -0
  53. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_model_dimension.py +19 -1
  54. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_never_raise.py +83 -0
  55. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_onboarding.py +48 -0
  56. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_semantic_provider.py +137 -1
  57. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_staleness.py +16 -0
  58. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_verify.py +85 -0
  59. codecortex-0.22.0/src/codeintel/__init__.py +0 -1
  60. codecortex-0.22.0/src/codeintel/commands/c4.py +0 -182
  61. codecortex-0.22.0/src/codeintel/providers/semantic.py +0 -427
  62. {codecortex-0.22.0 → codecortex-0.23.1}/LICENSE +0 -0
  63. {codecortex-0.22.0 → codecortex-0.23.1}/setup.cfg +0 -0
  64. {codecortex-0.22.0 → codecortex-0.23.1}/src/codecortex.egg-info/dependency_links.txt +0 -0
  65. {codecortex-0.22.0 → codecortex-0.23.1}/src/codecortex.egg-info/entry_points.txt +0 -0
  66. {codecortex-0.22.0 → codecortex-0.23.1}/src/codecortex.egg-info/top_level.txt +0 -0
  67. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/agent_prompt.py +0 -0
  68. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/auth.py +0 -0
  69. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/cache.py +0 -0
  70. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/__init__.py +0 -0
  71. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/_common.py +0 -0
  72. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/doctor.py +0 -0
  73. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/gen_token.py +0 -0
  74. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/graph.py +0 -0
  75. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/install.py +0 -0
  76. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/map.py +0 -0
  77. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/prompt.py +0 -0
  78. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/query.py +0 -0
  79. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/reset.py +0 -0
  80. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/serve.py +0 -0
  81. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/commands/serve_http.py +0 -0
  82. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/config.py +0 -0
  83. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/containment.py +0 -0
  84. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/graph_backend.py +0 -0
  85. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/graph_render.py +0 -0
  86. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/graph_resolution.py +0 -0
  87. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/grapher.py +0 -0
  88. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/http_server.py +0 -0
  89. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/injector.py +0 -0
  90. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/installer.py +0 -0
  91. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/loc.py +0 -0
  92. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/logconfig.py +0 -0
  93. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/metrics.py +0 -0
  94. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/outcome.py +0 -0
  95. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/paths.py +0 -0
  96. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/policy.py +0 -0
  97. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/progress.py +0 -0
  98. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/providers/__init__.py +0 -0
  99. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/providers/none.py +0 -0
  100. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/py.typed +0 -0
  101. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/query_ops.py +0 -0
  102. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/redact.py +0 -0
  103. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/reset.py +0 -0
  104. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/source_kind.py +0 -0
  105. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/term.py +0 -0
  106. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/viewer/__init__.py +0 -0
  107. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/viewer/graph_template.html +0 -0
  108. {codecortex-0.22.0 → codecortex-0.23.1}/src/codeintel/wire_text.py +0 -0
  109. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_agent_prompt.py +0 -0
  110. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_backend_reaper.py +0 -0
  111. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_c4_model.py +0 -0
  112. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cache.py +0 -0
  113. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_chunking.py +0 -0
  114. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cli_install_dry_run.py +0 -0
  115. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_cli_install_offer_injection.py +0 -0
  116. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_config.py +0 -0
  117. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_corpus.py +0 -0
  118. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_docs_ci_claims.py +0 -0
  119. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_docs_deadcode_withdrawal.py +0 -0
  120. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_e2e.py +0 -0
  121. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_edge_confidence.py +0 -0
  122. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_engine_adoption.py +0 -0
  123. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_enterprise.py +0 -0
  124. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_gateway.py +0 -0
  125. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_graph_real.py +0 -0
  126. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_graph_stdin.py +0 -0
  127. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_grapher.py +0 -0
  128. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_hardening.py +0 -0
  129. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_http_auth.py +0 -0
  130. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_http_server.py +0 -0
  131. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_index_progress.py +0 -0
  132. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_indexer_binary.py +0 -0
  133. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_installer.py +0 -0
  134. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_integration.py +0 -0
  135. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_loc_census.py +0 -0
  136. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_lsp_language_coverage.py +0 -0
  137. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_mcp_handshake.py +0 -0
  138. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_null_reason_and_ripple.py +0 -0
  139. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_rbac.py +0 -0
  140. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_redaction_boundary.py +0 -0
  141. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_reindexer.py +0 -0
  142. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_rerank.py +0 -0
  143. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_reset.py +0 -0
  144. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_source_kind.py +0 -0
  145. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_symbol_context.py +0 -0
  146. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_term.py +0 -0
  147. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_treesitter.py +0 -0
  148. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_verify_call.py +0 -0
  149. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_version_skew.py +0 -0
  150. {codecortex-0.22.0 → codecortex-0.23.1}/tests/test_wire_text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.22.0
3
+ Version: 0.23.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
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
20
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
22
  Classifier: Topic :: Software Development :: Quality Assurance
22
23
  Classifier: Typing :: Typed
@@ -33,6 +34,8 @@ Requires-Dist: pytest-cov>=5; extra == "dev"
33
34
  Requires-Dist: numpy>=1.24; extra == "dev"
34
35
  Requires-Dist: ruff>=0.16; extra == "dev"
35
36
  Requires-Dist: mypy>=1.11; extra == "dev"
37
+ Provides-Extra: bench
38
+ Requires-Dist: anthropic>=0.40; extra == "bench"
36
39
  Dynamic: license-file
37
40
 
38
41
  # codeintel
@@ -55,122 +58,19 @@ Dynamic: license-file
55
58
 
56
59
  > *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)**.
57
60
 
58
- 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:
59
-
60
- <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.">
61
-
62
- **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:
63
-
64
- - **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 short, tool-naming pointer block into `AGENTS.md` (the cross-tool surface read by Codex, Cursor, Zed, and others — created with your consent if it doesn't exist yet) plus a one-line `@AGENTS.md` import into `CLAUDE.md`, and writes the fuller [`USING_CODEINTEL.md`](USING_CODEINTEL.md) guide the block points to — so an agent knows to reach for `code.query` before it reaches for grep, not just that a `CODE_INTEL.md` exists.
65
- - **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.
66
- - **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.
67
-
68
- See **[docs/map-file.md](docs/map-file.md)** for the format and the `--inject` flow.
69
-
70
- ## Why an agent needs it
71
-
72
- 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:
73
-
74
- - **"What calls this? What breaks if I change it?"** → the real call graph, which catches cross-file and module-level callers a text search silently misses.
75
- - **"Where is this symbol defined, and everywhere it's used?"** → the language server, with exact locations.
76
- - **"Where's the code that does X?"** (when you don't know the name) → semantic search over the repo.
77
- - **Always a clean answer.** Every call returns the same JSON envelope. A missing or broken backend degrades to a safe `null` *with a reason* — so the agent falls back to grep instead of crashing on an exception it can't reason its way out of.
78
-
79
- Net effect: fewer, sharper tool calls, less re-reading, and an agent that can see *structure* — callers, impact, call chains — that plain search can't.
80
-
81
- **The honest framing.** Agentic grep is still the backbone, and codeintel doesn't claim otherwise — Claude Code itself ships grep-only and that is a reasonable default for most of what an agent reads. The defensible claim is narrower: a structural index *where it pays*, degrading to grep the moment an engine is missing or not indexed, which is exactly what the safe-null contract above already does under the hood. Worth saying explicitly rather than leaving it implicit in a failure mode.
82
-
83
- ## What your agent can ask
84
-
85
- It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the default) codeintel picks the engine per operation:
86
-
87
- | Ask | `op` | Engine (auto) | Comes back as |
88
- |---|---|---|---|
89
- | Find code by meaning ("auth middleware") | `search` | semantic | ranked `path:line │ snippet` hits |
90
- | A symbol's definition **and** all references | `symbol` | lsp | definition body + reference list |
91
- | Who calls this? | `callers` | graph | caller symbols + files |
92
- | What does this call? | `callees` | graph | callee symbols + files |
93
- | Blast radius of a change | `impact` | graph | callers **and** callees together |
94
- | Trace a call chain up/downstream | `chain` | graph | ordered, risk-labeled hops |
95
- | Find symbols by pattern | `pattern` | graph | matching nodes + locations |
96
- | Project shape at a glance | `overview` | graph → lsp | modules, node/edge counts, languages |
97
- | Everything about one symbol | `context` | graph + lsp | both views merged |
98
- | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
99
- | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
100
- | Unreferenced (dead) code | `deadcode` | graph | **withdrawn and now retired** — a labelled corpus measured its precision at 25%; safe-nulls with `reason: "op-withdrawn"`, and no flag brings it back — [the measurement, and what to use instead](#deadcode-is-retired) |
101
-
102
- Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
103
-
104
- `callers`, `callees` and `impact` resolve the target by its **unqualified name**. When several
105
- symbols share it, each matched symbol's rows are reported separately under its own heading and the
106
- result says how many it found — narrow to one with a qualified target (`core.Group.invoke`) or a file
107
- hint (`invoke@src/click/testing.py`); see
108
- [when several symbols share a name](docs/graph.md#when-several-symbols-share-a-name).
109
-
110
- #### `deadcode` is retired
111
-
112
- `deadcode` no longer exists. Asking for it returns a safe-null (`reason: "op-withdrawn"`) with a hint
113
- naming what to use instead, and **no flag brings it back** — the implementation has been deleted.
114
-
115
- It was withdrawn pending one condition: *"it returns when a labelled corpus measures its precision
116
- and recall — not before."* That corpus now exists, in
117
- [`tests/test_corpus.py`](tests/test_corpus.py), and the measurement is what retired it.
118
-
119
- **How it was measured.** Two pinned real Python repositories (`pallets/click`, `psf/requests`), with
120
- every function and method collected from the **AST** — 2,425 definitions, `async def` and class
121
- methods included, because a verification whose population comes from a pattern like `^\s*def ` cannot
122
- see half of them. Each is labelled live or dead with the reference behind the label recorded beside
123
- it. The oracle errs toward *live*: a decorator, a dunder, an override of an external interface, a
124
- string-dispatch mention, or public-API status is each enough to call a symbol live, so "dead" is only
125
- what survives all of them. That biases the numbers against the op, which is the correct direction for
126
- a check whose output is an instruction to delete code. Known-answer canaries are planted in both trees
127
- so recall has a denominator at all.
128
-
129
- **The numbers.**
130
-
131
- | | precision | recall |
132
- |---|---|---|
133
- | as shipped | **25%** (6 of 24) | 60% (6 of 10) |
134
- | with the two repairs this codebase already contains elsewhere | 89% (8 of 9) | 80% (8 of 10) |
135
-
136
- And the measurement that decided it — **real code only, canaries removed**: the op as shipped named
137
- **18 candidates across those two repositories, and every one of them was live.** All 18 were Makefile
138
- targets, which the graph backend indexes as `Function` nodes. Repaired, it names exactly one, and
139
- that one is `MockRequest.get_type` in requests — a method `http.cookiejar` calls by duck-typed
140
- convention, whose name appears once in the source.
141
-
142
- **Why it was not repaired further.** The verification was a name-frequency scan over the source, so it
143
- fails on exactly one condition: a symbol whose name appears once and is called by a convention
144
- outside the source. Two repositories produced three distinct instances of that condition — non-code
145
- nodes labelled `Function`, interpreter-called dunders, and stdlib duck-typed protocol methods — and
146
- the earlier TypeScript evidence adds a rollup plugin hook and object-literal properties. The set is
147
- not enumerable: no specification lists `get_type`. Every repository added revealed a new member of it.
148
-
149
- Weighed against that: in 2,425 real definitions across two maintained repositories there was **not
150
- one** dead private symbol to find. An op whose measured yield on real code is zero true positives has
151
- no benefit to set against that error rate.
152
-
153
- **Use `callers` on a specific symbol instead.** "Does anything call this?" is exactly the question
154
- `deadcode` was trying to answer in bulk, and `callers` answers it accurately, one symbol at a time.
155
-
156
- ## What makes it good
157
-
158
- - **Local-first and private.** One process on your machine — no cloud service, no API keys, no telemetry, no per-query network. Safe to point at a private repo, even with `--engine all`. (The one-time exception: `fastembed` downloads its embedding model once, then runs fully offline.)
159
- - **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`.
160
- - **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.
161
- - **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.
162
- - **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).)
163
- - **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.
164
- - **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.
165
-
166
61
  ## Quickstart
167
62
 
168
63
  ```bash
169
64
  pip install codecortex
170
65
  ```
171
66
 
172
- This installs the `codeintel` CLI; the **semantic** engine works out of the box. (On PyPI the
173
- distribution is `codecortex` because `codeintel` was taken; the CLI and import stay `codeintel`.)
67
+ This installs the `codeintel` CLI; the **semantic** engine works out of the box with one
68
+ network step: `fastembed` downloads its ~50 MB embedding model from `huggingface.co` the first
69
+ time you index, then runs fully offline. Behind a corporate proxy, in restricted CI, or
70
+ air-gapped, that download is the one thing here that can fail — see
71
+ [Offline / air-gapped install](docs/install.md#offline--air-gapped-install) for the
72
+ `FASTEMBED_CACHE_PATH` workaround. (On PyPI the distribution is `codecortex` because `codeintel`
73
+ was taken; the CLI and import stay `codeintel`.)
174
74
 
175
75
  **One command prepares the rest and indexes your repo:**
176
76
 
@@ -184,19 +84,34 @@ remaining step. It's idempotent, so re-running is safe. The **graph** engine (`c
184
84
  is an *optional* external binary that adds who-calls / impact / hotspots / `changed`; codeintel is
185
85
  fully usable without it.
186
86
 
187
- Or from source:
87
+ **Then verify, before trusting an answer.** `doctor` answers three separate questions per engine —
88
+ *installed?* *runnable?* *is this repo indexed?* — so "installed" is never mistaken for "working",
89
+ and names the one command that fixes each gap:
188
90
 
189
91
  ```bash
190
- git clone https://github.com/hamilton-sky/codeintel.git
191
- cd codeintel
192
- pip install -e .
92
+ codeintel doctor
193
93
  ```
194
94
 
195
- Register with your AI agent(s), then query:
95
+ **Register with your agent, and ask it something grep cannot answer:**
196
96
 
197
97
  ```bash
198
98
  codeintel install # registers with the agents you actually have installed
199
- codeintel query --op search --target "authentication middleware"
99
+ codeintel query --op callers --target aFunctionYouKnow
100
+ ```
101
+
102
+ `callers` is the first query worth running because you can check it by eye: it should name the call
103
+ sites you already know about, including cross-file and module-level ones a text search misses. If
104
+ the graph engine isn't installed it safe-nulls with a reason and a hint rather than failing — that
105
+ is the contract, visible on your first call. `--op search --target "authentication middleware"`
106
+ works with no backend at all, but a semantic hit on an unfamiliar repo is harder to judge, which
107
+ makes it the weaker thing to try first.
108
+
109
+ Or from source:
110
+
111
+ ```bash
112
+ git clone https://github.com/hamilton-sky/codeintel.git
113
+ cd codeintel
114
+ pip install -e .
200
115
  ```
201
116
 
202
117
  ### Or: have your agent set it up
@@ -281,7 +196,7 @@ Then it launches the exact command it registered and drives a real MCP handshake
281
196
  ```text
282
197
  v claude: registered at /Users/you/.claude.json
283
198
 
284
- v verified: codeintel 0.22.0 — 4 tools (code.query, code.status, code.doctor, code.map)
199
+ v verified: codeintel 0.23.0 — 4 tools (code.query, code.status, code.doctor, code.map)
285
200
  ```
286
201
 
287
202
  If the command is not on `PATH`, or the server fails to start, install says so and exits non-zero
@@ -298,6 +213,136 @@ against the built wheel in a clean environment: it registers Codex and Claude Co
298
213
  > Full reference — what each host reads, the absolute-path rationale, and troubleshooting:
299
214
  > **[docs/install.md](docs/install.md)**.
300
215
 
216
+ ## Why an agent needs it
217
+
218
+ 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:
219
+
220
+ - **"What calls this? What breaks if I change it?"** → the real call graph, which catches cross-file and module-level callers a text search silently misses.
221
+ - **"Where is this symbol defined, and everywhere it's used?"** → the language server, with exact locations.
222
+ - **"Where's the code that does X?"** (when you don't know the name) → semantic search over the repo.
223
+ - **Always a clean answer.** Every call returns the same JSON envelope. A missing or broken backend degrades to a safe `null` *with a reason* — so the agent falls back to grep instead of crashing on an exception it can't reason its way out of.
224
+
225
+ Net effect: fewer, sharper tool calls, less re-reading, and an agent that can see *structure* — callers, impact, call chains — that plain search can't.
226
+
227
+ **The honest framing.** Agentic grep is still the backbone, and codeintel doesn't claim otherwise — Claude Code itself ships grep-only and that is a reasonable default for most of what an agent reads. The defensible claim is narrower: a structural index *where it pays*, degrading to grep the moment an engine is missing or not indexed, which is exactly what the safe-null contract above already does under the hood. Worth saying explicitly rather than leaving it implicit in a failure mode.
228
+
229
+ ## What your agent can ask
230
+
231
+ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the default) codeintel picks the engine per operation:
232
+
233
+ | Ask | `op` | Engine (auto) | Comes back as |
234
+ |---|---|---|---|
235
+ | Find code by meaning ("auth middleware") | `search` | semantic | ranked `path:line │ snippet` hits |
236
+ | A symbol's definition **and** all references | `symbol` | lsp | definition body + reference list |
237
+ | Who calls this? | `callers` | graph | caller symbols + files |
238
+ | What does this call? | `callees` | graph | callee symbols + files |
239
+ | Blast radius of a change | `impact` | graph | callers **and** callees together |
240
+ | Trace a call chain up/downstream | `chain` | graph | ordered hops, each labelled with how it was resolved |
241
+ | Find symbols by pattern | `pattern` | graph | matching nodes + locations |
242
+ | Project shape at a glance | `overview` | graph → lsp | modules, node/edge counts, languages |
243
+ | Everything about one symbol | `context` | graph + lsp | both views merged |
244
+ | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
245
+ | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
246
+ | Unreferenced (dead) code | `deadcode` | graph | **withdrawn and now retired** — a labelled corpus measured its precision at 25%; safe-nulls with `reason: "op-withdrawn"`, and no flag brings it back — [the measurement, and what to use instead](#deadcode-is-retired) |
247
+
248
+ Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
249
+
250
+ `callers`, `callees` and `impact` resolve the target by its **unqualified name**. When several
251
+ symbols share it, each matched symbol's rows are reported separately under its own heading and the
252
+ result says how many it found — narrow to one with a qualified target (`core.Group.invoke`) or a file
253
+ hint (`invoke@src/click/testing.py`); see
254
+ [when several symbols share a name](docs/graph.md#when-several-symbols-share-a-name).
255
+
256
+ #### `deadcode` is retired
257
+
258
+ `deadcode` no longer exists. Asking for it returns a safe-null (`reason: "op-withdrawn"`) with a
259
+ hint naming what to use instead, and **no flag brings it back** — the implementation has been
260
+ deleted. A labelled corpus of 2,425 AST-collected definitions across `pallets/click` and
261
+ `psf/requests` measured it at **25% precision**, and on real code with the canaries removed it
262
+ named 18 candidates of which **every one was live**.
263
+
264
+ **Use `callers` on a specific symbol instead.** "Does anything call this?" is exactly the question
265
+ `deadcode` was trying to answer in bulk, and `callers` answers it accurately, one symbol at a time.
266
+
267
+ The full measurement — the oracle's design, why it errs toward *live*, the two repairs that would
268
+ take it to 89%, and why it was not repaired further — is
269
+ [ADR 0002](docs/adr/0002-retire-deadcode.md).
270
+
271
+ ## A committed architecture overview
272
+
273
+ 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:
274
+
275
+ <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.">
276
+
277
+ **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:
278
+
279
+ - **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 short, tool-naming pointer block into `AGENTS.md` (the cross-tool surface read by Codex, Cursor, Zed, and others — created with your consent if it doesn't exist yet) plus a one-line `@AGENTS.md` import into `CLAUDE.md`, and writes the fuller `USING_CODEINTEL.md` guide the block points to — into **your** repo, which is why there is no such file to click here — so an agent knows to reach for `code.query` before it reaches for grep, not just that a `CODE_INTEL.md` exists.
280
+ - **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.
281
+ - **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. The ranking counts `CALLS` edges into **callable** nodes only (functions, methods, classes, interfaces, routes), and skips test, generated and archived files. Both constraints are load-bearing rather than tidy-up: without them a 12,638-node TypeScript repo ranked `logger` (a *folder* node) at 716 and `onClick` at 107, and a Python repo ranked YAML and JSON keys — `flow` from a `.flow.yaml`, `feature` from a `.schema.json` — as its most depended-on symbols. This repo's own map is Python-heavy and barely showed it, which is exactly why dogfooding did not catch it.
282
+
283
+ See **[docs/map-file.md](docs/map-file.md)** for the format and the `--inject` flow.
284
+
285
+ ## An architecture model, as source
286
+
287
+ `codeintel c4` turns the graph index into a [LikeC4](https://likec4.dev) model — architecture-as-code
288
+ you can commit, diff and hand-edit, rather than a rendered picture you have to regenerate to read.
289
+ Abridged output, from `--scope src/codeintel/providers`:
290
+
291
+ ```
292
+ // Generated by codeintel from the graph index — do not edit by hand.
293
+ // edges: union of IMPORTS (static module-level imports) and CALLS|USAGE (call/usage references)
294
+ // coverage is NOT complete: dispatch through a name/lookup table can still be invisible to both
295
+ // hotspot ranking uses IMPORTS-only fan-in, never the CALLS|USAGE union
296
+
297
+ model {
298
+ src = area 'src' {
299
+ codeintel = area 'codeintel' {
300
+ providers = area 'providers' {
301
+ lsp = module 'lsp' {
302
+ technology 'Python'
303
+ metadata { path 'src/codeintel/providers/lsp.py' churn '12' fan_out '2' }
304
+ }
305
+ graph = module 'graph' { /* … */ }
306
+ }
307
+ }
308
+ }
309
+
310
+ src.codeintel.providers.lsp -[calls_usage]-> src.codeintel.providers.graph { metadata { n '2' } }
311
+ }
312
+ ```
313
+
314
+ Directories become `area`, files become `module`, and every element carries its real path plus churn
315
+ and fan-in as `metadata` — so the model is queryable, not only drawable. Each file opens with what it
316
+ cannot know: which edge sources it used, that coverage is incomplete, and how many references were
317
+ folded or dropped. `npx likec4 start codeintel-c4` renders it; nothing else here needs Node.
318
+
319
+ **Two edge sources, and the choice matters more than it sounds.** The default unions static `IMPORTS`
320
+ with `CALLS|USAGE`, which recovers lazy and function-body imports. On this repo at
321
+ `--scope src --depth 3` that is 179 relations — **134 of them `CALLS|USAGE`-only**, which renders as a
322
+ hairball. `--edges imports` leaves the same 45 elements with 45 edges: legible, and roughly layered,
323
+ because every remaining edge descends.
324
+
325
+ **It also answers two questions without drawing anything.** `--layers` infers architectural bands
326
+ from the import graph — 7 bands over 35 of those 45 elements, the other 10 having no module-level
327
+ import edge either way. `--check` gates CI on a declared
328
+ `[layers]` block in `.codeintel.toml`, exiting **2** on architectural drift — deliberately distinct
329
+ from exit 1 for a broken run, so a CI step can tell "your architecture drifted" from "codeintel is
330
+ broken". A config generated by `--suggest-config` is a *provably* green baseline on the commit that
331
+ produced it, so adoption is paste-confirm-tighten rather than a wall of false positives.
332
+
333
+ Full reference: **[docs/c4.md](docs/c4.md)**. Why LikeC4 and not something else, measured:
334
+ [docs/eval-2026-08-26-likec4.md](docs/eval-2026-08-26-likec4.md).
335
+
336
+ ## What makes it good
337
+
338
+ - **Local-first and private.** One process on your machine — no cloud service, no API keys, no telemetry, no per-query network. Safe to point at a private repo, even with `--engine all`. (The one-time exception: `fastembed` downloads its embedding model once, then runs fully offline.)
339
+ - **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`.
340
+ - **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.
341
+ - **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.
342
+ - **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).)
343
+ - **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.
344
+ - **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.
345
+
301
346
  ## How it works
302
347
 
303
348
  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.
@@ -343,7 +388,7 @@ The optional keys are the ones worth reading when an answer surprises you:
343
388
 
344
389
  | Engine | Key ops | Install prereq |
345
390
  |---|---|---|
346
- | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` **0.9.x** on PATH (`pip install 'codebase-memory-mcp==0.9.*'`) — 0.10.x changed its response format and returns nothing for every op but resolution; see [docs/graph.md](docs/graph.md) |
391
+ | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` **0.9.x or 0.10.x** on PATH — both wire dialects are read; `0.10.x` is the more accurate backend and is recommended (`pip install 'codebase-memory-mcp==0.10.*'`). See [docs/graph.md](docs/graph.md) |
347
392
  | `lsp` | `symbol`, `overview`, `context` | `uvx` on PATH — serena is fetched from GitHub on first use; see [docs/lsp.md](docs/lsp.md) |
348
393
  | `semantic` | `search`, `context` | `fastembed` + `sqlite-vec` (installed with the package) — see [docs/semantic.md](docs/semantic.md) |
349
394
 
@@ -396,7 +441,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
396
441
  |---|---|
397
442
  | `codeintel help` | Every command grouped by task, with descriptions and examples (also the bare `codeintel`). A mistyped command suggests what you meant. |
398
443
  | `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 |
399
- | `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 |
444
+ | `codeintel setup [project_root] [--all] [--index] [--warm] [--languages] [--install-uv] [--install-deps] [--json]` | Prepare backends + index this repo (`--all` = one command: do everything automatable, idempotent); ends with a health report + **Next:** steps |
400
445
  | `codeintel prompt [project_root] [--agent auto\|claude\|codex\|gemini\|zed] [--fresh] [--deep]` | Print a paste-to-your-agent setup prompt; probes health and emits only the outstanding steps (or "just restart me" when already healthy). `--fresh` = the full sequence from `pip install`, to send a friend |
401
446
  | `codeintel index [project_root] [--quiet]` | Index a repo (semantic embeddings + best-effort graph & map refresh), with a live progress display; `--quiet` prints only the result line |
402
447
  | `codeintel serve` | Start the MCP server (stdio transport) |
@@ -406,7 +451,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
406
451
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
407
452
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
408
453
  | `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) |
409
- | `codeintel c4 [project_root] [--out DIR] [--depth N] [--scope PATH] [--include-tests] [--no-index] [--json]` | Write a LikeC4 architecture model (`.c4`) of the repo's file/directory structure and its import graph committable, diffable, hand-editable source rather than a rendered picture. Indexes the repo first if it has no graph index. See [docs/c4.md](docs/c4.md) |
454
+ | `codeintel c4 [project_root] [--out DIR] [--depth N] [--scope PATH] [--include-tests] [--no-index] [--edges union\|imports] [--json] [--layers] [--suggest-config] [--check] [--layers-from auto\|inferred\|declared]` | Write a LikeC4 architecture model (`.c4`) of the repo's files and import graph. Indexes the repo first if it has no graph index. `--edges imports` drops the lower-confidence edges for a legible diagram; `--layers` reports inferred architectural bands; `--check` gates CI on a declared `[layers]` config, exit 2 on drift. See [An architecture model, as source](#an-architecture-model-as-source) and [docs/c4.md](docs/c4.md) |
410
455
  | `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear this repo's index — **both** semantic and graph — so it's as if never indexed; `--all` wipes every repo. Recovers from a corrupt/stale DB |
411
456
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
412
457
 
@@ -489,7 +534,7 @@ Register codeintel as an MCP server (`codeintel install`) and the agent gets fou
489
534
  | MCP tool | HTTP equivalent | Purpose |
490
535
  |---|---|---|
491
536
  | `code.query` | `POST /code/query` | The main call — search, trace, understand (the `op` table above) |
492
- | `code.status` | `GET /code/status` | Per-engine `installed` / `runnable` / `repo_indexed`, probed against the live engines a query actually hits |
537
+ | `code.status` | `GET /code/status` | Per-engine `installed` / `runnable` / `repo_indexed` (plus `model_cached` for semantic), probed against the live engines a query actually hits |
493
538
  | `code.doctor` | `POST /code/doctor` | Per-engine health + repo index status, with a fix for each gap |
494
539
  | `code.map` | — | Generate/refresh `CODE_INTEL.md`, a static orientation file for hosts without MCP |
495
540
 
@@ -595,7 +640,8 @@ answer — and `deadcode` in particular is retired rather than merely caveated (
595
640
  something looks off please [report it](#reporting-a-problem) — an issue from someone who is not the
596
641
  author is the single most useful thing this project can receive right now.
597
642
 
598
- **Engine coverage depends on external binaries.** Semantic search works out of the box. The graph
643
+ **Engine coverage depends on external binaries.** Semantic search works out of the box once its
644
+ embedding model is cached ([offline install](docs/install.md#offline--air-gapped-install)). The graph
599
645
  engine needs `codebase-memory-mcp` and the LSP engine needs `uvx` on `PATH` — without them those
600
646
  engines safe-null and you get a fraction of the capability table above. `codeintel setup --all`
601
647
  installs what it can and `codeintel doctor` tells you exactly what is missing and how to fix it.
@@ -646,7 +692,7 @@ cd codeintel
646
692
  pip install -e .[dev]
647
693
 
648
694
  pytest tests/ -q # ~740 tests, ~30s; fails under 83% coverage
649
- ruff check src tests # lint
695
+ ruff check src tests bench # lint
650
696
  mypy # types (src/ only)
651
697
  ```
652
698