codecortex 0.9.0__tar.gz → 0.11.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 (74) hide show
  1. {codecortex-0.9.0/src/codecortex.egg-info → codecortex-0.11.0}/PKG-INFO +22 -14
  2. {codecortex-0.9.0 → codecortex-0.11.0}/README.md +21 -13
  3. {codecortex-0.9.0 → codecortex-0.11.0}/pyproject.toml +4 -0
  4. {codecortex-0.9.0 → codecortex-0.11.0/src/codecortex.egg-info}/PKG-INFO +22 -14
  5. {codecortex-0.9.0 → codecortex-0.11.0}/src/codecortex.egg-info/SOURCES.txt +4 -0
  6. codecortex-0.11.0/src/codeintel/__init__.py +1 -0
  7. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/__main__.py +40 -4
  8. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/doctor.py +17 -4
  9. codecortex-0.11.0/src/codeintel/grapher.py +115 -0
  10. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/onboarding.py +76 -12
  11. codecortex-0.11.0/src/codeintel/viewer/__init__.py +1 -0
  12. codecortex-0.11.0/src/codeintel/viewer/graph_template.html +490 -0
  13. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_doctor.py +18 -0
  14. codecortex-0.11.0/tests/test_grapher.py +127 -0
  15. codecortex-0.11.0/tests/test_onboarding.py +160 -0
  16. codecortex-0.9.0/src/codeintel/__init__.py +0 -1
  17. codecortex-0.9.0/tests/test_onboarding.py +0 -78
  18. {codecortex-0.9.0 → codecortex-0.11.0}/LICENSE +0 -0
  19. {codecortex-0.9.0 → codecortex-0.11.0}/setup.cfg +0 -0
  20. {codecortex-0.9.0 → codecortex-0.11.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
  21. {codecortex-0.9.0 → codecortex-0.11.0}/src/codecortex.egg-info/entry_points.txt +0 -0
  22. {codecortex-0.9.0 → codecortex-0.11.0}/src/codecortex.egg-info/requires.txt +0 -0
  23. {codecortex-0.9.0 → codecortex-0.11.0}/src/codecortex.egg-info/top_level.txt +0 -0
  24. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/auth.py +0 -0
  25. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/cache.py +0 -0
  26. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/config.py +0 -0
  27. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/gateway.py +0 -0
  28. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/http_server.py +0 -0
  29. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/indexer.py +0 -0
  30. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/injector.py +0 -0
  31. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/installer.py +0 -0
  32. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/logconfig.py +0 -0
  33. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/mapper.py +0 -0
  34. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/metrics.py +0 -0
  35. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/policy.py +0 -0
  36. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/provider.py +0 -0
  37. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/providers/__init__.py +0 -0
  38. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/providers/graph.py +0 -0
  39. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/providers/lsp.py +0 -0
  40. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/providers/none.py +0 -0
  41. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/providers/semantic.py +0 -0
  42. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/reindexer.py +0 -0
  43. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/reset.py +0 -0
  44. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/searcher.py +0 -0
  45. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/semantic_db.py +0 -0
  46. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/server.py +0 -0
  47. {codecortex-0.9.0 → codecortex-0.11.0}/src/codeintel/term.py +0 -0
  48. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_cache.py +0 -0
  49. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_chunking.py +0 -0
  50. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_config.py +0 -0
  51. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_e2e.py +0 -0
  52. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_enterprise.py +0 -0
  53. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_gateway.py +0 -0
  54. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_graph_provider.py +0 -0
  55. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_graph_real.py +0 -0
  56. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_graph_stdin.py +0 -0
  57. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_hardening.py +0 -0
  58. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_http_auth.py +0 -0
  59. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_http_server.py +0 -0
  60. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_installer.py +0 -0
  61. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_integration.py +0 -0
  62. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_lsp_provider.py +0 -0
  63. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_lsp_real.py +0 -0
  64. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_mapper.py +0 -0
  65. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_mcp_server.py +0 -0
  66. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_model_dimension.py +0 -0
  67. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_never_raise.py +0 -0
  68. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_rbac.py +0 -0
  69. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_reindexer.py +0 -0
  70. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_rerank.py +0 -0
  71. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_reset.py +0 -0
  72. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_semantic_provider.py +0 -0
  73. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_term.py +0 -0
  74. {codecortex-0.9.0 → codecortex-0.11.0}/tests/test_treesitter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.9.0
3
+ Version: 0.11.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
@@ -60,10 +60,13 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
60
60
  | Who calls this? | `callers` | graph | caller symbols + files |
61
61
  | What does this call? | `callees` | graph | callee symbols + files |
62
62
  | Blast radius of a change | `impact` | graph | callers **and** callees together |
63
- | Trace a call chain up/downstream | `chain` | graph | ordered hops |
63
+ | Trace a call chain up/downstream | `chain` | graph | ordered, risk-labeled hops |
64
64
  | Find symbols by pattern | `pattern` | graph | matching nodes + locations |
65
65
  | Project shape at a glance | `overview` | graph → lsp | modules, node/edge counts, languages |
66
66
  | Everything about one symbol | `context` | graph + lsp | both views merged |
67
+ | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
68
+ | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
69
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
67
70
 
68
71
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
69
72
 
@@ -99,11 +102,21 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
99
102
  pip install codecortex
100
103
  ```
101
104
 
102
- This installs the `codeintel` CLI; the **semantic** engine works out of the box. The **graph**
103
- and **LSP** engines use external backends (`codebase-memory-mcp`, and serena via `uvx`) —
104
- run `codeintel doctor` to see what's available and how to enable the rest. (On PyPI the
105
+ This installs the `codeintel` CLI; the **semantic** engine works out of the box. (On PyPI the
105
106
  distribution is `codecortex` because `codeintel` was taken; the CLI and import stay `codeintel`.)
106
107
 
108
+ **One command prepares the rest and indexes your repo:**
109
+
110
+ ```bash
111
+ codeintel setup --all /path/to/your/project
112
+ ```
113
+
114
+ This installs `uv` (for the LSP engine), warms serena, downloads the embedding model, indexes the
115
+ repo, and prints a health report ending in a **Next:** list — exactly what's ready and the one
116
+ remaining step. It's idempotent, so re-running is safe. The **graph** engine (`codebase-memory-mcp`)
117
+ is an *optional* external binary that adds who-calls / impact / hotspots / `changed`; codeintel is
118
+ fully usable without it.
119
+
107
120
  Or from source:
108
121
 
109
122
  ```bash
@@ -112,17 +125,10 @@ cd codeintel
112
125
  pip install -e .
113
126
  ```
114
127
 
115
- Register with your AI agent(s):
128
+ Register with your AI agent(s), then query:
116
129
 
117
130
  ```bash
118
131
  codeintel install # registers with Claude, Codex, Gemini, Zed
119
- ```
120
-
121
- Index a project, check what's ready, and run your first query:
122
-
123
- ```bash
124
- codeintel index /path/to/your/project
125
- codeintel doctor /path/to/your/project # which engines are ready + how to fix the rest
126
132
  codeintel query --op search --target "authentication middleware"
127
133
  ```
128
134
 
@@ -174,6 +180,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
174
180
  - **[Architecture](docs/architecture.md)** — layers, the `CodeProvider` protocol, the safe-null contract, caching, freshness (ASCII + Mermaid).
175
181
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
176
182
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
183
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
177
184
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
178
185
 
179
186
  ## CLI reference
@@ -181,7 +188,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
181
188
  | Command | Purpose |
182
189
  |---|---|
183
190
  | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
184
- | `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
191
+ | `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 |
185
192
  | `codeintel index [project_root]` | Index a project for semantic search |
186
193
  | `codeintel serve` | Start the MCP server (stdio transport) |
187
194
  | `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
@@ -189,6 +196,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
189
196
  | `codeintel status [project_root]` | Show engine availability and index age |
190
197
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
191
198
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
199
+ | `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) |
192
200
  | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
193
201
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
194
202
 
@@ -26,10 +26,13 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
26
26
  | Who calls this? | `callers` | graph | caller symbols + files |
27
27
  | What does this call? | `callees` | graph | callee symbols + files |
28
28
  | Blast radius of a change | `impact` | graph | callers **and** callees together |
29
- | Trace a call chain up/downstream | `chain` | graph | ordered hops |
29
+ | Trace a call chain up/downstream | `chain` | graph | ordered, risk-labeled hops |
30
30
  | Find symbols by pattern | `pattern` | graph | matching nodes + locations |
31
31
  | Project shape at a glance | `overview` | graph → lsp | modules, node/edge counts, languages |
32
32
  | Everything about one symbol | `context` | graph + lsp | both views merged |
33
+ | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
34
+ | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
35
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
33
36
 
34
37
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
35
38
 
@@ -65,11 +68,21 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
65
68
  pip install codecortex
66
69
  ```
67
70
 
68
- This installs the `codeintel` CLI; the **semantic** engine works out of the box. The **graph**
69
- and **LSP** engines use external backends (`codebase-memory-mcp`, and serena via `uvx`) —
70
- run `codeintel doctor` to see what's available and how to enable the rest. (On PyPI the
71
+ This installs the `codeintel` CLI; the **semantic** engine works out of the box. (On PyPI the
71
72
  distribution is `codecortex` because `codeintel` was taken; the CLI and import stay `codeintel`.)
72
73
 
74
+ **One command prepares the rest and indexes your repo:**
75
+
76
+ ```bash
77
+ codeintel setup --all /path/to/your/project
78
+ ```
79
+
80
+ This installs `uv` (for the LSP engine), warms serena, downloads the embedding model, indexes the
81
+ repo, and prints a health report ending in a **Next:** list — exactly what's ready and the one
82
+ remaining step. It's idempotent, so re-running is safe. The **graph** engine (`codebase-memory-mcp`)
83
+ is an *optional* external binary that adds who-calls / impact / hotspots / `changed`; codeintel is
84
+ fully usable without it.
85
+
73
86
  Or from source:
74
87
 
75
88
  ```bash
@@ -78,17 +91,10 @@ cd codeintel
78
91
  pip install -e .
79
92
  ```
80
93
 
81
- Register with your AI agent(s):
94
+ Register with your AI agent(s), then query:
82
95
 
83
96
  ```bash
84
97
  codeintel install # registers with Claude, Codex, Gemini, Zed
85
- ```
86
-
87
- Index a project, check what's ready, and run your first query:
88
-
89
- ```bash
90
- codeintel index /path/to/your/project
91
- codeintel doctor /path/to/your/project # which engines are ready + how to fix the rest
92
98
  codeintel query --op search --target "authentication middleware"
93
99
  ```
94
100
 
@@ -140,6 +146,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
140
146
  - **[Architecture](docs/architecture.md)** — layers, the `CodeProvider` protocol, the safe-null contract, caching, freshness (ASCII + Mermaid).
141
147
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
142
148
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
149
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
143
150
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
144
151
 
145
152
  ## CLI reference
@@ -147,7 +154,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
147
154
  | Command | Purpose |
148
155
  |---|---|
149
156
  | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
150
- | `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
157
+ | `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 |
151
158
  | `codeintel index [project_root]` | Index a project for semantic search |
152
159
  | `codeintel serve` | Start the MCP server (stdio transport) |
153
160
  | `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
@@ -155,6 +162,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
155
162
  | `codeintel status [project_root]` | Show engine availability and index age |
156
163
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
157
164
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
165
+ | `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) |
158
166
  | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
