codecortex 0.8.5__tar.gz → 0.10.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 (70) hide show
  1. {codecortex-0.8.5/src/codecortex.egg-info → codecortex-0.10.0}/PKG-INFO +21 -14
  2. {codecortex-0.8.5 → codecortex-0.10.0}/README.md +20 -13
  3. {codecortex-0.8.5 → codecortex-0.10.0/src/codecortex.egg-info}/PKG-INFO +21 -14
  4. codecortex-0.10.0/src/codeintel/__init__.py +1 -0
  5. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/__main__.py +8 -4
  6. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/doctor.py +17 -4
  7. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/gateway.py +28 -5
  8. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/onboarding.py +76 -12
  9. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/providers/graph.py +191 -2
  10. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/server.py +5 -1
  11. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_doctor.py +18 -0
  12. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_gateway.py +30 -0
  13. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_graph_real.py +220 -0
  14. codecortex-0.10.0/tests/test_onboarding.py +160 -0
  15. codecortex-0.8.5/src/codeintel/__init__.py +0 -1
  16. codecortex-0.8.5/tests/test_onboarding.py +0 -78
  17. {codecortex-0.8.5 → codecortex-0.10.0}/LICENSE +0 -0
  18. {codecortex-0.8.5 → codecortex-0.10.0}/pyproject.toml +0 -0
  19. {codecortex-0.8.5 → codecortex-0.10.0}/setup.cfg +0 -0
  20. {codecortex-0.8.5 → codecortex-0.10.0}/src/codecortex.egg-info/SOURCES.txt +0 -0
  21. {codecortex-0.8.5 → codecortex-0.10.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
  22. {codecortex-0.8.5 → codecortex-0.10.0}/src/codecortex.egg-info/entry_points.txt +0 -0
  23. {codecortex-0.8.5 → codecortex-0.10.0}/src/codecortex.egg-info/requires.txt +0 -0
  24. {codecortex-0.8.5 → codecortex-0.10.0}/src/codecortex.egg-info/top_level.txt +0 -0
  25. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/auth.py +0 -0
  26. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/cache.py +0 -0
  27. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/config.py +0 -0
  28. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/http_server.py +0 -0
  29. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/indexer.py +0 -0
  30. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/injector.py +0 -0
  31. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/installer.py +0 -0
  32. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/logconfig.py +0 -0
  33. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/mapper.py +0 -0
  34. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/metrics.py +0 -0
  35. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/policy.py +0 -0
  36. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/provider.py +0 -0
  37. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/providers/__init__.py +0 -0
  38. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/providers/lsp.py +0 -0
  39. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/providers/none.py +0 -0
  40. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/providers/semantic.py +0 -0
  41. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/reindexer.py +0 -0
  42. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/reset.py +0 -0
  43. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/searcher.py +0 -0
  44. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/semantic_db.py +0 -0
  45. {codecortex-0.8.5 → codecortex-0.10.0}/src/codeintel/term.py +0 -0
  46. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_cache.py +0 -0
  47. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_chunking.py +0 -0
  48. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_config.py +0 -0
  49. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_e2e.py +0 -0
  50. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_enterprise.py +0 -0
  51. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_graph_provider.py +0 -0
  52. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_graph_stdin.py +0 -0
  53. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_hardening.py +0 -0
  54. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_http_auth.py +0 -0
  55. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_http_server.py +0 -0
  56. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_installer.py +0 -0
  57. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_integration.py +0 -0
  58. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_lsp_provider.py +0 -0
  59. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_lsp_real.py +0 -0
  60. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_mapper.py +0 -0
  61. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_mcp_server.py +0 -0
  62. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_model_dimension.py +0 -0
  63. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_never_raise.py +0 -0
  64. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_rbac.py +0 -0
  65. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_reindexer.py +0 -0
  66. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_rerank.py +0 -0
  67. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_reset.py +0 -0
  68. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_semantic_provider.py +0 -0
  69. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_term.py +0 -0
  70. {codecortex-0.8.5 → codecortex-0.10.0}/tests/test_treesitter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.8.5
3
+ Version: 0.10.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) |
@@ -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) |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.8.5
3
+ Version: 0.10.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) |
@@ -0,0 +1 @@
1
+ __version__ = "0.10.0"
@@ -77,6 +77,9 @@ def main() -> None:
77
77
  # setup subcommand
78
78
  setup_parser = subparsers.add_parser("setup", parents=[color_parent], help="Prepare backends and optionally index this repo")
79
79
  setup_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
