codecortex 0.15.5__tar.gz → 0.16.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.
- {codecortex-0.15.5/src/codecortex.egg-info → codecortex-0.16.0}/PKG-INFO +64 -32
- {codecortex-0.15.5 → codecortex-0.16.0}/README.md +63 -31
- {codecortex-0.15.5 → codecortex-0.16.0/src/codecortex.egg-info}/PKG-INFO +64 -32
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codecortex.egg-info/SOURCES.txt +7 -0
- codecortex-0.16.0/src/codeintel/__init__.py +1 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/__main__.py +14 -0
- codecortex-0.16.0/src/codeintel/agent_prompt.py +107 -0
- codecortex-0.16.0/src/codeintel/commands/prompt.py +33 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/doctor.py +4 -0
- codecortex-0.16.0/src/codeintel/graph_backend.py +281 -0
- codecortex-0.16.0/src/codeintel/graph_render.py +95 -0
- codecortex-0.16.0/src/codeintel/graph_resolution.py +274 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/onboarding.py +2 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/providers/graph.py +696 -683
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/server.py +3 -0
- codecortex-0.16.0/tests/test_agent_prompt.py +86 -0
- codecortex-0.16.0/tests/test_backend_reaper.py +57 -0
- codecortex-0.16.0/tests/test_corpus.py +890 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_docs_deadcode_withdrawal.py +52 -3
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_doctor.py +2 -0
- codecortex-0.16.0/tests/test_graph_failure_population.py +850 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_graph_provider.py +112 -156
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_graph_real.py +14 -37
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_hardening.py +5 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_incompleteness.py +21 -6
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_loc_census.py +3 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_onboarding.py +1 -0
- codecortex-0.16.0/tests/test_reset.py +671 -0
- codecortex-0.15.5/src/codeintel/__init__.py +0 -1
- codecortex-0.15.5/tests/test_corpus.py +0 -335
- codecortex-0.15.5/tests/test_graph_failure_population.py +0 -256
- codecortex-0.15.5/tests/test_reset.py +0 -181
- {codecortex-0.15.5 → codecortex-0.16.0}/LICENSE +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/pyproject.toml +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/setup.cfg +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codecortex.egg-info/entry_points.txt +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codecortex.egg-info/requires.txt +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codecortex.egg-info/top_level.txt +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/auth.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/cache.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/__init__.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/_common.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/doctor.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/gen_token.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/graph.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/index.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/install.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/map.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/query.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/reset.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/serve.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/serve_http.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/setup.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/commands/status.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/config.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/containment.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/gateway.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/grapher.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/http_server.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/indexer.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/injector.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/installer.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/loc.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/logconfig.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/mapper.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/metrics.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/outcome.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/policy.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/provider.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/providers/__init__.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/providers/lsp.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/providers/none.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/providers/semantic.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/py.typed +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/redact.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/reindexer.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/reset.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/searcher.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/semantic_db.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/source_kind.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/term.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/verify.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/viewer/__init__.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/src/codeintel/viewer/graph_template.html +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_cache.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_chunking.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_cli_commands.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_cli_help.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_cold_process.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_config.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_docs_ci_claims.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_e2e.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_engine_adoption.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_enterprise.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_gateway.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_graph_stdin.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_grapher.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_http_auth.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_http_server.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_installer.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_integration.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_lsp_provider.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_lsp_real.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_mapper.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_mcp_handshake.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_mcp_server.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_model_dimension.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_never_raise.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_rbac.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_reindexer.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_rerank.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_semantic_provider.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_source_kind.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_term.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_treesitter.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_verify.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_verify_call.py +0 -0
- {codecortex-0.15.5 → codecortex-0.16.0}/tests/test_version_skew.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.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
|
|
@@ -95,45 +95,61 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
|
|
|
95
95
|
| Everything about one symbol | `context` | graph + lsp | both views merged |
|
|
96
96
|
| **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
|
|
97
97
|
| Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
|
|
98
|
-
| Unreferenced (dead) code | `deadcode` | graph | **withdrawn** —
|
|
98
|
+
| 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) |
|
|
99
99
|
|
|
100
100
|
Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
`callers`, `callees` and `impact` resolve the target by its **unqualified name**. When several
|
|
103
|
+
symbols share it, each matched symbol's rows are reported separately under its own heading and the
|
|
104
|
+
result says how many it found — narrow to one with a qualified target (`core.Group.invoke`) or a file
|
|
105
|
+
hint (`invoke@src/click/testing.py`); see
|
|
106
|
+
[when several symbols share a name](docs/graph.md#when-several-symbols-share-a-name).
|
|
103
107
|
|
|
104
|
-
`deadcode` is
|
|
105
|
-
was measured wrong in **both** directions on real repositories — on one it named five candidates of
|
|
106
|
-
which four were live code (a rollup plugin hook; two entries of a `Record<string, fn>` reached by a
|
|
107
|
-
runtime string; a `predicate` passed inline to the call that consumes it), and on another it
|
|
108
|
-
reported "(none found)" for a 4,883-function codebase that had at least three genuinely unreferenced
|
|
109
|
-
private helpers. It is the one op whose output is an instruction to delete code, so it needs the
|
|
110
|
-
highest evidence bar of any op here, and currently has the least. It returns when a labelled corpus
|
|
111
|
-
measures its precision and recall — not before.
|
|
108
|
+
#### `deadcode` is retired
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
`deadcode` no longer exists. Asking for it returns a safe-null (`reason: "op-withdrawn"`) with a hint
|
|
111
|
+
naming what to use instead, and **no flag brings it back** — the implementation has been deleted.
|
|
115
112
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
**review every hit before deleting anything, and never wire it into an agent that deletes without a
|
|
120
|
-
human in the loop.**
|
|
113
|
+
It was withdrawn pending one condition: *"it returns when a labelled corpus measures its precision
|
|
114
|
+
and recall — not before."* That corpus now exists, in
|
|
115
|
+
[`tests/test_corpus.py`](tests/test_corpus.py), and the measurement is what retired it.
|
|
121
116
|
|
|
122
|
-
**
|
|
117
|
+
**How it was measured.** Two pinned real Python repositories (`pallets/click`, `psf/requests`), with
|
|
118
|
+
every function and method collected from the **AST** — 2,425 definitions, `async def` and class
|
|
119
|
+
methods included, because a verification whose population comes from a pattern like `^\s*def ` cannot
|
|
120
|
+
see half of them. Each is labelled live or dead with the reference behind the label recorded beside
|
|
121
|
+
it. The oracle errs toward *live*: a decorator, a dunder, an override of an external interface, a
|
|
122
|
+
string-dispatch mention, or public-API status is each enough to call a symbol live, so "dead" is only
|
|
123
|
+
what survives all of them. That biases the numbers against the op, which is the correct direction for
|
|
124
|
+
a check whose output is an instruction to delete code. Known-answer canaries are planted in both trees
|
|
125
|
+
so recall has a denominator at all.
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
// request
|
|
126
|
-
{ "op": "callers", "target": "safe_null_result", "engine": "auto" }
|
|
127
|
+
**The numbers.**
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
| | precision | recall |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| as shipped | **25%** (6 of 24) | 60% (6 of 10) |
|
|
132
|
+
| with the two repairs this codebase already contains elsewhere | 89% (8 of 9) | 80% (8 of 10) |
|
|
133
|
+
|
|
134
|
+
And the measurement that decided it — **real code only, canaries removed**: the op as shipped named
|
|
135
|
+
**18 candidates across those two repositories, and every one of them was live.** All 18 were Makefile
|
|
136
|
+
targets, which the graph backend indexes as `Function` nodes. Repaired, it names exactly one, and
|
|
137
|
+
that one is `MockRequest.get_type` in requests — a method `http.cookiejar` calls by duck-typed
|
|
138
|
+
convention, whose name appears once in the source.
|
|
139
|
+
|
|
140
|
+
**Why it was not repaired further.** The verification was a name-frequency scan over the source, so it
|
|
141
|
+
fails on exactly one condition: a symbol whose name appears once and is called by a convention
|
|
142
|
+
outside the source. Two repositories produced three distinct instances of that condition — non-code
|
|
143
|
+
nodes labelled `Function`, interpreter-called dunders, and stdlib duck-typed protocol methods — and
|
|
144
|
+
the earlier TypeScript evidence adds a rollup plugin hook and object-literal properties. The set is
|
|
145
|
+
not enumerable: no specification lists `get_type`. Every repository added revealed a new member of it.
|
|
146
|
+
|
|
147
|
+
Weighed against that: in 2,425 real definitions across two maintained repositories there was **not
|
|
148
|
+
one** dead private symbol to find. An op whose measured yield on real code is zero true positives has
|
|
149
|
+
no benefit to set against that error rate.
|
|
135
150
|
|
|
136
|
-
|
|
151
|
+
**Use `callers` on a specific symbol instead.** "Does anything call this?" is exactly the question
|
|
152
|
+
`deadcode` was trying to answer in bulk, and `callers` answers it accurately, one symbol at a time.
|
|
137
153
|
|
|
138
154
|
## What makes it good
|
|
139
155
|
|
|
@@ -181,6 +197,21 @@ codeintel install # registers with the agents you actually have insta
|
|
|
181
197
|
codeintel query --op search --target "authentication middleware"
|
|
182
198
|
```
|
|
183
199
|
|
|
200
|
+
### Or: have your agent set it up
|
|
201
|
+
|
|
202
|
+
Prefer to let your coding agent run the steps? Generate a paste-ready prompt, tailored to this
|
|
203
|
+
machine and agent:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
codeintel prompt # this repo — probes health, emits only the steps still outstanding
|
|
207
|
+
codeintel prompt --fresh | pbcopy # the full sequence from `pip install`, to send a friend
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It runs a `doctor` probe and prints a block you copy into Claude Code / Codex / Gemini / Zed: the
|
|
211
|
+
exact remaining commands (or "just restart me" when everything is already healthy and registered),
|
|
212
|
+
a `doctor --deep` verification, and the reminder to restart the agent so the MCP tools load. The
|
|
213
|
+
prompt goes to stdout (so `| pbcopy` grabs exactly it); the "paste this" note goes to stderr.
|
|
214
|
+
|
|
184
215
|
### Enable native Codex integration
|
|
185
216
|
|
|
186
217
|
`codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
|
|
@@ -364,6 +395,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
364
395
|
| `codeintel help` | Every command grouped by task, with descriptions and examples (also the bare `codeintel`). A mistyped command suggests what you meant. |
|
|
365
396
|
| `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 |
|
|
366
397
|
| `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 |
|
|
398
|
+
| `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 |
|
|
367
399
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
368
400
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
369
401
|
| `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) |
|
|
@@ -517,7 +549,7 @@ back to grep rather than crashing.
|
|
|
517
549
|
|
|
518
550
|
| Area | Why |
|
|
519
551
|
|---|---|
|
|
520
|
-
| `deadcode` | Withdrawn
|
|
552
|
+
| `deadcode` | Withdrawn, then **retired** (`reason: "op-withdrawn"`) — a labelled corpus measured 25% precision. Use `callers` on a specific symbol instead — [the measurement](#deadcode-is-retired). |
|
|
521
553
|
| Non-loopback serving | `serve-http` is stdlib `http.server`. It binds loopback by default for a reason; front it with a reverse proxy and see [docs/deploy.md](docs/deploy.md). |
|
|
522
554
|
| RBAC between **untrusting** tenants | It separates privilege levels among callers you already trust. It is not a wall against an adversary with write access to their own root — see the warning in [docs/deploy.md](docs/deploy.md). |
|
|
523
555
|
| Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
|
|
@@ -541,7 +573,7 @@ not write, and that is where its bugs have come from — every fix in `0.15.x` c
|
|
|
541
573
|
at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
|
|
542
574
|
wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
|
|
543
575
|
wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
|
|
544
|
-
answer — and `deadcode` in particular is
|
|
576
|
+
answer — and `deadcode` in particular is retired rather than merely caveated (see above) — and if
|
|
545
577
|
something looks off please [report it](#reporting-a-problem) — an issue from someone who is not the
|
|
546
578
|
author is the single most useful thing this project can receive right now.
|
|
547
579
|
|
|
@@ -58,45 +58,61 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
|
|
|
58
58
|
| Everything about one symbol | `context` | graph + lsp | both views merged |
|
|
59
59
|
| **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
|
|
60
60
|
| Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
|
|
61
|
-
| Unreferenced (dead) code | `deadcode` | graph | **withdrawn** —
|
|
61
|
+
| 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) |
|
|
62
62
|
|
|
63
63
|
Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
`callers`, `callees` and `impact` resolve the target by its **unqualified name**. When several
|
|
66
|
+
symbols share it, each matched symbol's rows are reported separately under its own heading and the
|
|
67
|
+
result says how many it found — narrow to one with a qualified target (`core.Group.invoke`) or a file
|
|
68
|
+
hint (`invoke@src/click/testing.py`); see
|
|
69
|
+
[when several symbols share a name](docs/graph.md#when-several-symbols-share-a-name).
|
|
66
70
|
|
|
67
|
-
`deadcode` is
|
|
68
|
-
was measured wrong in **both** directions on real repositories — on one it named five candidates of
|
|
69
|
-
which four were live code (a rollup plugin hook; two entries of a `Record<string, fn>` reached by a
|
|
70
|
-
runtime string; a `predicate` passed inline to the call that consumes it), and on another it
|
|
71
|
-
reported "(none found)" for a 4,883-function codebase that had at least three genuinely unreferenced
|
|
72
|
-
private helpers. It is the one op whose output is an instruction to delete code, so it needs the
|
|
73
|
-
highest evidence bar of any op here, and currently has the least. It returns when a labelled corpus
|
|
74
|
-
measures its precision and recall — not before.
|
|
71
|
+
#### `deadcode` is retired
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
`deadcode` no longer exists. Asking for it returns a safe-null (`reason: "op-withdrawn"`) with a hint
|
|
74
|
+
naming what to use instead, and **no flag brings it back** — the implementation has been deleted.
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
**review every hit before deleting anything, and never wire it into an agent that deletes without a
|
|
83
|
-
human in the loop.**
|
|
76
|
+
It was withdrawn pending one condition: *"it returns when a labelled corpus measures its precision
|
|
77
|
+
and recall — not before."* That corpus now exists, in
|
|
78
|
+
[`tests/test_corpus.py`](tests/test_corpus.py), and the measurement is what retired it.
|
|
84
79
|
|
|
85
|
-
**
|
|
80
|
+
**How it was measured.** Two pinned real Python repositories (`pallets/click`, `psf/requests`), with
|
|
81
|
+
every function and method collected from the **AST** — 2,425 definitions, `async def` and class
|
|
82
|
+
methods included, because a verification whose population comes from a pattern like `^\s*def ` cannot
|
|
83
|
+
see half of them. Each is labelled live or dead with the reference behind the label recorded beside
|
|
84
|
+
it. The oracle errs toward *live*: a decorator, a dunder, an override of an external interface, a
|
|
85
|
+
string-dispatch mention, or public-API status is each enough to call a symbol live, so "dead" is only
|
|
86
|
+
what survives all of them. That biases the numbers against the op, which is the correct direction for
|
|
87
|
+
a check whose output is an instruction to delete code. Known-answer canaries are planted in both trees
|
|
88
|
+
so recall has a denominator at all.
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
// request
|
|
89
|
-
{ "op": "callers", "target": "safe_null_result", "engine": "auto" }
|
|
90
|
+
**The numbers.**
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
| | precision | recall |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| as shipped | **25%** (6 of 24) | 60% (6 of 10) |
|
|
95
|
+
| with the two repairs this codebase already contains elsewhere | 89% (8 of 9) | 80% (8 of 10) |
|
|
96
|
+
|
|
97
|
+
And the measurement that decided it — **real code only, canaries removed**: the op as shipped named
|
|
98
|
+
**18 candidates across those two repositories, and every one of them was live.** All 18 were Makefile
|
|
99
|
+
targets, which the graph backend indexes as `Function` nodes. Repaired, it names exactly one, and
|
|
100
|
+
that one is `MockRequest.get_type` in requests — a method `http.cookiejar` calls by duck-typed
|
|
101
|
+
convention, whose name appears once in the source.
|
|
102
|
+
|
|
103
|
+
**Why it was not repaired further.** The verification was a name-frequency scan over the source, so it
|
|
104
|
+
fails on exactly one condition: a symbol whose name appears once and is called by a convention
|
|
105
|
+
outside the source. Two repositories produced three distinct instances of that condition — non-code
|
|
106
|
+
nodes labelled `Function`, interpreter-called dunders, and stdlib duck-typed protocol methods — and
|
|
107
|
+
the earlier TypeScript evidence adds a rollup plugin hook and object-literal properties. The set is
|
|
108
|
+
not enumerable: no specification lists `get_type`. Every repository added revealed a new member of it.
|
|
109
|
+
|
|
110
|
+
Weighed against that: in 2,425 real definitions across two maintained repositories there was **not
|
|
111
|
+
one** dead private symbol to find. An op whose measured yield on real code is zero true positives has
|
|
112
|
+
no benefit to set against that error rate.
|
|
98
113
|
|
|
99
|
-
|
|
114
|
+
**Use `callers` on a specific symbol instead.** "Does anything call this?" is exactly the question
|
|
115
|
+
`deadcode` was trying to answer in bulk, and `callers` answers it accurately, one symbol at a time.
|
|
100
116
|
|
|
101
117
|
## What makes it good
|
|
102
118
|
|
|
@@ -144,6 +160,21 @@ codeintel install # registers with the agents you actually have insta
|
|
|
144
160
|
codeintel query --op search --target "authentication middleware"
|
|
145
161
|
```
|
|
146
162
|
|
|
163
|
+
### Or: have your agent set it up
|
|
164
|
+
|
|
165
|
+
Prefer to let your coding agent run the steps? Generate a paste-ready prompt, tailored to this
|
|
166
|
+
machine and agent:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
codeintel prompt # this repo — probes health, emits only the steps still outstanding
|
|
170
|
+
codeintel prompt --fresh | pbcopy # the full sequence from `pip install`, to send a friend
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
It runs a `doctor` probe and prints a block you copy into Claude Code / Codex / Gemini / Zed: the
|
|
174
|
+
exact remaining commands (or "just restart me" when everything is already healthy and registered),
|
|
175
|
+
a `doctor --deep` verification, and the reminder to restart the agent so the MCP tools load. The
|
|
176
|
+
prompt goes to stdout (so `| pbcopy` grabs exactly it); the "paste this" note goes to stderr.
|
|
177
|
+
|
|
147
178
|
### Enable native Codex integration
|
|
148
179
|
|
|
149
180
|
`codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
|
|
@@ -327,6 +358,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
327
358
|
| `codeintel help` | Every command grouped by task, with descriptions and examples (also the bare `codeintel`). A mistyped command suggests what you meant. |
|
|
328
359
|
| `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 |
|
|
329
360
|
| `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 |
|
|
361
|
+
| `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 |
|
|
330
362
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
331
363
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
332
364
|
| `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) |
|
|
@@ -480,7 +512,7 @@ back to grep rather than crashing.
|
|
|
480
512
|
|
|
481
513
|
| Area | Why |
|
|
482
514
|
|---|---|
|
|
483
|
-
| `deadcode` | Withdrawn
|
|
515
|
+
| `deadcode` | Withdrawn, then **retired** (`reason: "op-withdrawn"`) — a labelled corpus measured 25% precision. Use `callers` on a specific symbol instead — [the measurement](#deadcode-is-retired). |
|
|
484
516
|
| Non-loopback serving | `serve-http` is stdlib `http.server`. It binds loopback by default for a reason; front it with a reverse proxy and see [docs/deploy.md](docs/deploy.md). |
|
|
485
517
|
| RBAC between **untrusting** tenants | It separates privilege levels among callers you already trust. It is not a wall against an adversary with write access to their own root — see the warning in [docs/deploy.md](docs/deploy.md). |
|
|
486
518
|
| Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
|
|
@@ -504,7 +536,7 @@ not write, and that is where its bugs have come from — every fix in `0.15.x` c
|
|
|
504
536
|
at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
|
|
505
537
|
wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
|
|
506
538
|
wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
|
|
507
|
-
answer — and `deadcode` in particular is
|
|
539
|
+
answer — and `deadcode` in particular is retired rather than merely caveated (see above) — and if
|
|
508
540
|
something looks off please [report it](#reporting-a-problem) — an issue from someone who is not the
|
|
509
541
|
author is the single most useful thing this project can receive right now.
|
|
510
542
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.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
|
|
@@ -95,45 +95,61 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
|
|
|
95
95
|
| Everything about one symbol | `context` | graph + lsp | both views merged |
|
|
96
96
|
| **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
|
|
97
97
|
| Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
|
|
98
|
-
| Unreferenced (dead) code | `deadcode` | graph | **withdrawn** —
|
|
98
|
+
| 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) |
|
|
99
99
|
|
|
100
100
|
Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
`callers`, `callees` and `impact` resolve the target by its **unqualified name**. When several
|
|
103
|
+
symbols share it, each matched symbol's rows are reported separately under its own heading and the
|
|
104
|
+
result says how many it found — narrow to one with a qualified target (`core.Group.invoke`) or a file
|
|
105
|
+
hint (`invoke@src/click/testing.py`); see
|
|
106
|
+
[when several symbols share a name](docs/graph.md#when-several-symbols-share-a-name).
|
|
103
107
|
|
|
104
|
-
`deadcode` is
|
|
105
|
-
was measured wrong in **both** directions on real repositories — on one it named five candidates of
|
|
106
|
-
which four were live code (a rollup plugin hook; two entries of a `Record<string, fn>` reached by a
|
|
107
|
-
runtime string; a `predicate` passed inline to the call that consumes it), and on another it
|
|
108
|
-
reported "(none found)" for a 4,883-function codebase that had at least three genuinely unreferenced
|
|
109
|
-
private helpers. It is the one op whose output is an instruction to delete code, so it needs the
|
|
110
|
-
highest evidence bar of any op here, and currently has the least. It returns when a labelled corpus
|
|
111
|
-
measures its precision and recall — not before.
|
|
108
|
+
#### `deadcode` is retired
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
`deadcode` no longer exists. Asking for it returns a safe-null (`reason: "op-withdrawn"`) with a hint
|
|
111
|
+
naming what to use instead, and **no flag brings it back** — the implementation has been deleted.
|
|
115
112
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
**review every hit before deleting anything, and never wire it into an agent that deletes without a
|
|
120
|
-
human in the loop.**
|
|
113
|
+
It was withdrawn pending one condition: *"it returns when a labelled corpus measures its precision
|
|
114
|
+
and recall — not before."* That corpus now exists, in
|
|
115
|
+
[`tests/test_corpus.py`](tests/test_corpus.py), and the measurement is what retired it.
|
|
121
116
|
|
|
122
|
-
**
|
|
117
|
+
**How it was measured.** Two pinned real Python repositories (`pallets/click`, `psf/requests`), with
|
|
118
|
+
every function and method collected from the **AST** — 2,425 definitions, `async def` and class
|
|
119
|
+
methods included, because a verification whose population comes from a pattern like `^\s*def ` cannot
|
|
120
|
+
see half of them. Each is labelled live or dead with the reference behind the label recorded beside
|
|
121
|
+
it. The oracle errs toward *live*: a decorator, a dunder, an override of an external interface, a
|
|
122
|
+
string-dispatch mention, or public-API status is each enough to call a symbol live, so "dead" is only
|
|
123
|
+
what survives all of them. That biases the numbers against the op, which is the correct direction for
|
|
124
|
+
a check whose output is an instruction to delete code. Known-answer canaries are planted in both trees
|
|
125
|
+
so recall has a denominator at all.
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
// request
|
|
126
|
-
{ "op": "callers", "target": "safe_null_result", "engine": "auto" }
|
|
127
|
+
**The numbers.**
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
| | precision | recall |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| as shipped | **25%** (6 of 24) | 60% (6 of 10) |
|
|
132
|
+
| with the two repairs this codebase already contains elsewhere | 89% (8 of 9) | 80% (8 of 10) |
|
|
133
|
+
|
|
134
|
+
And the measurement that decided it — **real code only, canaries removed**: the op as shipped named
|
|
135
|
+
**18 candidates across those two repositories, and every one of them was live.** All 18 were Makefile
|
|
136
|
+
targets, which the graph backend indexes as `Function` nodes. Repaired, it names exactly one, and
|
|
137
|
+
that one is `MockRequest.get_type` in requests — a method `http.cookiejar` calls by duck-typed
|
|
138
|
+
convention, whose name appears once in the source.
|
|
139
|
+
|
|
140
|
+
**Why it was not repaired further.** The verification was a name-frequency scan over the source, so it
|
|
141
|
+
fails on exactly one condition: a symbol whose name appears once and is called by a convention
|
|
142
|
+
outside the source. Two repositories produced three distinct instances of that condition — non-code
|
|
143
|
+
nodes labelled `Function`, interpreter-called dunders, and stdlib duck-typed protocol methods — and
|
|
144
|
+
the earlier TypeScript evidence adds a rollup plugin hook and object-literal properties. The set is
|
|
145
|
+
not enumerable: no specification lists `get_type`. Every repository added revealed a new member of it.
|
|
146
|
+
|
|
147
|
+
Weighed against that: in 2,425 real definitions across two maintained repositories there was **not
|
|
148
|
+
one** dead private symbol to find. An op whose measured yield on real code is zero true positives has
|
|
149
|
+
no benefit to set against that error rate.
|
|
135
150
|
|
|
136
|
-
|
|
151
|
+
**Use `callers` on a specific symbol instead.** "Does anything call this?" is exactly the question
|
|
152
|
+
`deadcode` was trying to answer in bulk, and `callers` answers it accurately, one symbol at a time.
|
|
137
153
|
|
|
138
154
|
## What makes it good
|
|
139
155
|
|
|
@@ -181,6 +197,21 @@ codeintel install # registers with the agents you actually have insta
|
|
|
181
197
|
codeintel query --op search --target "authentication middleware"
|
|
182
198
|
```
|
|
183
199
|
|
|
200
|
+
### Or: have your agent set it up
|
|
201
|
+
|
|
202
|
+
Prefer to let your coding agent run the steps? Generate a paste-ready prompt, tailored to this
|
|
203
|
+
machine and agent:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
codeintel prompt # this repo — probes health, emits only the steps still outstanding
|
|
207
|
+
codeintel prompt --fresh | pbcopy # the full sequence from `pip install`, to send a friend
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It runs a `doctor` probe and prints a block you copy into Claude Code / Codex / Gemini / Zed: the
|
|
211
|
+
exact remaining commands (or "just restart me" when everything is already healthy and registered),
|
|
212
|
+
a `doctor --deep` verification, and the reminder to restart the agent so the MCP tools load. The
|
|
213
|
+
prompt goes to stdout (so `| pbcopy` grabs exactly it); the "paste this" note goes to stderr.
|
|
214
|
+
|
|
184
215
|
### Enable native Codex integration
|
|
185
216
|
|
|
186
217
|
`codeintel` is an MCP server, so Codex can call its tools directly rather than invoking the CLI.
|
|
@@ -364,6 +395,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
364
395
|
| `codeintel help` | Every command grouped by task, with descriptions and examples (also the bare `codeintel`). A mistyped command suggests what you meant. |
|
|
365
396
|
| `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 |
|
|
366
397
|
| `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 |
|
|
398
|
+
| `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 |
|
|
367
399
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
368
400
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
369
401
|
| `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) |
|
|
@@ -517,7 +549,7 @@ back to grep rather than crashing.
|
|
|
517
549
|
|
|
518
550
|
| Area | Why |
|
|
519
551
|
|---|---|
|
|
520
|
-
| `deadcode` | Withdrawn
|
|
552
|
+
| `deadcode` | Withdrawn, then **retired** (`reason: "op-withdrawn"`) — a labelled corpus measured 25% precision. Use `callers` on a specific symbol instead — [the measurement](#deadcode-is-retired). |
|
|
521
553
|
| Non-loopback serving | `serve-http` is stdlib `http.server`. It binds loopback by default for a reason; front it with a reverse proxy and see [docs/deploy.md](docs/deploy.md). |
|
|
522
554
|
| RBAC between **untrusting** tenants | It separates privilege levels among callers you already trust. It is not a wall against an adversary with write access to their own root — see the warning in [docs/deploy.md](docs/deploy.md). |
|
|
523
555
|
| Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
|
|
@@ -541,7 +573,7 @@ not write, and that is where its bugs have come from — every fix in `0.15.x` c
|
|
|
541
573
|
at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
|
|
542
574
|
wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
|
|
543
575
|
wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
|
|
544
|
-
answer — and `deadcode` in particular is
|
|
576
|
+
answer — and `deadcode` in particular is retired rather than merely caveated (see above) — and if
|
|
545
577
|
something looks off please [report it](#reporting-a-problem) — an issue from someone who is not the
|
|
546
578
|
author is the single most useful thing this project can receive right now.
|
|
547
579
|
|
|
@@ -9,12 +9,16 @@ src/codecortex.egg-info/requires.txt
|
|
|
9
9
|
src/codecortex.egg-info/top_level.txt
|
|
10
10
|
src/codeintel/__init__.py
|
|
11
11
|
src/codeintel/__main__.py
|
|
12
|
+
src/codeintel/agent_prompt.py
|
|
12
13
|
src/codeintel/auth.py
|
|
13
14
|
src/codeintel/cache.py
|
|
14
15
|
src/codeintel/config.py
|
|
15
16
|
src/codeintel/containment.py
|
|
16
17
|
src/codeintel/doctor.py
|
|
17
18
|
src/codeintel/gateway.py
|
|
19
|
+
src/codeintel/graph_backend.py
|
|
20
|
+
src/codeintel/graph_render.py
|
|
21
|
+
src/codeintel/graph_resolution.py
|
|
18
22
|
src/codeintel/grapher.py
|
|
19
23
|
src/codeintel/http_server.py
|
|
20
24
|
src/codeintel/indexer.py
|
|
@@ -46,6 +50,7 @@ src/codeintel/commands/graph.py
|
|
|
46
50
|
src/codeintel/commands/index.py
|
|
47
51
|
src/codeintel/commands/install.py
|
|
48
52
|
src/codeintel/commands/map.py
|
|
53
|
+
src/codeintel/commands/prompt.py
|
|
49
54
|
src/codeintel/commands/query.py
|
|
50
55
|
src/codeintel/commands/reset.py
|
|
51
56
|
src/codeintel/commands/serve.py
|
|
@@ -59,6 +64,8 @@ src/codeintel/providers/none.py
|
|
|
59
64
|
src/codeintel/providers/semantic.py
|
|
60
65
|
src/codeintel/viewer/__init__.py
|
|
61
66
|
src/codeintel/viewer/graph_template.html
|
|
67
|
+
tests/test_agent_prompt.py
|
|
68
|
+
tests/test_backend_reaper.py
|
|
62
69
|
tests/test_cache.py
|
|
63
70
|
tests/test_chunking.py
|
|
64
71
|
tests/test_cli_commands.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.16.0"
|
|
@@ -18,6 +18,7 @@ _COMMAND_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
|
|
|
18
18
|
("setup", "Prepare backends + index this repo (--all does everything automatable)"),
|
|
19
19
|
("index", "Index a project for semantic search"),
|
|
20
20
|
("install", "Register codeintel with the AI agents installed on this machine"),
|
|
21
|
+
("prompt", "Print a paste-to-your-agent setup prompt, tailored to this machine"),
|
|
21
22
|
]),
|
|
22
23
|
("Check health", [
|
|
23
24
|
("doctor", "Per-engine health + index status, with the fix for each gap"),
|
|
@@ -44,6 +45,7 @@ _MODULES = {
|
|
|
44
45
|
"setup": "setup",
|
|
45
46
|
"index": "index",
|
|
46
47
|
"install": "install",
|
|
48
|
+
"prompt": "prompt",
|
|
47
49
|
"doctor": "doctor",
|
|
48
50
|
"status": "status",
|
|
49
51
|
"reset": "reset",
|
|
@@ -236,6 +238,18 @@ def main() -> None:
|
|
|
236
238
|
setup_parser.add_argument("--warm", action="store_true", help="Boot serena now (first run pulls it via uvx; slow)")
|
|
237
239
|
setup_parser.add_argument("--json", action="store_true", help="Emit the structured JSON report")
|
|
238
240
|
|
|
241
|
+
# prompt subcommand
|
|
242
|
+
prompt_parser = subparsers.add_parser("prompt", parents=[color_parent],
|
|
243
|
+
help="Print a paste-to-your-agent setup prompt for this repo")
|
|
244
|
+
prompt_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
|
|
245
|
+
prompt_parser.add_argument("--agent", default="auto",
|
|
246
|
+
help="Agent the prompt targets: claude|codex|gemini|zed|auto (default: auto)")
|
|
247
|
+
prompt_parser.add_argument("--fresh", action="store_true",
|
|
248
|
+
help="Emit the full sequence from `pip install`, ignoring local state "
|
|
249
|
+
"(a template to paste to a friend on a clean machine)")
|
|
250
|
+
prompt_parser.add_argument("--deep", action="store_true",
|
|
251
|
+
help="Boot-check serena while probing (slower; sharper 'already set up' result)")
|
|
252
|
+
|
|
239
253
|
# reset subcommand
|
|
240
254
|
reset_parser = subparsers.add_parser("reset", parents=[color_parent],
|
|
241
255
|
help="Clear the semantic index (recover from a corrupt/stale DB)")
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""`codeintel prompt` — generate a paste-to-your-agent setup prompt, tailored to this machine.
|
|
2
|
+
|
|
3
|
+
`setup` DOES the bring-up and `install` REGISTERS the MCP server; this hands the same job to your
|
|
4
|
+
coding agent instead. It runs a doctor probe, sees which engines are actually missing and whether
|
|
5
|
+
this agent is already registered, and prints a natural-language prompt you copy into Claude Code /
|
|
6
|
+
Codex / etc. — telling it the exact remaining commands for THIS repo, to verify with `doctor`, and to
|
|
7
|
+
have you restart it so the MCP tools load. A static prompt would just repeat the README; reflecting
|
|
8
|
+
the live health is what makes it worth a command.
|
|
9
|
+
|
|
10
|
+
Two modes: the default reflects this machine (it drops steps already satisfied, and when everything
|
|
11
|
+
is healthy and registered it reduces to "just restart me"); `--fresh` ignores local state and emits
|
|
12
|
+
the full sequence from `pip install`, for pasting to a friend on a clean machine.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from codeintel.installer import _AGENTS, detect_agents
|
|
19
|
+
|
|
20
|
+
# On PyPI the distribution is `codecortex` (the name `codeintel` was taken); the CLI and import stay
|
|
21
|
+
# `codeintel`. A pasted prompt has to name the installable, so this is the one place the CLI spells it.
|
|
22
|
+
_PYPI_PACKAGE = "codecortex"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def resolve_agent(agent: str) -> str:
|
|
26
|
+
"""Which agent the prompt targets. A named agent is honoured; `auto` picks the one installed on
|
|
27
|
+
this machine (the first, if several); `""` when none is detected → the prompt says 'your agent'
|
|
28
|
+
and omits the `--agent` flag rather than guessing a host you do not have."""
|
|
29
|
+
a = (agent or "auto").strip().lower()
|
|
30
|
+
if a in _AGENTS:
|
|
31
|
+
return a
|
|
32
|
+
detected = detect_agents()
|
|
33
|
+
return detected[0] if detected else ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_prompt(project_root: str, agent: str, report: dict[str, Any], *, fresh: bool = False) -> str:
|
|
37
|
+
"""The paste-ready prompt, tailored to what *report* (a doctor report) says is still missing.
|
|
38
|
+
|
|
39
|
+
Pure and side-effect-free so a test can assert the tailoring without a live backend. With
|
|
40
|
+
``fresh=True`` the local state is ignored and every step is emitted."""
|
|
41
|
+
engines = {} if fresh else (report.get("engines") or {})
|
|
42
|
+
|
|
43
|
+
def _ok(name: str) -> bool:
|
|
44
|
+
return (engines.get(name) or {}).get("status") == "ok"
|
|
45
|
+
|
|
46
|
+
graph_ok, lsp_ok, semantic_ok = _ok("graph"), _ok("lsp"), _ok("semantic")
|
|
47
|
+
healthy = (not fresh) and bool((report.get("summary") or {}).get("healthy"))
|
|
48
|
+
registered = (not fresh) and any(
|
|
49
|
+
r.get("agent") == agent and r.get("runnable")
|
|
50
|
+
for r in (report.get("registrations") or [])
|
|
51
|
+
)
|
|
52
|
+
install_flag = f" --agent {agent}" if agent else ""
|
|
53
|
+
tools = "code.query, code.doctor, code.map"
|
|
54
|
+
|
|
55
|
+
# Already fully set up: the one thing an agent cannot do for itself is get restarted.
|
|
56
|
+
if healthy and registered:
|
|
57
|
+
return (
|
|
58
|
+
f"codeintel is already installed and healthy for this project ({project_root}) and "
|
|
59
|
+
f"registered as one of your MCP servers — nothing to install. Confirm with "
|
|
60
|
+
f"`codeintel doctor --deep {project_root}` (expect \"ready 3/3, healthy\"), then tell me "
|
|
61
|
+
f"to restart you so your codeintel MCP tools ({tools}) load."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Emit only the steps that are actually outstanding.
|
|
65
|
+
steps: list[str] = []
|
|
66
|
+
pip_targets: list[str] = []
|
|
67
|
+
if not semantic_ok:
|
|
68
|
+
pip_targets.append(_PYPI_PACKAGE)
|
|
69
|
+
if not graph_ok:
|
|
70
|
+
pip_targets.append("'codebase-memory-mcp==0.9.*'")
|
|
71
|
+
if pip_targets:
|
|
72
|
+
adds = " and ".join(
|
|
73
|
+
part for part in (
|
|
74
|
+
"the codeintel CLI + semantic engine" if not semantic_ok else "",
|
|
75
|
+
"the graph engine" if not graph_ok else "",
|
|
76
|
+
) if part
|
|
77
|
+
)
|
|
78
|
+
steps.append(f"`pip install {' '.join(pip_targets)}` — installs {adds}")
|
|
79
|
+
# setup --all installs uv, indexes, and warms serena — needed unless all three engines are
|
|
80
|
+
# already ok (in which case the repo is indexed too, and only registration can be outstanding).
|
|
81
|
+
if not (graph_ok and lsp_ok and semantic_ok):
|
|
82
|
+
steps.append(
|
|
83
|
+
f"`codeintel setup --all {project_root}` — installs uv (the LSP engine), indexes this "
|
|
84
|
+
f"repo, and warms serena; idempotent, safe to re-run"
|
|
85
|
+
)
|
|
86
|
+
if not registered:
|
|
87
|
+
steps.append(f"`codeintel install{install_flag}` — registers codeintel as my MCP server")
|
|
88
|
+
|
|
89
|
+
numbered = "\n".join(f"{i}. {s}" for i, s in enumerate(steps, 1))
|
|
90
|
+
return (
|
|
91
|
+
f"Set up the codeintel code-intelligence tool for this project ({project_root}) and wire it "
|
|
92
|
+
f"into me. Run these in order, showing me the output of each:\n\n{numbered}\n\n"
|
|
93
|
+
f"Then run `codeintel doctor --deep {project_root}` and show me the result — I expect "
|
|
94
|
+
f"\"ready 3/3, healthy\". If any engine is not \"ok\", run the exact remediation `doctor` "
|
|
95
|
+
f"prints for it and re-check. When it is healthy, tell me to restart you so my codeintel MCP "
|
|
96
|
+
f"tools ({tools}) load."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def run_prompt(project_root: str, agent: str = "auto", *, fresh: bool = False, deep: bool = False) -> str:
|
|
101
|
+
"""Probe (unless ``fresh``) and render the prompt. Never raises here — the command wrapper also
|
|
102
|
+
guards, but keeping the probe optional means `--fresh` needs no backend at all."""
|
|
103
|
+
report: dict[str, Any] = {}
|
|
104
|
+
if not fresh:
|
|
105
|
+
from codeintel import doctor as _doctor
|
|
106
|
+
report = _doctor.run_doctor(project_root, deep=deep)
|
|
107
|
+
return build_prompt(project_root, resolve_agent(agent), report, fresh=fresh)
|