159
167
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
160
168
 
@@ -56,3 +56,7 @@ version = { attr = "codeintel.__version__" }
56
56
 
57
57
  [tool.setuptools.packages.find]
58
58
  where = ["src"]
59
+
60
+ # Ship the self-contained interactive graph viewer template with the wheel.
61
+ [tool.setuptools.package-data]
62
+ "codeintel.viewer" = ["*.html"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.9.0
3
+ Version: 0.11.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
@@ -60,10 +60,13 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
60
60
  | Who calls this? | `callers` | graph | caller symbols + files |
61
61
  | What does this call? | `callees` | graph | callee symbols + files |
62
62
  | Blast radius of a change | `impact` | graph | callers **and** callees together |
63
- | Trace a call chain up/downstream | `chain` | graph | ordered hops |
63
+ | Trace a call chain up/downstream | `chain` | graph | ordered, risk-labeled hops |
64
64
  | Find symbols by pattern | `pattern` | graph | matching nodes + locations |
65
65
  | Project shape at a glance | `overview` | graph → lsp | modules, node/edge counts, languages |
66
66
  | Everything about one symbol | `context` | graph + lsp | both views merged |
67
+ | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
68
+ | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
69
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
67
70
 
68
71
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
69
72
 
@@ -99,11 +102,21 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
99
102
  pip install codecortex
100
103
  ```
101
104
 
102
- This installs the `codeintel` CLI; the **semantic** engine works out of the box. The **graph**
103
- and **LSP** engines use external backends (`codebase-memory-mcp`, and serena via `uvx`) —
104
- run `codeintel doctor` to see what's available and how to enable the rest. (On PyPI the
105
+ This installs the `codeintel` CLI; the **semantic** engine works out of the box. (On PyPI the
105
106
  distribution is `codecortex` because `codeintel` was taken; the CLI and import stay `codeintel`.)
106
107
 
108
+ **One command prepares the rest and indexes your repo:**
109
+
110
+ ```bash
111
+ codeintel setup --all /path/to/your/project
112
+ ```
113
+
114
+ This installs `uv` (for the LSP engine), warms serena, downloads the embedding model, indexes the
115
+ repo, and prints a health report ending in a **Next:** list — exactly what's ready and the one
116
+ remaining step. It's idempotent, so re-running is safe. The **graph** engine (`codebase-memory-mcp`)
117
+ is an *optional* external binary that adds who-calls / impact / hotspots / `changed`; codeintel is
118
+ fully usable without it.
119
+
107
120
  Or from source:
108
121
 
109
122
  ```bash
@@ -112,17 +125,10 @@ cd codeintel
112
125
  pip install -e .
113
126
  ```
114
127
 
115
- Register with your AI agent(s):
128
+ Register with your AI agent(s), then query:
116
129
 
117
130
  ```bash
118
131
  codeintel install # registers with Claude, Codex, Gemini, Zed
119
- ```
120
-
121
- Index a project, check what's ready, and run your first query:
122
-
123
- ```bash
124
- codeintel index /path/to/your/project
125
- codeintel doctor /path/to/your/project # which engines are ready + how to fix the rest
126
132
  codeintel query --op search --target "authentication middleware"
127
133
  ```
128
134
 
@@ -174,6 +180,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
174
180
  - **[Architecture](docs/architecture.md)** — layers, the `CodeProvider` protocol, the safe-null contract, caching, freshness (ASCII + Mermaid).
175
181
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
176
182
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
183
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
177
184
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
178
185
 
179
186
  ## CLI reference
@@ -181,7 +188,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
181
188
  | Command | Purpose |
182
189
  |---|---|
183
190
  | `codeintel install [--agent claude\|codex\|gemini\|zed\|all]` | Register codeintel with AI agent(s) |
184
- | `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
191
+ | `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 |
185
192
  | `codeintel index [project_root]` | Index a project for semantic search |
186
193
  | `codeintel serve` | Start the MCP server (stdio transport) |
187
194
  | `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
@@ -189,6 +196,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
189
196
  | `codeintel status [project_root]` | Show engine availability and index age |
190
197
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
191
198
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
199
+ | `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) |
192
200
  | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
193
201
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
194
202
 
@@ -14,6 +14,7 @@ src/codeintel/cache.py
14
14
  src/codeintel/config.py
15
15
  src/codeintel/doctor.py
16
16
  src/codeintel/gateway.py
17
+ src/codeintel/grapher.py
17
18
  src/codeintel/http_server.py
18
19
  src/codeintel/indexer.py
19
20
  src/codeintel/injector.py
@@ -35,6 +36,8 @@ src/codeintel/providers/graph.py
35
36
  src/codeintel/providers/lsp.py
36
37
  src/codeintel/providers/none.py
37
38
  src/codeintel/providers/semantic.py
39
+ src/codeintel/viewer/__init__.py
40
+ src/codeintel/viewer/graph_template.html
38
41
  tests/test_cache.py
39
42
  tests/test_chunking.py
40
43
  tests/test_config.py
@@ -45,6 +48,7 @@ tests/test_gateway.py
45
48
  tests/test_graph_provider.py
46
49
  tests/test_graph_real.py
47
50
  tests/test_graph_stdin.py
51
+ tests/test_grapher.py
48
52
  tests/test_hardening.py
49
53
  tests/test_http_auth.py
50
54
  tests/test_http_server.py
@@ -0,0 +1 @@
1
+ __version__ = "0.11.0"
@@ -68,6 +68,13 @@ def main() -> None:
68
68
  map_parser.add_argument("--inject", action="store_true", help="Inject reference block into CLAUDE.md/AGENTS.md")
69
69
  map_parser.add_argument("--budget", type=int, default=32768, help="Byte budget for CODE_INTEL.md (default: 32768)")
70
70
 
71
+ # graph subcommand — interactive call-graph view (HTML) or the raw {nodes,edges} JSON
72
+ graph_parser = subparsers.add_parser("graph", help="Build an interactive call-graph view (--html) or emit the graph as JSON — works on any indexed repo")
73
+ graph_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
74
+ graph_parser.add_argument("--html", action="store_true", help="Write a self-contained interactive HTML viewer (default: print JSON)")
75
+ graph_parser.add_argument("--out", default=None, help="Output path for --html (default: codeintel-graph.html)")
76
+ graph_parser.add_argument("--limit", type=int, default=220, help="Max call edges to include (default: 220)")
77
+
71
78
  # doctor subcommand
72
79
  doctor_parser = subparsers.add_parser("doctor", parents=[color_parent], help="Diagnose engine health + index status for a repo")
73
80
  doctor_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
@@ -77,6 +84,9 @@ def main() -> None:
77
84
  # setup subcommand
78
85
  setup_parser = subparsers.add_parser("setup", parents=[color_parent], help="Prepare backends and optionally index this repo")
79
86
  setup_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
87
+ setup_parser.add_argument("--all", action="store_true", dest="all_steps",
88
+ help="One-command setup: do everything automatable (uv + deps + index + "
89
+ "warm serena). Idempotent — skips what's already installed.")
80
90
  setup_parser.add_argument("--install-uv", action="store_true", help="Run `pip install uv` (provides uvx for the LSP engine)")
81
91
  setup_parser.add_argument("--install-deps", action="store_true", help="Run `pip install -e .` (semantic engine deps)")
82
92
  setup_parser.add_argument("--index", action="store_true", help="Index this repo now (first run downloads the ~50MB model)")
@@ -187,6 +197,31 @@ def main() -> None:
187
197
  print(f"map failed: {exc}")
188
198
  sys.exit(0)
189
199
 
200
+ elif args.command == "graph":
201
+ from codeintel import grapher
202
+ project_root = args.project_root or os.getcwd()
203
+ try:
204
+ payload = grapher.build_graph_payload(project_root, limit=args.limit)
205
+ n, e = len(payload.get("nodes", [])), len(payload.get("edges", []))
206
+ if args.html:
207
+ out = args.out or "codeintel-graph.html"
208
+ with open(out, "w", encoding="utf-8") as f:
209
+ f.write(grapher.render_html(payload))
210
+ print(f"Wrote {out} ({n} nodes, {e} edges) — open it in any browser")
211
+ if not n:
212
+ reason = payload.get("reason")
213
+ if reason:
214
+ print(f" (graph empty: {reason} — run `codeintel doctor` to check the "
215
+ f"graph backend + index)")
216
+ else:
217
+ print(" (no internal call edges found for this repo)")
218
+ else:
219
+ import json as _json
220
+ print(_json.dumps(payload, indent=2))
221
+ except Exception as exc:
222
+ print(f"graph failed: {exc}")
223
+ sys.exit(0)
224
+
190
225
  elif args.command == "query":
191
226
  try:
192
227
  import time
@@ -284,12 +319,13 @@ def main() -> None:
284
319
  from codeintel import onboarding
285
320
 
286
321
  project_root = args.project_root or os.getcwd()
322
+ all_steps = getattr(args, "all_steps", False) # --all implies every automatable step
287
323
  report = onboarding.run_setup(
288
324
  project_root,
289
- install_uv=args.install_uv,
290
- install_deps=args.install_deps,
291
- do_index=args.index,
292
- warm_lsp=args.warm,
325
+ install_uv=args.install_uv or all_steps,
326
+ install_deps=args.install_deps or all_steps,
327
+ do_index=args.index or all_steps,
328
+ warm_lsp=args.warm or all_steps,
293
329
  )
294
330
  if args.json:
295
331
  import json as _json
@@ -11,6 +11,9 @@ import os
11
11
  from typing import Any, Callable, Optional
12
12
 
13
13
  _ENGINES = ("graph", "lsp", "semantic")
14
+ # Graph needs an external native binary (codebase-memory-mcp) codeintel can't auto-install, so a
15
+ # missing graph engine does NOT make a repo "unhealthy" — the tool is fully usable on semantic + lsp.
16
+ _OPTIONAL_ENGINES = frozenset({"graph"})
14
17
 
15
18
 
16
19
  def _status_for(report: dict) -> str:
@@ -112,13 +115,17 @@ def run_doctor(
112
115
  treesitter = False
113
116
 
114
117
  ready = sum(1 for e in engines.values() if e.get("status") != "fail")
118
+ # "healthy" ignores OPTIONAL engines (graph): a repo with semantic + lsp ready is healthy even
119
+ # without the external graph binary. `ready`/`total` stay literal (all three) for transparency.
120
+ healthy = all(
121
+ e.get("status") != "fail" for n, e in engines.items() if n not in _OPTIONAL_ENGINES
122
+ )
115
123
  return {
116
124
  "ok": True,
117
125
  "project_root": root,
118
126
  "deep": bool(deep),
119
127
  "treesitter": treesitter,
120
- "summary": {"ready": ready, "total": len(engines),
121
- "healthy": all(e.get("status") != "fail" for e in engines.values())},
128
+ "summary": {"ready": ready, "total": len(engines), "healthy": healthy},
122
129
  "engines": engines,
123
130
  }
124
131
 
@@ -168,10 +175,16 @@ def render_doctor_text(report: dict) -> str:
168
175
  tail = "" if report.get("deep") else c.dim(" (run with --deep to boot-check serena)")
169
176
  out.append("")
170
177
  out.append(f" {count} engines ready for this repo.{tail}")
178
+ opt_down = [n for n in _ENGINES
179
+ if n in _OPTIONAL_ENGINES and (engines.get(n) or {}).get("status") == "fail"]
180
+ if opt_down:
181
+ out.append(" " + c.dim(
182
+ f"({', '.join(opt_down)} optional — an external backend; codeintel works without it)"
183
+ ))
171
184
  if healthy is False:
172
185
  out.append(" " + c.dim(
173
- "tip: `codeintel setup --install-uv --install-deps --index` bootstraps the "
174
- "pip-installable backends; each fix: line above has the per-engine command."
186
+ "tip: `codeintel setup --all` installs + indexes everything automatable in one command; "
187
+ "each fix: line above has the per-engine command."
175
188
  ))
176
189
  if report.get("treesitter") is False:
177
190
  out.append(" " + c.dim(
@@ -0,0 +1,115 @@
1
+ """Build an interactive view of a project's call graph from the graph engine.
2
+
3
+ Headless-first, in board-differ's data→renderer spirit: `build_graph_payload` returns a plain
4
+ ``{project, nodes, edges}`` dict (the machine-readable `--format json` shape); `render_html` wraps
5
+ that payload in the self-contained interactive viewer (``viewer/graph_template.html``). The engine
6
+ produces the *data*; the template is the *renderer*. Both never raise.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ from typing import Any
13
+
14
+ from codeintel.provider import log_swallowed
15
+
16
+ _EMPTY = {"project": "", "engine": "graph", "op": "callgraph", "nodes": [], "edges": []}
17
+
18
+
19
+ def build_graph_payload(project_root: Any, *, limit: int = 220, timeout_ms: int = 8000) -> dict:
20
+ """Query the graph engine for a project's internal call graph → ``{project, nodes, edges}``.
21
+
22
+ Nodes are the symbols that participate in an internal call edge, enriched with the same
23
+ complexity metrics the ``hotspots`` op surfaces; edges are ``CALLS``/``USAGE`` relations.
24
+ Builtins/synthetic nodes are excluded. Returns an empty payload with a ``reason`` on any
25
+ failure — never raises (mirrors the safe-null contract)."""
26
+ try:
27
+ try:
28
+ limit = max(1, min(2000, int(limit)))
29
+ except Exception:
30
+ limit = 220
31
+ try:
32
+ timeout_ms = int(timeout_ms)
33
+ except Exception:
34
+ timeout_ms = 8000
35
+ from codeintel.providers.graph import GraphProvider
36
+ p = GraphProvider()
37
+ if not getattr(p, "available", False):
38
+ return {**_EMPTY, "reason": "engine-unavailable"}
39
+ project = p._resolve_project(str(project_root or ""))
40
+ if not project:
41
+ return {**_EMPTY, "reason": "project-not-indexed"}
42
+
43
+ # per-symbol metrics (same search_graph the `hotspots` op uses)
44
+ metrics: dict[str, dict] = {}
45
+ for r in (p._search_symbols({"label": "Function", "min_degree": 1, "limit": 600}, project, timeout_ms) or []):
46
+ qn = str(r.get("qualified_name") or "")
47
+ if qn:
48
+ metrics[qn] = r
49
+
50
+ # internal call edges — fetch generously and filter builtins/synthetic ('<...>') nodes
51
+ # CLIENT-SIDE (robust across Cypher dialects; mirrors how the hotspots op drops them).
52
+ cypher = (
53
+ "MATCH (a)-[c:CALLS|USAGE]->(b) "
54
+ "RETURN a.qualified_name, a.name, a.file_path, b.qualified_name, b.name, b.file_path, type(c) "
55
+ "LIMIT " + str(min(max(limit * 3, 400), 1500))
56
+ )
57
+
58
+ def _synth(fp: str) -> bool:
59
+ return (not fp) or fp.startswith("<")
60
+
61
+ nodes_by_id: dict[str, dict] = {}
62
+ edges: list[dict] = []
63
+ seen: set = set()
64
+
65
+ def _add(qn: str, name: str, fp: str) -> None:
66
+ if qn and qn not in nodes_by_id:
67
+ m = metrics.get(qn, {})
68
+ nodes_by_id[qn] = {
69
+ "id": qn, "label": name or qn.rsplit(".", 1)[-1], "file": str(fp or ""),
70
+ "complexity": m.get("complexity") or 0, "cognitive": m.get("cognitive") or 0,
71
+ "in_degree": m.get("in_degree") or 0, "out_degree": m.get("out_degree") or 0,
72
+ "lines": m.get("lines") or 0,
73
+ }
74
+
75
+ for e in (p._query_rows(cypher, project, timeout_ms) or []):
76
+ if len(edges) >= limit:
77
+ break
78
+ aq, bq = str(e.get("a.qualified_name") or ""), str(e.get("b.qualified_name") or "")
79
+ af, bf = str(e.get("a.file_path") or ""), str(e.get("b.file_path") or "")
80
+ if not aq or not bq or aq == bq or _synth(af) or _synth(bf):
81
+ continue
82
+ _add(aq, str(e.get("a.name") or ""), af)
83
+ _add(bq, str(e.get("b.name") or ""), bf)
84
+ if (aq, bq) not in seen:
85
+ seen.add((aq, bq))
86
+ edges.append({"from": aq, "to": bq, "type": str(e.get("type(c)") or "")})
87
+
88
+ nodes = list(nodes_by_id.values())
89
+ return {"project": project, "engine": "graph", "op": "callgraph", "nodes": nodes, "edges": edges}
90
+ except Exception as exc:
91
+ log_swallowed("grapher.build_graph_payload", exc)
92
+ return {**_EMPTY, "reason": "error"}
93
+
94
+
95
+ def _template_path() -> str:
96
+ return os.path.join(os.path.dirname(__file__), "viewer", "graph_template.html")
97
+
98
+
99
+ def render_html(payload: dict) -> str:
100
+ """Wrap a graph payload in the self-contained interactive viewer template. Never raises."""
101
+ try:
102
+ # default=str → serialization can't raise on an unexpected value; "</" escape keeps the
103
+ # JSON inside the <script> element (the template's viewer JS re-emits every string via
104
+ # textContent, so there is no innerHTML/XSS sink downstream).
105
+ data = json.dumps(payload, default=str).replace("</", "<\\/")
106
+ with open(_template_path(), encoding="utf-8") as f:
107
+ return f.read().replace("__DATA__", data)
108
+ except Exception as exc:
109
+ log_swallowed("grapher.render_html", exc)
110
+ try:
111
+ body = json.dumps(payload, indent=2, default=str).replace("&", "&amp;").replace("<", "&lt;")
112
+ except Exception:
113
+ body = "(graph payload unavailable)"
114
+ return ("<!doctype html><meta charset=utf-8><title>codeintel graph</title>"
115
+ "<pre style='font:13px ui-monospace,monospace;padding:16px'>" + body + "</pre>")