80
+ setup_parser.add_argument("--all", action="store_true", dest="all_steps",
81
+ help="One-command setup: do everything automatable (uv + deps + index + "
82
+ "warm serena). Idempotent — skips what's already installed.")
80
83
  setup_parser.add_argument("--install-uv", action="store_true", help="Run `pip install uv` (provides uvx for the LSP engine)")
81
84
  setup_parser.add_argument("--install-deps", action="store_true", help="Run `pip install -e .` (semantic engine deps)")
82
85
  setup_parser.add_argument("--index", action="store_true", help="Index this repo now (first run downloads the ~50MB model)")
@@ -284,12 +287,13 @@ def main() -> None:
284
287
  from codeintel import onboarding
285
288
 
286
289
  project_root = args.project_root or os.getcwd()
290
+ all_steps = getattr(args, "all_steps", False) # --all implies every automatable step
287
291
  report = onboarding.run_setup(
288
292
  project_root,
289
- install_uv=args.install_uv,
290
- install_deps=args.install_deps,
291
- do_index=args.index,
292
- warm_lsp=args.warm,
293
+ install_uv=args.install_uv or all_steps,
294
+ install_deps=args.install_deps or all_steps,
295
+ do_index=args.index or all_steps,
296
+ warm_lsp=args.warm or all_steps,
293
297
  )
294
298
  if args.json:
