codecortex 0.14.2__tar.gz → 0.15.2__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.

Potentially problematic release.


This version of codecortex might be problematic. Click here for more details.

Files changed (100) hide show
  1. {codecortex-0.14.2/src/codecortex.egg-info → codecortex-0.15.2}/PKG-INFO +119 -10
  2. {codecortex-0.14.2 → codecortex-0.15.2}/README.md +117 -8
  3. {codecortex-0.14.2 → codecortex-0.15.2}/pyproject.toml +5 -1
  4. {codecortex-0.14.2 → codecortex-0.15.2/src/codecortex.egg-info}/PKG-INFO +119 -10
  5. {codecortex-0.14.2 → codecortex-0.15.2}/src/codecortex.egg-info/SOURCES.txt +3 -0
  6. codecortex-0.15.2/src/codeintel/__init__.py +1 -0
  7. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/__main__.py +6 -0
  8. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/query.py +40 -11
  9. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/status.py +24 -4
  10. codecortex-0.15.2/src/codeintel/containment.py +80 -0
  11. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/doctor.py +7 -1
  12. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/gateway.py +28 -4
  13. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/grapher.py +26 -6
  14. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/http_server.py +10 -2
  15. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/indexer.py +137 -30
  16. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/mapper.py +27 -2
  17. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/onboarding.py +13 -3
  18. codecortex-0.15.2/src/codeintel/providers/graph.py +1264 -0
  19. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/providers/semantic.py +29 -8
  20. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/searcher.py +32 -6
  21. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/semantic_db.py +91 -2
  22. codecortex-0.15.2/src/codeintel/source_kind.py +191 -0
  23. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_chunking.py +62 -0
  24. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_cli_commands.py +46 -1
  25. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_doctor.py +78 -0
  26. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_gateway.py +91 -0
  27. codecortex-0.15.2/tests/test_graph_provider.py +801 -0
  28. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_graph_real.py +8 -2
  29. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_graph_stdin.py +9 -2
  30. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_grapher.py +37 -2
  31. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_hardening.py +2 -2
  32. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_http_auth.py +7 -1
  33. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_http_server.py +10 -4
  34. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_mapper.py +5 -2
  35. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_rbac.py +170 -3
  36. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_reindexer.py +92 -0
  37. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_rerank.py +18 -0
  38. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_semantic_provider.py +16 -0
  39. codecortex-0.15.2/tests/test_source_kind.py +228 -0
  40. codecortex-0.14.2/src/codeintel/__init__.py +0 -1
  41. codecortex-0.14.2/src/codeintel/providers/graph.py +0 -717
  42. codecortex-0.14.2/tests/test_graph_provider.py +0 -332
  43. {codecortex-0.14.2 → codecortex-0.15.2}/LICENSE +0 -0
  44. {codecortex-0.14.2 → codecortex-0.15.2}/setup.cfg +0 -0
  45. {codecortex-0.14.2 → codecortex-0.15.2}/src/codecortex.egg-info/dependency_links.txt +0 -0
  46. {codecortex-0.14.2 → codecortex-0.15.2}/src/codecortex.egg-info/entry_points.txt +0 -0
  47. {codecortex-0.14.2 → codecortex-0.15.2}/src/codecortex.egg-info/requires.txt +0 -0
  48. {codecortex-0.14.2 → codecortex-0.15.2}/src/codecortex.egg-info/top_level.txt +0 -0
  49. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/auth.py +0 -0
  50. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/cache.py +0 -0
  51. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/__init__.py +0 -0
  52. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/_common.py +0 -0
  53. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/doctor.py +0 -0
  54. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/gen_token.py +0 -0
  55. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/graph.py +0 -0
  56. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/index.py +0 -0
  57. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/install.py +0 -0
  58. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/map.py +0 -0
  59. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/reset.py +0 -0
  60. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/serve.py +0 -0
  61. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/serve_http.py +0 -0
  62. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/commands/setup.py +0 -0
  63. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/config.py +0 -0
  64. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/injector.py +0 -0
  65. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/installer.py +0 -0
  66. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/logconfig.py +0 -0
  67. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/metrics.py +0 -0
  68. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/policy.py +0 -0
  69. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/provider.py +0 -0
  70. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/providers/__init__.py +0 -0
  71. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/providers/lsp.py +0 -0
  72. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/providers/none.py +0 -0
  73. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/py.typed +0 -0
  74. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/reindexer.py +0 -0
  75. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/reset.py +0 -0
  76. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/server.py +0 -0
  77. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/term.py +0 -0
  78. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/verify.py +0 -0
  79. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/viewer/__init__.py +0 -0
  80. {codecortex-0.14.2 → codecortex-0.15.2}/src/codeintel/viewer/graph_template.html +0 -0
  81. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_cache.py +0 -0
  82. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_cli_help.py +0 -0
  83. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_config.py +0 -0
  84. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_e2e.py +0 -0
  85. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_engine_adoption.py +0 -0
  86. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_enterprise.py +0 -0
  87. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_installer.py +0 -0
  88. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_integration.py +0 -0
  89. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_lsp_provider.py +0 -0
  90. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_lsp_real.py +0 -0
  91. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_mcp_handshake.py +0 -0
  92. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_mcp_server.py +0 -0
  93. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_model_dimension.py +0 -0
  94. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_never_raise.py +0 -0
  95. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_onboarding.py +0 -0
  96. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_reset.py +0 -0
  97. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_term.py +0 -0
  98. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_treesitter.py +0 -0
  99. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_verify.py +0 -0
  100. {codecortex-0.14.2 → codecortex-0.15.2}/tests/test_verify_call.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.14.2