295
299
  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(
@@ -20,11 +20,21 @@ _AUTO_ENGINE: dict[str, str] = {
20
20
  "chain": "graph",
21
21
  "pattern": "graph",
22
22
  "overview": "graph",
23
+ "changed": "graph",
24
+ "changes": "graph",
25
+ "deadcode": "graph",
26
+ "hotspots": "graph",
23
27
  "symbol": "lsp",
24
28
  "search": "semantic",
25
29
  "context": "both", # fan-out; resolved in Phase 4
26
30
  }
27
31
 
32
+ # Ops whose answer depends on live, unhashable state (the git worktree) rather than the indexed
33
+ # content the cache key is built from — caching them would serve a stale answer within a freshness
34
+ # generation. `changed` reads uncommitted edits; `deadcode`/`hotspots` are pure functions of the
35
+ # index and stay cached (correctly keyed by the freshness generation).
36
+ _UNCACHED_OPS: frozenset[str] = frozenset({"changed", "changes"})
37
+
28
38
 
29
39
  class Gateway:
30
40
  def __init__(self, graph=None, lsp=None, semantic=None, policy: TieringPolicy | None = None, reindexer: Reindexer | None = None):
@@ -199,9 +209,17 @@ class Gateway:
199
209
  except Exception:
200
210
  freshness = 0
201
211
 
212
+ # Ops that read live, unhashable state (the git worktree) must never be served from the
213
+ # content-hash cache — it can't see uncommitted edits. Computed ONCE here so it covers
214
+ # BOTH the fan-out and the single-engine paths below (a miss on either serves a stale diff).
215
+ uncacheable = op_str in _UNCACHED_OPS
216
+
202
217
  # Fan-out: dispatch to multiple engines concurrently and merge
203
218
  if engine_str in _FANOUT_ENGINES:
204
- cached_result = self._cache.get(op_str, target_str, engine_str, root_str, freshness)
219
+ cached_result = (
220
+ None if uncacheable
221
+ else self._cache.get(op_str, target_str, engine_str, root_str, freshness)
222
+ )
205
223
  if cached_result is not None:
206
224
  return {**cached_result, "cached": True}
207
225
  if engine_str == "both":
@@ -210,11 +228,15 @@ class Gateway:
210
228
  engines = ["graph", "lsp", "semantic"]
211
229
  fan_results = self._fan_out(engines, op_str, target_str, budget, project_root)
212
230
  result = self._merge(fan_results, op_str, target_str, engine_str)
213
- self._cache.put(op_str, target_str, engine_str, root_str, result, freshness)
231
+ if not uncacheable:
232
+ self._cache.put(op_str, target_str, engine_str, root_str, result, freshness)
214
233
  return result
215
234
 
216
- # Single-engine dispatch
217
- cached_result = self._cache.get(op_str, target_str, engine_str, root_str, freshness)
235
+ # Single-engine dispatch (`uncacheable`, computed above, also guards this path).
236
+ cached_result = (
237
+ None if uncacheable
238
+ else self._cache.get(op_str, target_str, engine_str, root_str, freshness)
239
+ )
218
240
  if cached_result is not None:
219
241
  return {**cached_result, "cached": True}
220
242
  provider = self._provider_for(engine_str)
@@ -236,7 +258,8 @@ class Gateway:
236
258
  if lsp_result.get("result") is not None:
237
259
  result = lsp_result
238
260
 
239
- self._cache.put(op_str, target_str, engine_str, root_str, result, freshness)
261
+ if not uncacheable:
262
+ self._cache.put(op_str, target_str, engine_str, root_str, result, freshness)
240
263
  return result
241
264
 
242
265
  except Exception as exc:
@@ -17,12 +17,25 @@ from codeintel import doctor
17
17
  _ENGINES = ("graph", "lsp", "semantic")
18
18
 
19
19
 
20
+ def _platform_tag() -> str:
21
+ """Best-effort ``System/arch`` (e.g. ``Darwin/arm64``) for platform-specific guidance."""
22
+ try:
23
+ import platform
24
+ return f"{platform.system() or 'your-OS'}/{platform.machine() or 'your-arch'}"
25
+ except Exception:
26
+ return "your platform"
27
+
28
+
20
29
  def _guidance_for(engine: str, probe: dict) -> str:
21
30
  """One-line install instructions for an engine that is not installed."""
22
31
  try:
23
32
  if engine == "graph":
24
- return ("install codebase-memory-mcp (a platform-specific binary) and ensure "
25
- "it's on PATH see its project; then `codeintel index`")
33
+ # codebase-memory-mcp is a standalone native binary distributed by its own project —
34
+ # codeintel can't (and shouldn't) auto-download a third-party binary. Frame it as the
35
+ # OPTIONAL add-on it is: the tool is fully usable on semantic + LSP without it.
36
+ return (f"OPTIONAL (adds who-calls / impact / hotspots / changed): install the "
37
+ f"codebase-memory-mcp binary for {_platform_tag()} on PATH, then re-run setup — "
38
+ f"codeintel works without it")
26
39
  if engine == "lsp":
27
40
  return "install uv (provides uvx): pip install uv — serena is fetched on first use"
28
41
  if engine == "semantic":
@@ -123,10 +136,10 @@ def run_setup(
123
136
  root = str(project_root or "")
124
137
 
125
138
  try:
126
- if warm_lsp:
127
- print(" first serena launch fetches it via uvx; this can be slow the first time…", file=out)
128
139
  try:
129
- report0 = doctor.run_doctor(root, deep=warm_lsp, lsp_deep_timeout_s=lsp_warm_timeout_s)
140
+ # Shallow diagnose only — the warm/deep serena boot happens AFTER the install loop (below)
141
+ # so a freshly-installed uv is visible to it (see the warm_lsp block).
142
+ report0 = doctor.run_doctor(root, deep=False)
130
143
  engines0 = report0.get("engines", {}) if isinstance(report0, dict) else {}
131
144
  except Exception as exc:
132
145
  engines0 = {}
@@ -136,11 +149,18 @@ def run_setup(
136
149
  if probe.get("installed") is False:
137
150
  _step(f"{name}: preflight", probe.get("status", "fail"), _guidance_for(name, probe))
138
151
 
139
- for flag, pkg_args, step_name in (
140
- (install_uv, ["uv"], "install uv"),
141
- (install_deps, ["-e", "."], "install deps (-e .)"),
152
+ # Idempotent installs: skip when the engine already reports installed, so `--all` and re-runs
153
+ # don't reinstall (lsp installed ⇒ uv/uvx present; semantic installed ⇒ deps present).
154
+ lsp_installed = (engines0.get("lsp") or {}).get("installed") is True
155
+ semantic_installed = (engines0.get("semantic") or {}).get("installed") is True
156
+ for flag, pkg_args, step_name, already in (
157
+ (install_uv, ["uv"], "install uv", lsp_installed),
158
+ (install_deps, ["-e", "."], "install deps (-e .)", semantic_installed),
142
159
  ):
143
160
  if flag:
161
+ if already:
162
+ _step(step_name, "ok", "already satisfied — skipped")
163
+ continue
144
164
  r = _pip_install(pkg_args, out=out)
145
165
  _step(step_name, "ok" if r["ok"] else "fail", r["detail"])
146
166
 
@@ -157,8 +177,17 @@ def run_setup(
157
177
  _step("index: graph", "warn", f"graph reindex skipped ({type(exc).__name__})")
158
178
 
159
179
  if warm_lsp:
160
- lsp = engines0.get("lsp") or {}
161
- _step("warm lsp", lsp.get("status", "warn"), lsp.get("detail", ""))
180
+ # Warm with a FRESH deep probe AFTER the install loop: on a fresh machine uv was just
181
+ # installed above, so the pre-install preflight would report lsp missing — emitting a
182
+ # stale, self-contradictory "warm lsp: fail" under "install uv: ok". Re-probe so the boot
183
+ # actually runs against the now-present uvx.
184
+ print(" first serena launch fetches it via uvx; this can be slow the first time…", file=out)
185
+ try:
186
+ from codeintel.providers.lsp import LspProvider
187
+ wl = LspProvider().probe(root, deep=True, timeout_s=lsp_warm_timeout_s)
188
+ _step("warm lsp", doctor._status_for({**wl, "engine": "lsp"}), wl.get("detail", ""))
189
+ except Exception as exc:
190
+ _step("warm lsp", "warn", f"warm attempt failed ({type(exc).__name__})")
162
191
 
163
192
  try:
164
193
  final_doctor = doctor.run_doctor(root)
@@ -170,6 +199,31 @@ def run_setup(
170
199
  "detail": f"setup failed ({type(exc).__name__})"}
171
200
 
172
201
 
202
+ def _next_steps(doctor_report: dict, root: str) -> list[str]:
203
+ """The crisp 'what's left' list after a setup pass, computed from the final doctor state —
204
+ so a user knows the ONE remaining action instead of parsing the engine table."""
205
+ try:
206
+ out: list[str] = []
207
+ engines = doctor_report.get("engines") if isinstance(doctor_report, dict) else None
208
+ engines = engines if isinstance(engines, dict) else {}
209
+ _get = lambda name: engines.get(name) if isinstance(engines.get(name), dict) else {}
210
+ sem, lsp, graph = _get("semantic"), _get("lsp"), _get("graph")
211
+ if sem.get("installed") is not True:
212
+ out.append("Semantic engine deps missing — pip install fastembed sqlite-vec")
213
+ elif sem.get("repo_indexed") is False:
214
+ out.append(f"Index this repo — codeintel index {root}")
215
+ if lsp.get("installed") is not True:
216
+ out.append("LSP engine — pip install uv (serena auto-fetched on first use)")
217
+ if graph.get("installed") is not True:
218
+ out.append(f"Graph engine (OPTIONAL — who-calls/impact/hotspots/changed) — put the "
219
+ f"codebase-memory-mcp binary for {_platform_tag()} on PATH")
220
+ # Always the last mile: an installed+indexed tool does nothing until the agent knows about it.
221
+ out.append("Make your AI agent use it — codeintel install")
222
+ return out
223
+ except Exception:
224
+ return []
225
+
226
+
173
227
  def render_setup_text(report: dict) -> str:
174
228
  """Human CLI view: a ``[n/N]`` step list, the doctor table, then the overall summary."""
175
229
  from codeintel.term import c # imported at call time so the CLI's term.configure() is honored
@@ -184,14 +238,24 @@ def render_setup_text(report: dict) -> str:
184
238
  tail = f" {c.dim(str(detail))}" if detail else ""
185
239
  name = c.bold(str(step.get("name", "")))
186
240
  lines.append(f" [{i}/{n}] {c.glyph(step.get('status', 'na'))} {name}{tail}")
187
- if not steps:
188
- lines.append(c.dim(" (nothing to do no opt-in flags set)"))
241
+ _ACTION_STEPS = {"install uv", "install deps (-e .)", "index: semantic", "index: graph", "warm lsp"}
242
+ if not any(s.get("name") in _ACTION_STEPS for s in steps):
243
+ lines.append(c.dim(" (diagnose only — run `codeintel setup --all` to install + index "
244
+ "everything automatically)"))
189
245
  lines.append("")
190
246
  try:
191
247
  from codeintel.doctor import render_doctor_text
192
248
  lines.append(render_doctor_text(report.get("doctor") or {}))
193
249
  except Exception:
194
250
  lines.append(c.dim("(doctor report unavailable)"))
251
+
252
+ nexts = _next_steps(report.get("doctor") or {}, root)
253
+ if nexts:
254
+ lines.append("")
255
+ lines.append(c.bold("Next:"))
256
+ for step in nexts:
257
+ lines.append(" " + c.cyan("→") + " " + step)
258
+
195
259
  lines.append("")
196
260
  lines.append(c.green("setup finished") if report.get("ok") else c.red("setup did not complete cleanly"))
197
261
  return "\n".join(lines)