3
+ Version: 0.15.2
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
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/hamilton-sky/codeintel
9
9
  Project-URL: Issues, https://github.com/hamilton-sky/codeintel/issues
10
10
  Project-URL: Changelog, https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md
11
11
  Keywords: mcp,model-context-protocol,code-intelligence,code-search,llm,agents,lsp,semantic-search,knowledge-graph,static-analysis,developer-tools
12
- Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Operating System :: OS Independent
@@ -40,6 +40,16 @@ Dynamic: license-file
40
40
  **One MCP tool that lets a coding agent search, trace, and *understand* a codebase — structurally, not by grepping.** codeintel unifies three engines — a call/import **graph**, an **LSP** for exact symbols, and **semantic** embedding search — behind a single `code.query` call that routes to the right engine, caches the answer, and **never throws**. The agent always gets back a clean, well-formed result to reason over.
41
41
 
42
42
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/codecortex.svg)](https://pypi.org/project/codecortex/)
44
+ [![Python](https://img.shields.io/pypi/pyversions/codecortex.svg)](https://pypi.org/project/codecortex/)
45
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
46
+
47
+ > **Status: beta (`0.x`), and young.** The `code.query` surface is one call and has been stable
48
+ > since `0.8`, the suite is thorough, and every release is gated by a canary that runs a real query
49
+ > against a built wheel. But this is a new project with a single maintainer, and each time it has
50
+ > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
51
+ > machine, for a single user** — that is the case it is built and tested for. Before relying on it
52
+ > for anything beyond that, read **[Project status](#project-status)**.
43
53
 
44
54
  ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
45
55
 
@@ -85,10 +95,24 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
85
95
  | Everything about one symbol | `context` | graph + lsp | both views merged |
86
96
  | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
87
97
  | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
88
- | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
98
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers, **verified against the source** — [treat as candidates, not instructions](#deadcode-is-a-candidate-list-not-a-delete-list) |
89
99
 
90
100
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
91
101
 
102
+ #### `deadcode` is a candidate list, not a delete list
103
+
104
+ `deadcode` is the one op whose output invites a destructive action, so it gets an explicit caveat.
105
+ Every hit is re-read and verified against the source before it is reported, which removes the
106
+ common false positives — but **no reachability analysis sees every caller.** Dynamic dispatch,
107
+ registries and decorators, `getattr` lookups, entry points declared in packaging metadata, plugin
108
+ discovery, reflection, and calls from languages the graph does not parse are all invisible to it.
109
+ Through `0.14.x` it was systematically wrong on callback-heavy code and confident about it; that
110
+ class of defect is fixed, but the underlying limit is structural and permanent.
111
+
112
+ **So: review each hit before deleting anything, and never wire `deadcode` into an agent that
113
+ deletes without a human in the loop.** Used as a ranked list of *places worth looking*, it is
114
+ genuinely useful. Used as a work order, it will eventually remove live code.
115
+
92
116
  **Example — "who uses `safe_null_result`?"**
93
117
 
94
118
  ```jsonc
@@ -262,13 +286,25 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
262
286
  {"ok": true, "op": "search", "target": "auth", "result": null, "engine": "semantic", "cached": false}
263
287
  ```
264
288
 
265
- `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. An optional `reason` key explains null results (e.g. `"engine-unavailable"`, `"no-result"`). Callers must check `result is not None` before using the value.
289
+ `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. Callers must check `result is not None` before using the value.
290
+
291
+ The optional keys are the ones worth reading when an answer surprises you:
292
+
293
+ | Key | Meaning |
294
+ |---|---|
295
+ | `reason` | Why `result` is null — `engine-unavailable`, `no-result`, `not-in-graph` (the symbol isn't in the index — usually a stale index), `project-not-indexed`, `unsupported-op`, `root-not-allowed-for-role` (RBAC) |
296
+ | `hint` | The specific command that resolves this `reason`, when there is one |
297
+ | `engine` | Which engine actually answered — not necessarily the one you asked for, under `auto` |
298
+ | `cached` | Whether it came from the content-hash cache |
299
+ | `reindexing` | Present and `true` when a reindex was running, i.e. the answer reflects the last *completed* index. See [Keeping answers fresh](#keeping-answers-fresh) |
300
+
301
+ `codeintel query --json` prints this envelope from the CLI.
266
302
 
267
303
  ## Engines
268
304
 
269
305
  | Engine | Key ops | Install prereq |
270
306
  |---|---|---|
271
- | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` CLI on PATH — see [docs/graph.md](docs/graph.md) |
307
+ | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` **0.9.x** on PATH (`pip install 'codebase-memory-mcp==0.9.*'`) 0.10.x changed its response format and returns nothing for every op but resolution; see [docs/graph.md](docs/graph.md) |
272
308
  | `lsp` | `symbol`, `overview`, `context` | `uvx` on PATH — serena is fetched from GitHub on first use; see [docs/lsp.md](docs/lsp.md) |
273
309
  | `semantic` | `search`, `context` | `fastembed` + `sqlite-vec` (installed with the package) — see [docs/semantic.md](docs/semantic.md) |
274
310
 
@@ -325,7 +361,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
325
361
  | `codeintel index [project_root]` | Index a project for semantic search |
326
362
  | `codeintel serve` | Start the MCP server (stdio transport) |
327
363
  | `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) |
328
- | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
364
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
329
365
  | `codeintel status [project_root]` | Show engine availability and index age |
330
366
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
331
367
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
@@ -335,6 +371,14 @@ Full system docs live in [`docs/`](docs/) — start with the index:
335
371
 
336
372
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
337
373
 
374
+ **Exit codes**, so a `make` target or CI step can gate on `$?`:
375
+
376
+ | | |
377
+ |---|---|
378
+ | `0` | The command did its job. For `query` this includes an empty result — "nothing found" is an answer, not a failure. |
379
+ | `1` | The command could not do its job: a file it exists to write wasn't written (`map`, `graph`), an index didn't happen (`index`), a project root doesn't exist, or an engine is unhealthy (`doctor`, `setup`). |
380
+ | `2` | Bad usage — an unknown command or a missing required flag. |
381
+
338
382
  ## Config
339
383
 
340
384
  Create `.codeintel.toml` at your project root to override defaults:
@@ -362,6 +406,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
362
406
  | `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
363
407
  | `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
364
408
  | `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
409
+ | `CODEINTEL_HOME` | Where the index cache lives (default `~/.codeintel`). Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
365
410
 
366
411
  ## Privacy & dependencies
367
412
 
@@ -439,6 +484,63 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
439
484
  -e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
440
485
  ```
441
486
 
487
+ ## Project status
488
+
489
+ An honest picture, so you can decide what to trust this with.
490
+
491
+ **What's solid.** The suite is large and real — more test code than source, a coverage floor
492
+ enforced in CI, and fault-injection tests behind the never-raise contract. CI runs lint, `mypy`,
493
+ and the full suite on Python 3.11/3.12/3.13, then builds the wheel, installs it into a clean
494
+ environment, and runs a **release canary** that registers the build with Codex and Claude Code in a
495
+ throwaway `HOME`, boots the server those configs name, and asserts on the answer text of a real
496
+ `code.query`. A release that installs into a file no host reads, or that boots and answers nothing,
497
+ fails before it ships. The `code.query` envelope has been stable since `0.8`.
498
+
499
+ **What's young.** The project is pre-1.0 and moves fast. The honest signal is in the
500
+ [CHANGELOG](CHANGELOG.md): `0.15.0` and `0.15.1` were written almost entirely from pointing the
501
+ tool at four repositories it had never seen, and several of those defects had survived multiple
502
+ adversarial review rounds. **The rate at which new codebases surface new bugs has not yet flattened.**
503
+ Expect to be the first person to hit something, and please report it — see below.
504
+
505
+ **Use it for.** Local, single-user code intelligence on a developer machine. That is the designed
506
+ case, it is the tested case, and the blast radius of a wrong answer is a wasted tool call: the
507
+ safe-null contract means a failing engine degrades to `null` with a reason, so your agent falls
508
+ back to grep rather than crashing.
509
+
510
+ **Be careful with.**
511
+
512
+ | Area | Why |
513
+ |---|---|
514
+ | `deadcode` | It suggests deletions and cannot see every caller — [read the caveat](#deadcode-is-a-candidate-list-not-a-delete-list). |
515
+ | 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). |
516
+ | 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). |
517
+ | Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
518
+
519
+ **On the test numbers.** The suite is large and the coverage floor is enforced, but read the figure
520
+ with its caveat: the graph and LSP backends are external binaries that are **not installed in CI**,
521
+ so those two engines are exercised against hand-authored mocks rather than the real wire contract,
522
+ and the release canary — which does assert on real answer text against a built wheel — currently
523
+ covers the semantic engine only. Line coverage measures how much of the intended behavior runs, not
524
+ how much of reality it has met.
525
+
526
+ **The honest one-paragraph version.** codeintel has been run on very few repositories its author did
527
+ not write, and that is where its bugs have come from — every fix in `0.15.x` came from pointing it
528
+ at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
529
+ wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
530
+ wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
531
+ answer, treat `deadcode` as candidates for review, and if something looks off please
532
+ [report it](#reporting-a-problem) — an issue from someone who is not the author is the single most
533
+ useful thing this project can receive right now.
534
+
535
+ **Engine coverage depends on external binaries.** Semantic search works out of the box. The graph
536
+ engine needs `codebase-memory-mcp` and the LSP engine needs `uvx` on `PATH` — without them those
537
+ engines safe-null and you get a fraction of the capability table above. `codeintel setup --all`
538
+ installs what it can and `codeintel doctor` tells you exactly what is missing and how to fix it.
539
+ Run `doctor` first if the tool seems quieter than the docs suggest.
540
+
541
+ **Maintenance.** One maintainer, MIT licensed, issues and PRs welcome. There is no support
542
+ guarantee — factor that into anything load-bearing.
543
+
442
544
  ## Reporting a problem
443
545
 
444
546
  `codeintel doctor --json` prints a complete, machine-readable picture of what's installed, what's
@@ -452,9 +554,16 @@ codeintel doctor --json
452
554
  It reports only local engine and index state. Over the HTTP transport the `registrations` field —
453
555
  which names agent config files on the machine running the server — is deliberately omitted.
454
556
 
455
- If a *result* looks wrong rather than a command failing, include the exact `code.query` call and
456
- its full envelope. `reason`, `hint`, `engine`, `cached`, and `reindexing` between them explain
457
- which engine answered and how current its index was, which is usually the whole diagnosis.
557
+ If a *result* looks wrong rather than a command failing, send the envelope rather than the
558
+ rendered text:
559
+
560
+ ```bash
561
+ codeintel query --op callers --target yourSymbol --json
562
+ ```
563
+
564
+ `engine` says which engine answered, `cached` whether it came from the cache, `reindexing` whether
565
+ the index was mid-rebuild, and `reason`/`hint` why an empty answer was empty. Those five fields are
566
+ usually the whole diagnosis.
458
567
 
459
568
  ## Development
460
569
 
@@ -463,7 +572,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
463
572
  cd codeintel
464
573
  pip install -e .[dev]
465
574
 
466
- pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
575
+ pytest tests/ -q # ~620 tests, ~30s; fails under 83% coverage
467
576
  ruff check src tests # lint
468
577
  mypy # types (src/ only)
469
578
  ```
@@ -3,6 +3,16 @@
3
3
  **One MCP tool that lets a coding agent search, trace, and *understand* a codebase — structurally, not by grepping.** codeintel unifies three engines — a call/import **graph**, an **LSP** for exact symbols, and **semantic** embedding search — behind a single `code.query` call that routes to the right engine, caches the answer, and **never throws**. The agent always gets back a clean, well-formed result to reason over.
4
4
 
5
5
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/codecortex.svg)](https://pypi.org/project/codecortex/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/codecortex.svg)](https://pypi.org/project/codecortex/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ > **Status: beta (`0.x`), and young.** The `code.query` surface is one call and has been stable
11
+ > since `0.8`, the suite is thorough, and every release is gated by a canary that runs a real query
12
+ > against a built wheel. But this is a new project with a single maintainer, and each time it has
13
+ > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
14
+ > machine, for a single user** — that is the case it is built and tested for. Before relying on it
15
+ > for anything beyond that, read **[Project status](#project-status)**.
6
16
 
7
17
  ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
8
18
 
@@ -48,10 +58,24 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
48
58
  | Everything about one symbol | `context` | graph + lsp | both views merged |
49
59
  | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
50
60
  | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
51
- | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
61
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers, **verified against the source** — [treat as candidates, not instructions](#deadcode-is-a-candidate-list-not-a-delete-list) |
52
62
 
53
63
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
54
64
 
65
+ #### `deadcode` is a candidate list, not a delete list
66
+
67
+ `deadcode` is the one op whose output invites a destructive action, so it gets an explicit caveat.
68
+ Every hit is re-read and verified against the source before it is reported, which removes the
69
+ common false positives — but **no reachability analysis sees every caller.** Dynamic dispatch,
70
+ registries and decorators, `getattr` lookups, entry points declared in packaging metadata, plugin
71
+ discovery, reflection, and calls from languages the graph does not parse are all invisible to it.
72
+ Through `0.14.x` it was systematically wrong on callback-heavy code and confident about it; that
73
+ class of defect is fixed, but the underlying limit is structural and permanent.
74
+
75
+ **So: review each hit before deleting anything, and never wire `deadcode` into an agent that
76
+ deletes without a human in the loop.** Used as a ranked list of *places worth looking*, it is
77
+ genuinely useful. Used as a work order, it will eventually remove live code.
78
+
55
79
  **Example — "who uses `safe_null_result`?"**
56
80
 
57
81
  ```jsonc
@@ -225,13 +249,25 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
225
249
  {"ok": true, "op": "search", "target": "auth", "result": null, "engine": "semantic", "cached": false}
226
250
  ```
227
251
 
228
- `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. An optional `reason` key explains null results (e.g. `"engine-unavailable"`, `"no-result"`). Callers must check `result is not None` before using the value.
252
+ `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. Callers must check `result is not None` before using the value.
253
+
254
+ The optional keys are the ones worth reading when an answer surprises you:
255
+
256
+ | Key | Meaning |
257
+ |---|---|
258
+ | `reason` | Why `result` is null — `engine-unavailable`, `no-result`, `not-in-graph` (the symbol isn't in the index — usually a stale index), `project-not-indexed`, `unsupported-op`, `root-not-allowed-for-role` (RBAC) |
259
+ | `hint` | The specific command that resolves this `reason`, when there is one |
260
+ | `engine` | Which engine actually answered — not necessarily the one you asked for, under `auto` |
261
+ | `cached` | Whether it came from the content-hash cache |
262
+ | `reindexing` | Present and `true` when a reindex was running, i.e. the answer reflects the last *completed* index. See [Keeping answers fresh](#keeping-answers-fresh) |
263
+
264
+ `codeintel query --json` prints this envelope from the CLI.
229
265
 
230
266
  ## Engines
231
267
 
232
268
  | Engine | Key ops | Install prereq |
233
269
  |---|---|---|
234
- | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` CLI on PATH — see [docs/graph.md](docs/graph.md) |
270
+ | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` **0.9.x** on PATH (`pip install 'codebase-memory-mcp==0.9.*'`) 0.10.x changed its response format and returns nothing for every op but resolution; see [docs/graph.md](docs/graph.md) |
235
271
  | `lsp` | `symbol`, `overview`, `context` | `uvx` on PATH — serena is fetched from GitHub on first use; see [docs/lsp.md](docs/lsp.md) |
236
272
  | `semantic` | `search`, `context` | `fastembed` + `sqlite-vec` (installed with the package) — see [docs/semantic.md](docs/semantic.md) |
237
273
 
@@ -288,7 +324,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
288
324
  | `codeintel index [project_root]` | Index a project for semantic search |
289
325
  | `codeintel serve` | Start the MCP server (stdio transport) |
290
326
  | `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) |
291
- | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
327
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
292
328
  | `codeintel status [project_root]` | Show engine availability and index age |
293
329
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
294
330
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
@@ -298,6 +334,14 @@ Full system docs live in [`docs/`](docs/) — start with the index:
298
334
 
299
335
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
300
336
 
337
+ **Exit codes**, so a `make` target or CI step can gate on `$?`:
338
+
339
+ | | |
340
+ |---|---|
341
+ | `0` | The command did its job. For `query` this includes an empty result — "nothing found" is an answer, not a failure. |
342
+ | `1` | The command could not do its job: a file it exists to write wasn't written (`map`, `graph`), an index didn't happen (`index`), a project root doesn't exist, or an engine is unhealthy (`doctor`, `setup`). |
343
+ | `2` | Bad usage — an unknown command or a missing required flag. |
344
+
301
345
  ## Config
302
346
 
303
347
  Create `.codeintel.toml` at your project root to override defaults:
@@ -325,6 +369,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
325
369
  | `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
326
370
  | `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
327
371
  | `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
372
+ | `CODEINTEL_HOME` | Where the index cache lives (default `~/.codeintel`). Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
328
373
 
329
374
  ## Privacy & dependencies
330
375
 
@@ -402,6 +447,63 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
402
447
  -e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
403
448
  ```
404
449
 
450
+ ## Project status
451
+
452
+ An honest picture, so you can decide what to trust this with.
453
+
454
+ **What's solid.** The suite is large and real — more test code than source, a coverage floor
455
+ enforced in CI, and fault-injection tests behind the never-raise contract. CI runs lint, `mypy`,
456
+ and the full suite on Python 3.11/3.12/3.13, then builds the wheel, installs it into a clean
457
+ environment, and runs a **release canary** that registers the build with Codex and Claude Code in a
458
+ throwaway `HOME`, boots the server those configs name, and asserts on the answer text of a real
459
+ `code.query`. A release that installs into a file no host reads, or that boots and answers nothing,
460
+ fails before it ships. The `code.query` envelope has been stable since `0.8`.
461
+
462
+ **What's young.** The project is pre-1.0 and moves fast. The honest signal is in the
463
+ [CHANGELOG](CHANGELOG.md): `0.15.0` and `0.15.1` were written almost entirely from pointing the
464
+ tool at four repositories it had never seen, and several of those defects had survived multiple
465
+ adversarial review rounds. **The rate at which new codebases surface new bugs has not yet flattened.**
466
+ Expect to be the first person to hit something, and please report it — see below.
467
+
468
+ **Use it for.** Local, single-user code intelligence on a developer machine. That is the designed
469
+ case, it is the tested case, and the blast radius of a wrong answer is a wasted tool call: the
470
+ safe-null contract means a failing engine degrades to `null` with a reason, so your agent falls
471
+ back to grep rather than crashing.
472
+
473
+ **Be careful with.**
474
+
475
+ | Area | Why |
476
+ |---|---|
477
+ | `deadcode` | It suggests deletions and cannot see every caller — [read the caveat](#deadcode-is-a-candidate-list-not-a-delete-list). |
478
+ | 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). |
479
+ | 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). |
480
+ | Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
481
+
482
+ **On the test numbers.** The suite is large and the coverage floor is enforced, but read the figure
483
+ with its caveat: the graph and LSP backends are external binaries that are **not installed in CI**,
484
+ so those two engines are exercised against hand-authored mocks rather than the real wire contract,
485
+ and the release canary — which does assert on real answer text against a built wheel — currently
486
+ covers the semantic engine only. Line coverage measures how much of the intended behavior runs, not
487
+ how much of reality it has met.
488
+
489
+ **The honest one-paragraph version.** codeintel has been run on very few repositories its author did
490
+ not write, and that is where its bugs have come from — every fix in `0.15.x` came from pointing it
491
+ at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
492
+ wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
493
+ wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
494
+ answer, treat `deadcode` as candidates for review, and if something looks off please
495
+ [report it](#reporting-a-problem) — an issue from someone who is not the author is the single most
496
+ useful thing this project can receive right now.
497
+
498
+ **Engine coverage depends on external binaries.** Semantic search works out of the box. The graph
499
+ engine needs `codebase-memory-mcp` and the LSP engine needs `uvx` on `PATH` — without them those
500
+ engines safe-null and you get a fraction of the capability table above. `codeintel setup --all`
501
+ installs what it can and `codeintel doctor` tells you exactly what is missing and how to fix it.
502
+ Run `doctor` first if the tool seems quieter than the docs suggest.
503
+
504
+ **Maintenance.** One maintainer, MIT licensed, issues and PRs welcome. There is no support
505
+ guarantee — factor that into anything load-bearing.
506
+
405
507
  ## Reporting a problem
406
508
 
407
509
  `codeintel doctor --json` prints a complete, machine-readable picture of what's installed, what's
@@ -415,9 +517,16 @@ codeintel doctor --json
415
517
  It reports only local engine and index state. Over the HTTP transport the `registrations` field —
416
518
  which names agent config files on the machine running the server — is deliberately omitted.
417
519
 
418
- If a *result* looks wrong rather than a command failing, include the exact `code.query` call and
419
- its full envelope. `reason`, `hint`, `engine`, `cached`, and `reindexing` between them explain
420
- which engine answered and how current its index was, which is usually the whole diagnosis.
520
+ If a *result* looks wrong rather than a command failing, send the envelope rather than the
521
+ rendered text:
522
+
523
+ ```bash
524
+ codeintel query --op callers --target yourSymbol --json
525
+ ```
526
+
527
+ `engine` says which engine answered, `cached` whether it came from the cache, `reindexing` whether
528
+ the index was mid-rebuild, and `reason`/`hint` why an empty answer was empty. Those five fields are
529
+ usually the whole diagnosis.
421
530
 
422
531
  ## Development
423
532
 
@@ -426,7 +535,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
426
535
  cd codeintel
427
536
  pip install -e .[dev]
428
537
 
429
- pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
538
+ pytest tests/ -q # ~620 tests, ~30s; fails under 83% coverage
430
539
  ruff check src tests # lint
431
540
  mypy # types (src/ only)
432
541
  ```
@@ -19,7 +19,11 @@ keywords = [
19
19
  "lsp", "semantic-search", "knowledge-graph", "static-analysis", "developer-tools",
20
20
  ]
21
21
  classifiers = [
22
- "Development Status :: 5 - Production/Stable",
22
+ # Beta, deliberately. The package is well-tested and the `code.query` surface has been stable
23
+ # since 0.8, but the project is young, pre-1.0, and still finding real defects each time it
24
+ # meets an unfamiliar repository. "Production/Stable" is a claim to make after that curve
25
+ # flattens and external users have exercised it — not before.
26
+ "Development Status :: 4 - Beta",
23
27
  "Intended Audience :: Developers",
24
28
  "Environment :: Console",
25
29
  "Operating System :: OS Independent",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.14.2
3
+ Version: 0.15.2
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
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/hamilton-sky/codeintel
9
9
  Project-URL: Issues, https://github.com/hamilton-sky/codeintel/issues
10
10
  Project-URL: Changelog, https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md
11
11
  Keywords: mcp,model-context-protocol,code-intelligence,code-search,llm,agents,lsp,semantic-search,knowledge-graph,static-analysis,developer-tools
12
- Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Operating System :: OS Independent
@@ -40,6 +40,16 @@ Dynamic: license-file
40
40
  **One MCP tool that lets a coding agent search, trace, and *understand* a codebase — structurally, not by grepping.** codeintel unifies three engines — a call/import **graph**, an **LSP** for exact symbols, and **semantic** embedding search — behind a single `code.query` call that routes to the right engine, caches the answer, and **never throws**. The agent always gets back a clean, well-formed result to reason over.
41
41
 
42
42
  [![CI](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml/badge.svg)](https://github.com/hamilton-sky/codeintel/actions/workflows/ci.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/codecortex.svg)](https://pypi.org/project/codecortex/)
44
+ [![Python](https://img.shields.io/pypi/pyversions/codecortex.svg)](https://pypi.org/project/codecortex/)
45
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
46
+
47
+ > **Status: beta (`0.x`), and young.** The `code.query` surface is one call and has been stable
48
+ > since `0.8`, the suite is thorough, and every release is gated by a canary that runs a real query
49
+ > against a built wheel. But this is a new project with a single maintainer, and each time it has
50
+ > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
51
+ > machine, for a single user** — that is the case it is built and tested for. Before relying on it
52
+ > for anything beyond that, read **[Project status](#project-status)**.
43
53
 
44
54
  ![codeintel's own call graph — an interactive, self-contained HTML view with force / radial / layered / module layouts, complexity-sized nodes, and click-to-inspect metrics.](docs/images/graph-codeintel.png)
45
55
 
@@ -85,10 +95,24 @@ It's one call: `code.query(op, target, engine="auto")`. In `auto` mode (the defa
85
95
  | Everything about one symbol | `context` | graph + lsp | both views merged |
86
96
  | **Impact of your uncommitted edits** | `changed` | graph | changed files → impacted symbols |
87
97
  | Refactor-risk hotspots | `hotspots` | graph | highest complexity / fan-in symbols |
88
- | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers |
98
+ | Unreferenced (dead) code | `deadcode` | graph | non-test symbols with no callers, **verified against the source** — [treat as candidates, not instructions](#deadcode-is-a-candidate-list-not-a-delete-list) |
89
99
 
90
100
  Pin one engine with `--engine graph│lsp│semantic`, or fan out with `--engine both` / `all` to merge results.
91
101
 
102
+ #### `deadcode` is a candidate list, not a delete list
103
+
104
+ `deadcode` is the one op whose output invites a destructive action, so it gets an explicit caveat.
105
+ Every hit is re-read and verified against the source before it is reported, which removes the
106
+ common false positives — but **no reachability analysis sees every caller.** Dynamic dispatch,
107
+ registries and decorators, `getattr` lookups, entry points declared in packaging metadata, plugin
108
+ discovery, reflection, and calls from languages the graph does not parse are all invisible to it.
109
+ Through `0.14.x` it was systematically wrong on callback-heavy code and confident about it; that
110
+ class of defect is fixed, but the underlying limit is structural and permanent.
111
+
112
+ **So: review each hit before deleting anything, and never wire `deadcode` into an agent that
113
+ deletes without a human in the loop.** Used as a ranked list of *places worth looking*, it is
114
+ genuinely useful. Used as a work order, it will eventually remove live code.
115
+
92
116
  **Example — "who uses `safe_null_result`?"**
93
117
 
94
118
  ```jsonc
@@ -262,13 +286,25 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
262
286
  {"ok": true, "op": "search", "target": "auth", "result": null, "engine": "semantic", "cached": false}
263
287
  ```
264
288
 
265
- `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. An optional `reason` key explains null results (e.g. `"engine-unavailable"`, `"no-result"`). Callers must check `result is not None` before using the value.
289
+ `ok` is always `true`. `result` is `null` when no provider has an answer — never an exception, never a 500. Callers must check `result is not None` before using the value.
290
+
291
+ The optional keys are the ones worth reading when an answer surprises you:
292
+
293
+ | Key | Meaning |
294
+ |---|---|
295
+ | `reason` | Why `result` is null — `engine-unavailable`, `no-result`, `not-in-graph` (the symbol isn't in the index — usually a stale index), `project-not-indexed`, `unsupported-op`, `root-not-allowed-for-role` (RBAC) |
296
+ | `hint` | The specific command that resolves this `reason`, when there is one |
297
+ | `engine` | Which engine actually answered — not necessarily the one you asked for, under `auto` |
298
+ | `cached` | Whether it came from the content-hash cache |
299
+ | `reindexing` | Present and `true` when a reindex was running, i.e. the answer reflects the last *completed* index. See [Keeping answers fresh](#keeping-answers-fresh) |
300
+
301
+ `codeintel query --json` prints this envelope from the CLI.
266
302
 
267
303
  ## Engines
268
304
 
269
305
  | Engine | Key ops | Install prereq |
270
306
  |---|---|---|
271
- | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` CLI on PATH — see [docs/graph.md](docs/graph.md) |
307
+ | `graph` | `impact`, `callers`, `callees`, `chain`, `pattern`, `overview`, `context` | `codebase-memory-mcp` **0.9.x** on PATH (`pip install 'codebase-memory-mcp==0.9.*'`) 0.10.x changed its response format and returns nothing for every op but resolution; see [docs/graph.md](docs/graph.md) |
272
308
  | `lsp` | `symbol`, `overview`, `context` | `uvx` on PATH — serena is fetched from GitHub on first use; see [docs/lsp.md](docs/lsp.md) |
273
309
  | `semantic` | `search`, `context` | `fastembed` + `sqlite-vec` (installed with the package) — see [docs/semantic.md](docs/semantic.md) |
274
310
 
@@ -325,7 +361,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
325
361
  | `codeintel index [project_root]` | Index a project for semantic search |
326
362
  | `codeintel serve` | Start the MCP server (stdio transport) |
327
363
  | `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) |
328
- | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
364
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
329
365
  | `codeintel status [project_root]` | Show engine availability and index age |
330
366
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
331
367
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
@@ -335,6 +371,14 @@ Full system docs live in [`docs/`](docs/) — start with the index:
335
371
 
336
372
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
337
373
 
374
+ **Exit codes**, so a `make` target or CI step can gate on `$?`:
375
+
376
+ | | |
377
+ |---|---|
378
+ | `0` | The command did its job. For `query` this includes an empty result — "nothing found" is an answer, not a failure. |
379
+ | `1` | The command could not do its job: a file it exists to write wasn't written (`map`, `graph`), an index didn't happen (`index`), a project root doesn't exist, or an engine is unhealthy (`doctor`, `setup`). |
380
+ | `2` | Bad usage — an unknown command or a missing required flag. |
381
+
338
382
  ## Config
339
383
 
340
384
  Create `.codeintel.toml` at your project root to override defaults:
@@ -362,6 +406,7 @@ Config is **validated on load** — an out-of-range number, a misspelled enum, o
362
406
  | `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
363
407
  | `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
364
408
  | `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
409
+ | `CODEINTEL_HOME` | Where the index cache lives (default `~/.codeintel`). Set this when the process has **no resolvable home directory** — a container running as a UID with no passwd entry and no `$HOME`, which is common when an agent runs in one. Without it, `Path.home()` raises and every command fails somewhere far from the cause. |
365
410
 
366
411
  ## Privacy & dependencies
367
412
 
@@ -439,6 +484,63 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
439
484
  -e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
440
485
  ```
441
486
 
487
+ ## Project status
488
+
489
+ An honest picture, so you can decide what to trust this with.
490
+
491
+ **What's solid.** The suite is large and real — more test code than source, a coverage floor
492
+ enforced in CI, and fault-injection tests behind the never-raise contract. CI runs lint, `mypy`,
493
+ and the full suite on Python 3.11/3.12/3.13, then builds the wheel, installs it into a clean
494
+ environment, and runs a **release canary** that registers the build with Codex and Claude Code in a
495
+ throwaway `HOME`, boots the server those configs name, and asserts on the answer text of a real
496
+ `code.query`. A release that installs into a file no host reads, or that boots and answers nothing,
497
+ fails before it ships. The `code.query` envelope has been stable since `0.8`.
498
+
499
+ **What's young.** The project is pre-1.0 and moves fast. The honest signal is in the
500
+ [CHANGELOG](CHANGELOG.md): `0.15.0` and `0.15.1` were written almost entirely from pointing the
501
+ tool at four repositories it had never seen, and several of those defects had survived multiple
502
+ adversarial review rounds. **The rate at which new codebases surface new bugs has not yet flattened.**
503
+ Expect to be the first person to hit something, and please report it — see below.
504
+
505
+ **Use it for.** Local, single-user code intelligence on a developer machine. That is the designed
506
+ case, it is the tested case, and the blast radius of a wrong answer is a wasted tool call: the
507
+ safe-null contract means a failing engine degrades to `null` with a reason, so your agent falls
508
+ back to grep rather than crashing.
509
+
510
+ **Be careful with.**
511
+
512
+ | Area | Why |
513
+ |---|---|
514
+ | `deadcode` | It suggests deletions and cannot see every caller — [read the caveat](#deadcode-is-a-candidate-list-not-a-delete-list). |
515
+ | 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). |
516
+ | 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). |
517
+ | Unattended automation | Anything that acts on a result without a human reading it deserves a pilot first. |
518
+
519
+ **On the test numbers.** The suite is large and the coverage floor is enforced, but read the figure
520
+ with its caveat: the graph and LSP backends are external binaries that are **not installed in CI**,
521
+ so those two engines are exercised against hand-authored mocks rather than the real wire contract,
522
+ and the release canary — which does assert on real answer text against a built wheel — currently
523
+ covers the semantic engine only. Line coverage measures how much of the intended behavior runs, not
524
+ how much of reality it has met.
525
+
526
+ **The honest one-paragraph version.** codeintel has been run on very few repositories its author did
527
+ not write, and that is where its bugs have come from — every fix in `0.15.x` came from pointing it
528
+ at an unfamiliar codebase. Its characteristic failure mode is **answering confidently from the
529
+ wrong index rather than failing loudly**, which the never-raise contract makes harder to notice: a
530
+ wrong answer and a right one are the same shape. Run `codeintel doctor` before trusting a repo-wide
531
+ answer, treat `deadcode` as candidates for review, and if something looks off please
532
+ [report it](#reporting-a-problem) — an issue from someone who is not the author is the single most
533
+ useful thing this project can receive right now.
534
+
535
+ **Engine coverage depends on external binaries.** Semantic search works out of the box. The graph
536
+ engine needs `codebase-memory-mcp` and the LSP engine needs `uvx` on `PATH` — without them those
537
+ engines safe-null and you get a fraction of the capability table above. `codeintel setup --all`
538
+ installs what it can and `codeintel doctor` tells you exactly what is missing and how to fix it.
539
+ Run `doctor` first if the tool seems quieter than the docs suggest.
540
+
541
+ **Maintenance.** One maintainer, MIT licensed, issues and PRs welcome. There is no support
542
+ guarantee — factor that into anything load-bearing.
543
+
442
544
  ## Reporting a problem
443
545
 
444
546
  `codeintel doctor --json` prints a complete, machine-readable picture of what's installed, what's
@@ -452,9 +554,16 @@ codeintel doctor --json
452
554
  It reports only local engine and index state. Over the HTTP transport the `registrations` field —
453
555
  which names agent config files on the machine running the server — is deliberately omitted.
454
556
 
455
- If a *result* looks wrong rather than a command failing, include the exact `code.query` call and
456
- its full envelope. `reason`, `hint`, `engine`, `cached`, and `reindexing` between them explain
457
- which engine answered and how current its index was, which is usually the whole diagnosis.
557
+ If a *result* looks wrong rather than a command failing, send the envelope rather than the
558
+ rendered text:
559
+
560
+ ```bash
561
+ codeintel query --op callers --target yourSymbol --json
562
+ ```
563
+
564
+ `engine` says which engine answered, `cached` whether it came from the cache, `reindexing` whether
565
+ the index was mid-rebuild, and `reason`/`hint` why an empty answer was empty. Those five fields are
566
+ usually the whole diagnosis.
458
567
 
459
568
  ## Development
460
569
 
@@ -463,7 +572,7 @@ git clone https://github.com/hamilton-sky/codeintel.git
463
572
  cd codeintel
464
573
  pip install -e .[dev]
465
574
 
466
- pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
575
+ pytest tests/ -q # ~620 tests, ~30s; fails under 83% coverage
467
576
  ruff check src tests # lint
468
577
  mypy # types (src/ only)
469
578
  ```