codecortex 0.23.4__tar.gz → 0.24.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. {codecortex-0.23.4/src/codecortex.egg-info → codecortex-0.24.1}/PKG-INFO +8 -3
  2. {codecortex-0.23.4 → codecortex-0.24.1}/README.md +6 -2
  3. {codecortex-0.23.4 → codecortex-0.24.1}/pyproject.toml +33 -1
  4. {codecortex-0.23.4 → codecortex-0.24.1/src/codecortex.egg-info}/PKG-INFO +8 -3
  5. {codecortex-0.23.4 → codecortex-0.24.1}/src/codecortex.egg-info/SOURCES.txt +19 -0
  6. {codecortex-0.23.4 → codecortex-0.24.1}/src/codecortex.egg-info/requires.txt +1 -0
  7. codecortex-0.24.1/src/codeintel/__init__.py +1 -0
  8. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/__main__.py +30 -2
  9. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/c4.py +2 -1
  10. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/index.py +36 -0
  11. codecortex-0.24.1/src/codeintel/commands/uninstall.py +106 -0
  12. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/doctor.py +5 -2
  13. codecortex-0.24.1/src/codeintel/filelock.py +160 -0
  14. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/gateway.py +108 -7
  15. codecortex-0.24.1/src/codeintel/graph_answer.py +970 -0
  16. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/graph_backend.py +5 -2
  17. codecortex-0.24.1/src/codeintel/graph_confidence.py +155 -0
  18. codecortex-0.24.1/src/codeintel/graph_edges.py +89 -0
  19. codecortex-0.24.1/src/codeintel/graph_ops.py +712 -0
  20. codecortex-0.24.1/src/codeintel/graph_render.py +295 -0
  21. codecortex-0.24.1/src/codeintel/graph_targets.py +138 -0
  22. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/grapher.py +2 -5
  23. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/installer.py +156 -0
  24. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/outcome.py +22 -0
  25. codecortex-0.24.1/src/codeintel/per_thread.py +64 -0
  26. codecortex-0.24.1/src/codeintel/provider.py +242 -0
  27. codecortex-0.24.1/src/codeintel/providers/graph.py +916 -0
  28. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/providers/lsp.py +288 -32
  29. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/providers/semantic.py +159 -3
  30. codecortex-0.24.1/src/codeintel/query_ops.py +32 -0
  31. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/reindexer.py +36 -11
  32. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/server.py +33 -11
  33. codecortex-0.24.1/tests/test_bench_corpus_typed.py +166 -0
  34. codecortex-0.24.1/tests/test_bench_gate.py +103 -0
  35. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_bench_oracle.py +24 -0
  36. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_bench_oracle_ts.py +125 -0
  37. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cli_commands.py +90 -0
  38. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_corpus.py +2 -3
  39. codecortex-0.24.1/tests/test_docs_trust_claims.py +194 -0
  40. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_doctor.py +242 -1
  41. codecortex-0.24.1/tests/test_edge_confidence.py +687 -0
  42. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_enterprise.py +5 -0
  43. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_gateway.py +125 -1
  44. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_graph_failure_population.py +36 -13
  45. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_graph_provider.py +3 -3
  46. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_grapher.py +2 -1
  47. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_hardening.py +92 -3
  48. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_http_auth.py +8 -0
  49. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_http_server.py +1 -0
  50. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_incompleteness.py +1 -1
  51. codecortex-0.24.1/tests/test_index_incompleteness.py +237 -0
  52. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_installer.py +8 -1
  53. codecortex-0.24.1/tests/test_lsp_language_coverage.py +231 -0
  54. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_lsp_provider.py +173 -0
  55. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_mapper.py +15 -14
  56. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_mcp_handshake.py +6 -5
  57. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_mcp_server.py +36 -0
  58. codecortex-0.24.1/tests/test_missing_target.py +270 -0
  59. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_null_reason_and_ripple.py +26 -0
  60. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_onboarding.py +3 -5
  61. codecortex-0.24.1/tests/test_per_thread.py +101 -0
  62. codecortex-0.24.1/tests/test_query_budget.py +220 -0
  63. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_rbac.py +22 -6
  64. codecortex-0.24.1/tests/test_reindex_lock.py +267 -0
  65. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_reindexer.py +7 -0
  66. codecortex-0.24.1/tests/test_summary_integrity.py +2078 -0
  67. codecortex-0.24.1/tests/test_uninstall.py +335 -0
  68. codecortex-0.24.1/tests/test_upgrade_path.py +220 -0
  69. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_verify.py +3 -3
  70. codecortex-0.23.4/src/codeintel/__init__.py +0 -1
  71. codecortex-0.23.4/src/codeintel/graph_render.py +0 -95
  72. codecortex-0.23.4/src/codeintel/provider.py +0 -151
  73. codecortex-0.23.4/src/codeintel/providers/graph.py +0 -2445
  74. codecortex-0.23.4/src/codeintel/query_ops.py +0 -17
  75. codecortex-0.23.4/tests/test_edge_confidence.py +0 -151
  76. codecortex-0.23.4/tests/test_lsp_language_coverage.py +0 -100
  77. {codecortex-0.23.4 → codecortex-0.24.1}/LICENSE +0 -0
  78. {codecortex-0.23.4 → codecortex-0.24.1}/setup.cfg +0 -0
  79. {codecortex-0.23.4 → codecortex-0.24.1}/src/codecortex.egg-info/dependency_links.txt +0 -0
  80. {codecortex-0.23.4 → codecortex-0.24.1}/src/codecortex.egg-info/entry_points.txt +0 -0
  81. {codecortex-0.23.4 → codecortex-0.24.1}/src/codecortex.egg-info/top_level.txt +0 -0
  82. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/agent_prompt.py +0 -0
  83. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/auth.py +0 -0
  84. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/c4_check.py +0 -0
  85. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/c4_layers.py +0 -0
  86. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/cache.py +0 -0
  87. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/__init__.py +0 -0
  88. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/_common.py +0 -0
  89. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/c4.py +0 -0
  90. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/doctor.py +0 -0
  91. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/gen_token.py +0 -0
  92. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/graph.py +0 -0
  93. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/install.py +0 -0
  94. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/map.py +0 -0
  95. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/prompt.py +0 -0
  96. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/query.py +0 -0
  97. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/reset.py +0 -0
  98. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/serve.py +0 -0
  99. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/serve_http.py +0 -0
  100. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/setup.py +0 -0
  101. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/commands/status.py +0 -0
  102. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/config.py +0 -0
  103. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/containment.py +0 -0
  104. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/graph_resolution.py +0 -0
  105. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/http_server.py +0 -0
  106. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/indexer.py +0 -0
  107. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/injector.py +0 -0
  108. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/lang_config.py +0 -0
  109. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/loc.py +0 -0
  110. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/logconfig.py +0 -0
  111. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/mapper.py +0 -0
  112. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/metrics.py +0 -0
  113. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/onboarding.py +0 -0
  114. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/paths.py +0 -0
  115. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/policy.py +0 -0
  116. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/progress.py +0 -0
  117. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/providers/__init__.py +0 -0
  118. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/providers/none.py +0 -0
  119. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/py.typed +0 -0
  120. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/redact.py +0 -0
  121. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/reset.py +0 -0
  122. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/searcher.py +0 -0
  123. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/semantic_db.py +0 -0
  124. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/source_kind.py +0 -0
  125. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/term.py +0 -0
  126. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/verify.py +0 -0
  127. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/viewer/__init__.py +0 -0
  128. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/viewer/graph_template.html +0 -0
  129. {codecortex-0.23.4 → codecortex-0.24.1}/src/codeintel/wire_text.py +0 -0
  130. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_agent_prompt.py +0 -0
  131. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_backend_reaper.py +0 -0
  132. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_background_index_failure.py +0 -0
  133. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_c4.py +0 -0
  134. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_c4_check.py +0 -0
  135. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_c4_layers.py +0 -0
  136. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_c4_model.py +0 -0
  137. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_c4_output.py +0 -0
  138. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cache.py +0 -0
  139. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_chunking.py +0 -0
  140. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cli_help.py +0 -0
  141. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cli_install_dry_run.py +0 -0
  142. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cli_install_offer_injection.py +0 -0
  143. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_cold_process.py +0 -0
  144. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_config.py +0 -0
  145. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_could_not_ask_semantic.py +0 -0
  146. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_docs_ci_claims.py +0 -0
  147. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_docs_deadcode_withdrawal.py +0 -0
  148. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_e2e.py +0 -0
  149. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_engine_adoption.py +0 -0
  150. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_graph_real.py +0 -0
  151. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_graph_stdin.py +0 -0
  152. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_hard_exit.py +0 -0
  153. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_index_failure_reason.py +0 -0
  154. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_index_progress.py +0 -0
  155. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_indexer_binary.py +0 -0
  156. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_integration.py +0 -0
  157. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_lang_config.py +0 -0
  158. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_loc_census.py +0 -0
  159. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_lsp_real.py +0 -0
  160. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_model_dimension.py +0 -0
  161. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_never_raise.py +0 -0
  162. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_redaction_boundary.py +0 -0
  163. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_rerank.py +0 -0
  164. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_reset.py +0 -0
  165. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_semantic_provider.py +0 -0
  166. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_source_kind.py +0 -0
  167. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_staleness.py +0 -0
  168. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_symbol_context.py +0 -0
  169. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_term.py +0 -0
  170. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_treesitter.py +0 -0
  171. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_verify_call.py +0 -0
  172. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_version_skew.py +0 -0
  173. {codecortex-0.23.4 → codecortex-0.24.1}/tests/test_wire_text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.23.4
3
+ Version: 0.24.1
4
4
  Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
5
  Author: Shammai Hamilton
6
6
  License-Expression: MIT
@@ -31,6 +31,7 @@ Requires-Dist: tree-sitter-language-pack>=1.0
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: pytest>=8; extra == "dev"
33
33
  Requires-Dist: pytest-cov>=5; extra == "dev"
34
+ Requires-Dist: pytest-timeout>=2.3; extra == "dev"
34
35
  Requires-Dist: numpy>=1.24; extra == "dev"
35
36
  Requires-Dist: ruff>=0.16; extra == "dev"
36
37
  Requires-Dist: mypy>=1.11; extra == "dev"
@@ -53,6 +54,10 @@ Dynamic: license-file
53
54
  > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
54
55
  > machine, for a single user** — that is the case it is built and tested for. Before relying on it
55
56
  > for anything beyond that, read **[Project status](#project-status)**.
57
+ >
58
+ > **New here? Read [docs/trust.md](docs/trust.md) first** — what each engine's answer is worth, a
59
+ > five-minute check on a symbol whose callers you already know, and the eight states your
60
+ > repository can be in with one command for each.
56
61
 
57
62
  ![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)
58
63
 
@@ -432,7 +437,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
432
437
  - **[Install & registration](docs/install.md)** — what each agent host actually reads, why the registered command is an absolute path, and the three levels of proof that registration worked.
433
438
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
434
439
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
435
- - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
440
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 30 k chunks indexed in ~10 min, ~233 ms warm queries, 68 MB index.
436
441
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
437
442
 
438
443
  ## CLI reference
@@ -448,7 +453,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
448
453
  | `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) |
449
454
  | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
450
455
  | `codeintel status [project_root]` | Show engine availability and index age |
451
- | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
456
+ | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap. `--deep` puts one **real query** to each engine and requires an answer with content — a process that starts is not a process that answers, and `runnable` means the second |
452
457
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
453
458
  | `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
454
459
  | `codeintel c4 [project_root] [--out DIR] [--depth N] [--scope PATH] [--include-tests] [--no-index] [--edges union\|imports] [--json] [--layers] [--suggest-config] [--check] [--layers-from auto\|inferred\|declared]` | Write a LikeC4 architecture model (`.c4`) of the repo's files and import graph. Indexes the repo first if it has no graph index. `--edges imports` drops the lower-confidence edges for a legible diagram; `--layers` reports inferred architectural bands; `--check` gates CI on a declared `[layers]` config, exit 2 on drift. See [An architecture model, as source](#an-architecture-model-as-source) and [docs/c4.md](docs/c4.md) |
@@ -13,6 +13,10 @@
13
13
  > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
14
14
  > machine, for a single user** — that is the case it is built and tested for. Before relying on it
15
15
  > for anything beyond that, read **[Project status](#project-status)**.
16
+ >
17
+ > **New here? Read [docs/trust.md](docs/trust.md) first** — what each engine's answer is worth, a
18
+ > five-minute check on a symbol whose callers you already know, and the eight states your
19
+ > repository can be in with one command for each.
16
20
 
17
21
  ![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)
18
22
 
@@ -392,7 +396,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
392
396
  - **[Install & registration](docs/install.md)** — what each agent host actually reads, why the registered command is an absolute path, and the three levels of proof that registration worked.
393
397
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
394
398
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
395
- - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
399
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 30 k chunks indexed in ~10 min, ~233 ms warm queries, 68 MB index.
396
400
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
397
401
 
398
402
  ## CLI reference
@@ -408,7 +412,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
408
412
  | `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) |
409
413
  | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
410
414
  | `codeintel status [project_root]` | Show engine availability and index age |
411
- | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
415
+ | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap. `--deep` puts one **real query** to each engine and requires an answer with content — a process that starts is not a process that answers, and `runnable` means the second |
412
416
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
413
417
  | `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
414
418
  | `codeintel c4 [project_root] [--out DIR] [--depth N] [--scope PATH] [--include-tests] [--no-index] [--edges union\|imports] [--json] [--layers] [--suggest-config] [--check] [--layers-from auto\|inferred\|declared]` | Write a LikeC4 architecture model (`.c4`) of the repo's files and import graph. Indexes the repo first if it has no graph index. `--edges imports` drops the lower-confidence edges for a legible diagram; `--layers` reports inferred architectural bands; `--check` gates CI on a declared `[layers]` config, exit 2 on drift. See [An architecture model, as source](#an-architecture-model-as-source) and [docs/c4.md](docs/c4.md) |
@@ -53,7 +53,8 @@ Changelog = "https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md"
53
53
  codeintel = "codeintel.__main__:main"
54
54
 
55
55
  [project.optional-dependencies]
56
- dev = ["pytest>=8", "pytest-cov>=5", "numpy>=1.24", "ruff>=0.16", "mypy>=1.11"]
56
+ dev = ["pytest>=8", "pytest-cov>=5", "pytest-timeout>=2.3", "numpy>=1.24", "ruff>=0.16",
57
+ "mypy>=1.11"]
57
58
 
58
59
  # `bench/agent_bench.py` only. Deliberately NOT in `dev`, and emphatically not a runtime dependency:
59
60
  # codeintel's own code makes zero outbound HTTP calls and that claim is load-bearing in the README.
@@ -161,3 +162,34 @@ follow_imports = "skip"
161
162
  # case in CI, and that costs ~0.4 points. A floor pinned to today's measurement would go red on a
162
163
  # runner's toolchain rather than on a real coverage regression. Ratchet it up deliberately.
163
164
  addopts = "--cov=codeintel --cov-report=term-missing:skip-covered --cov-fail-under=83"
165
+
166
+ # No test may run indefinitely. The ceiling is deliberately far above any legitimate test rather
167
+ # than tuned to the current slowest: its job is to convert a hang into a named failure, not to
168
+ # police performance. Measured worst cases after the background-reindex guard landed —
169
+ # `test_cold_process` spawns real cold interpreters at ~60s, `test_hard_exit` runs nested pytest
170
+ # sessions at ~12s — so 180s leaves room for a slow CI runner and still fails in three minutes
171
+ # instead of never.
172
+ #
173
+ # `signal` rather than `thread`: SIGALRM fails the ONE test that overran and lets the session carry
174
+ # on to a normal report. pytest-timeout's `thread` method kills the whole process, which would
175
+ # reproduce the thing being fixed — an unexplained death partway through — with better provenance
176
+ # but no more information. A test that needs longer says so with
177
+ # `@pytest.mark.timeout(<seconds>)` and, ideally, why.
178
+ timeout = 180
179
+ timeout_method = "signal"
180
+
181
+ # A leaked socket, pipe or file handle is a defect, not a note. Promoted to an error once the
182
+ # suite's own leaks were closed — sixteen HTTP listeners kept open by `shutdown()` without
183
+ # `server_close()`, a template file, eight bare `open(...).read()` calls in `test_mapper`, and two
184
+ # MCP children killed without being waited on or having their pipes closed. Leaving it at
185
+ # `warning` is what let all of those accumulate: the count was in every run's output and had been
186
+ # for months.
187
+ #
188
+ # `default::ResourceWarning:_pytest.unraisableexception` is NOT an exemption for our own leaks —
189
+ # pytest surfaces a garbage-collected leak through that module rather than from the test that
190
+ # caused it, and as an error it fails whichever unrelated test happened to trigger the collection.
191
+ # Our leaks are caught at their own site by the first rule; this keeps the blame accurate.
192
+ filterwarnings = [
193
+ "error::ResourceWarning",
194
+ "default::ResourceWarning:_pytest.unraisableexception",
195
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.23.4
3
+ Version: 0.24.1
4
4
  Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
5
  Author: Shammai Hamilton
6
6
  License-Expression: MIT
@@ -31,6 +31,7 @@ Requires-Dist: tree-sitter-language-pack>=1.0
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: pytest>=8; extra == "dev"
33
33
  Requires-Dist: pytest-cov>=5; extra == "dev"
34
+ Requires-Dist: pytest-timeout>=2.3; extra == "dev"
34
35
  Requires-Dist: numpy>=1.24; extra == "dev"
35
36
  Requires-Dist: ruff>=0.16; extra == "dev"
36
37
  Requires-Dist: mypy>=1.11; extra == "dev"
@@ -53,6 +54,10 @@ Dynamic: license-file
53
54
  > been pointed at an unfamiliar codebase it has found real bugs. **Use it locally, on a developer
54
55
  > machine, for a single user** — that is the case it is built and tested for. Before relying on it
55
56
  > for anything beyond that, read **[Project status](#project-status)**.
57
+ >
58
+ > **New here? Read [docs/trust.md](docs/trust.md) first** — what each engine's answer is worth, a
59
+ > five-minute check on a symbol whose callers you already know, and the eight states your
60
+ > repository can be in with one command for each.
56
61
 
57
62
  ![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)
58
63
 
@@ -432,7 +437,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
432
437
  - **[Install & registration](docs/install.md)** — what each agent host actually reads, why the registered command is an absolute path, and the three levels of proof that registration worked.
433
438
  - **[Query flow](docs/query-flow.md)** — request lifecycle, engine selection, fan-out & merge, and why it never throws.
434
439
  - **[Map file](docs/map-file.md)** — the static `CODE_INTEL.md` orientation layer for hosts with no MCP support.
435
- - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 25 k chunks indexed in ~8 min, ~235 ms warm queries, 60 MB index.
440
+ - **[Benchmarks](docs/benchmarks.md)** — real numbers at scale: 30 k chunks indexed in ~10 min, ~233 ms warm queries, 68 MB index.
436
441
  - Engine references: **[graph](docs/graph.md)** · **[lsp](docs/lsp.md)** · **[semantic](docs/semantic.md)**.
437
442
 
438
443
  ## CLI reference
@@ -448,7 +453,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
448
453
  | `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) |
449
454
  | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR] [--json]` | Run a single query and print the result |
450
455
  | `codeintel status [project_root]` | Show engine availability and index age |
451
- | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
456
+ | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap. `--deep` puts one **real query** to each engine and requires an answer with content — a process that starts is not a process that answers, and `runnable` means the second |
452
457
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
453
458
  | `codeintel graph [project_root] [--html] [--out FILE] [--limit N]` | Emit the call graph as `{nodes,edges}` JSON, or `--html` a self-contained interactive viewer — see [docs/graph-viewer.md](docs/graph-viewer.md) |
454
459
  | `codeintel c4 [project_root] [--out DIR] [--depth N] [--scope PATH] [--include-tests] [--no-index] [--edges union\|imports] [--json] [--layers] [--suggest-config] [--check] [--layers-from auto\|inferred\|declared]` | Write a LikeC4 architecture model (`.c4`) of the repo's files and import graph. Indexes the repo first if it has no graph index. `--edges imports` drops the lower-confidence edges for a legible diagram; `--layers` reports inferred architectural bands; `--check` gates CI on a declared `[layers]` config, exit 2 on drift. See [An architecture model, as source](#an-architecture-model-as-source) and [docs/c4.md](docs/c4.md) |
@@ -18,10 +18,16 @@ src/codeintel/cache.py
18
18
  src/codeintel/config.py
19
19
  src/codeintel/containment.py
20
20
  src/codeintel/doctor.py
21
+ src/codeintel/filelock.py
21
22
  src/codeintel/gateway.py
23
+ src/codeintel/graph_answer.py
22
24
  src/codeintel/graph_backend.py
25
+ src/codeintel/graph_confidence.py
26
+ src/codeintel/graph_edges.py
27
+ src/codeintel/graph_ops.py
23
28
  src/codeintel/graph_render.py
24
29
  src/codeintel/graph_resolution.py
30
+ src/codeintel/graph_targets.py
25
31
  src/codeintel/grapher.py
26
32
  src/codeintel/http_server.py
27
33
  src/codeintel/indexer.py
@@ -35,6 +41,7 @@ src/codeintel/metrics.py
35
41
  src/codeintel/onboarding.py
36
42
  src/codeintel/outcome.py
37
43
  src/codeintel/paths.py
44
+ src/codeintel/per_thread.py
38
45
  src/codeintel/policy.py
39
46
  src/codeintel/progress.py
40
47
  src/codeintel/provider.py
@@ -66,6 +73,7 @@ src/codeintel/commands/serve.py
66
73
  src/codeintel/commands/serve_http.py
67
74
  src/codeintel/commands/setup.py
68
75
  src/codeintel/commands/status.py
76
+ src/codeintel/commands/uninstall.py
69
77
  src/codeintel/providers/__init__.py
70
78
  src/codeintel/providers/graph.py
71
79
  src/codeintel/providers/lsp.py
@@ -76,6 +84,8 @@ src/codeintel/viewer/graph_template.html
76
84
  tests/test_agent_prompt.py
77
85
  tests/test_backend_reaper.py
78
86
  tests/test_background_index_failure.py
87
+ tests/test_bench_corpus_typed.py
88
+ tests/test_bench_gate.py
79
89
  tests/test_bench_oracle.py
80
90
  tests/test_bench_oracle_ts.py
81
91
  tests/test_c4.py
@@ -95,6 +105,7 @@ tests/test_corpus.py
95
105
  tests/test_could_not_ask_semantic.py
96
106
  tests/test_docs_ci_claims.py
97
107
  tests/test_docs_deadcode_withdrawal.py
108
+ tests/test_docs_trust_claims.py
98
109
  tests/test_doctor.py
99
110
  tests/test_e2e.py
100
111
  tests/test_edge_confidence.py
@@ -112,6 +123,7 @@ tests/test_http_auth.py
112
123
  tests/test_http_server.py
113
124
  tests/test_incompleteness.py
114
125
  tests/test_index_failure_reason.py
126
+ tests/test_index_incompleteness.py
115
127
  tests/test_index_progress.py
116
128
  tests/test_indexer_binary.py
117
129
  tests/test_installer.py
@@ -124,21 +136,28 @@ tests/test_lsp_real.py
124
136
  tests/test_mapper.py
125
137
  tests/test_mcp_handshake.py
126
138
  tests/test_mcp_server.py
139
+ tests/test_missing_target.py
127
140
  tests/test_model_dimension.py
128
141
  tests/test_never_raise.py
129
142
  tests/test_null_reason_and_ripple.py
130
143
  tests/test_onboarding.py
144
+ tests/test_per_thread.py
145
+ tests/test_query_budget.py
131
146
  tests/test_rbac.py
132
147
  tests/test_redaction_boundary.py
148
+ tests/test_reindex_lock.py
133
149
  tests/test_reindexer.py
134
150
  tests/test_rerank.py
135
151
  tests/test_reset.py
136
152
  tests/test_semantic_provider.py
137
153
  tests/test_source_kind.py
138
154
  tests/test_staleness.py
155
+ tests/test_summary_integrity.py
139
156
  tests/test_symbol_context.py
140
157
  tests/test_term.py
141
158
  tests/test_treesitter.py
159
+ tests/test_uninstall.py
160
+ tests/test_upgrade_path.py
142
161
  tests/test_verify.py
143
162
  tests/test_verify_call.py
144
163
  tests/test_version_skew.py
@@ -9,6 +9,7 @@ anthropic>=0.40
9
9
  [dev]
10
10
  pytest>=8
11
11
  pytest-cov>=5
12
+ pytest-timeout>=2.3
12
13
  numpy>=1.24
13
14
  ruff>=0.16
14
15
  mypy>=1.11
@@ -0,0 +1 @@
1
+ __version__ = "0.24.1"
@@ -44,6 +44,7 @@ _COMMAND_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
44
44
  ("setup", "Prepare backends + index this repo (--all does it all)"),
45
45
  ("index", "Build the index every other command reads (semantic + graph)"),
46
46
  ("install", "Register codeintel with the AI agents on this machine"),
47
+ ("uninstall", "Remove those registrations — the index is left alone"),
47
48
  ("prompt", "Print a paste-to-your-agent setup prompt for this machine"),
48
49
  ]),
49
50
  ("Check health", [
@@ -79,6 +80,7 @@ _MODULES = {
79
80
  "setup": "setup",
80
81
  "index": "index",
81
82
  "install": "install",
83
+ "uninstall": "uninstall",
82
84
  "prompt": "prompt",
83
85
  "doctor": "doctor",
84
86
  "status": "status",
@@ -333,6 +335,13 @@ The one-shot path on a new machine.""",
333
335
  Writes the MCP server config so an agent can call codeintel. Then restart the
334
336
  agent (or start a new session) — a running host does not reload its MCP
335
337
  config. `codeintel doctor` lists what got registered where.""",
338
+ "uninstall": """examples:
339
+ codeintel uninstall
340
+ codeintel uninstall --dry-run show what would go, change nothing
341
+
342
+ Removes only codeintel's own entry from each agent config — neighbouring
343
+ servers and your settings are left alone. The index is NOT deleted; use
344
+ `codeintel reset --all` for that.""",
336
345
  "prompt": """examples:
337
346
  codeintel prompt
338
347
 
@@ -462,7 +471,8 @@ def build_parser() -> argparse.ArgumentParser:
462
471
  "--json", action="store_true", help="Emit the structured status and index-age report")
463
472
  status_parser.add_argument(
464
473
  "--deep", action="store_true",
465
- help="Boot LSP and sample indexed source readability (slower, read-only)")
474
+ help="Put a real query to each engine and require an answer, boot the LSP, and sample "
475
+ "indexed source readability (slower, read-only)")
466
476
 
467
477
  # serve-http subcommand
468
478
  http_parser = subparsers.add_parser(
@@ -503,6 +513,22 @@ def build_parser() -> argparse.ArgumentParser:
503
513
  help="Show what would be registered, and where — write nothing",
504
514
  )
505
515
 
516
+ # uninstall subcommand
517
+ uninstall_parser = subparsers.add_parser(
518
+ "uninstall", help=_DESCRIPTIONS["uninstall"], description=_DESCRIPTIONS["uninstall"])
519
+ uninstall_parser.add_argument(
520
+ "--agent",
521
+ choices=["auto", "claude", "codex", "gemini", "zed", "all"],
522
+ default="auto",
523
+ help="Agent to remove from (default: auto — every agent codeintel is REGISTERED with, "
524
+ "which is a different set from the agents installed here)",
525
+ )
526
+ uninstall_parser.add_argument(
527
+ "--dry-run",
528
+ action="store_true",
529
+ help="Show what would be removed, and from where — change nothing",
530
+ )
531
+
506
532
  # map subcommand
507
533
  map_parser = subparsers.add_parser(
508
534
  "map", help=_DESCRIPTIONS["map"], description=_DESCRIPTIONS["map"])
@@ -575,7 +601,9 @@ def build_parser() -> argparse.ArgumentParser:
575
601
  "doctor", help=_DESCRIPTIONS["doctor"], description=_DESCRIPTIONS["doctor"])
576
602
  doctor_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
577
603
  doctor_parser.add_argument("--deep", action="store_true",
578
- help="Also boot-check serena (slower; first boot pulls it via uvx)")
604
+ help="Ask each engine one REAL query and require content — a "
605
+ "process that starts is not one that answers. Also boots "
606
+ "serena (slower; first boot pulls it via uvx)")
579
607
  doctor_parser.add_argument("--json", action="store_true",
580
608
  help="Emit the structured JSON report instead of the table")
581
609
 
@@ -495,7 +495,8 @@ def build_c4_payload(project_root: Any, *, depth: int | None = None, scope: tupl
495
495
  payload at an auto-fit (or requested) directory depth. Never raises -- returns a flat `reason`
496
496
  string and empty lists on any failure."""
497
497
  try:
498
- from codeintel.providers.graph import GraphProvider, _repo_display_name
498
+ from codeintel.graph_render import _repo_display_name
499
+ from codeintel.providers.graph import GraphProvider
499
500
 
500
501
  p = GraphProvider()
501
502
  if not getattr(p, "available", False):
@@ -6,6 +6,11 @@ from typing import Any
6
6
 
7
7
  from codeintel.commands._common import resolve_root
8
8
 
9
+ # How long a foreground `codeintel index` waits for another process's pass over the same repository
10
+ # before indexing anyway. Long enough to outlast a cold index of a large repo (measured at ~10 min
11
+ # for 30k chunks, docs/benchmarks.md); short enough that a hung holder cannot block it forever.
12
+ _LOCK_WAIT_S = 900.0
13
+
9
14
 
10
15
  class _ProgressLogBridge(logging.Handler):
11
16
  """Routes WARNING+ log records from the indexer to a live progress counter's ``notice()``, so a
@@ -52,6 +57,35 @@ def run(args: Any) -> int:
52
57
  quiet = getattr(args, "quiet", False)
53
58
  counter = None if quiet else term.LiveCounter(console)
54
59
 
60
+ # Take the same per-repository lock the background reindexer takes, so a terminal `index` and
61
+ # a running server's background pass do not embed the whole repository twice. It covers the
62
+ # semantic AND graph passes, as the background pass does; the map refresh is not indexing.
63
+ import contextlib
64
+
65
+ from codeintel.filelock import exclusive_waiting, lock_path
66
+
67
+ def _say_waiting() -> None:
68
+ if not quiet: # --quiet promises the result line only, contention or not
69
+ print("waiting for another codeintel process to finish indexing this repository…",
70
+ flush=True)
71
+
72
+ lock = contextlib.ExitStack()
73
+ # Resolving the lock path reads the codeintel home, which raises where there is no resolvable
74
+ # home directory (a container UID with no passwd entry and no CODEINTEL_HOME). That must not
75
+ # turn into a traceback before the semantic pass's own error handling below — which reports
76
+ # exactly that failure as "index failed: …". A lock we cannot place is a lock we do without,
77
+ # by the rule in `filelock`: it may dedupe work, never stop it.
78
+ try:
79
+ path = lock_path(os.path.realpath(project_root))
80
+ except Exception:
81
+ path = None
82
+ if path is not None:
83
+ got_lock = lock.enter_context(
84
+ exclusive_waiting(path, _LOCK_WAIT_S, on_wait=_say_waiting))
85
+ if not got_lock and not quiet:
86
+ print(f"still locked after {int(_LOCK_WAIT_S)}s — indexing anyway; the other pass "
87
+ "may repeat some of this work", flush=True)
88
+
55
89
  failed = False
56
90
  # Wrap the whole semantic pass so a setup failure (e.g. an unresolvable home dir →
57
91
  # Path.home() raising) degrades with a message, like every other subcommand, not a traceback.
@@ -146,6 +180,8 @@ def run(args: Any) -> int:
146
180
  if beat:
147
181
  beat.stop("warn", "skipped")
148
182
 
183
+ lock.close()
184
+
149
185
  # best-effort map refresh after index (fast, kept silent — not worth a checklist row)
150
186
  try:
151
187
  from codeintel.mapper import MapGenerator
@@ -0,0 +1,106 @@
1
+ """`codeintel uninstall` — remove codeintel's entry from the agent configs `install` wrote.
2
+
3
+ The inverse of `install`, and deliberately only that. It does NOT delete the semantic index: that
4
+ is `reset`'s job, it is expensive to rebuild (ten minutes on a large repo), and a command whose
5
+ name says "uninstall" quietly destroying ten minutes of work is the kind of surprise this project
6
+ spends most of its comments avoiding. What the index costs and how to remove it is printed instead,
7
+ which is the same rule every other disclosure here follows — name what was NOT done.
8
+
9
+ There is no confirmation prompt, and that is a considered difference from `reset --all`. This edits
10
+ one entry in a config file and `codeintel install` puts it straight back; `reset` destroys an index
11
+ that has to be re-embedded. `--dry-run` covers the reader who wants to look first.
12
+ """
13
+
14
+ from typing import Any
15
+
16
+ from codeintel.commands._common import never_raise
17
+
18
+
19
+ def _targets(agent: str, installer_mod: Any) -> tuple[list[str], list[str]]:
20
+ """`(agents, untouched)` for the requested `--agent` selection.
21
+
22
+ `auto` means "wherever codeintel is actually registered", not "agents installed on this
23
+ machine" — the question `install` asks. They differ in both directions that matter: an agent
24
+ uninstalled from this machine can still hold our entry, and an agent that is present but never
25
+ registered has nothing to remove and should not be reported as though it did.
26
+ """
27
+ if agent == "auto":
28
+ registered = installer_mod.registered_agents()
29
+ return registered, [a for a in installer_mod._AGENTS if a not in registered]
30
+ if agent == "all":
31
+ return list(installer_mod._AGENTS), []
32
+ return [agent], []
33
+
34
+
35
+ def _dry_run_line(installer_mod: Any, agent: str) -> str:
36
+ """What `--dry-run` would do to one agent's config — read-only, through the same lookup
37
+ `codeintel doctor` uses to notice a registration at all."""
38
+ spec = installer_mod._CONFIG[agent]
39
+ path, current = installer_mod.registered_command(spec)
40
+ if current is None:
41
+ return f"~ {agent}: no codeintel entry in {path} (no change)"
42
+ return f"- {agent}: would remove the codeintel entry from {path} (command: {current})"
43
+
44
+
45
+ def _report(results: list) -> tuple[bool, bool, list[str]]:
46
+ """Print one line per agent; return (removed_any, failed_any, legacy_paths)."""
47
+ removed_any = failed_any = False
48
+ legacy_paths: list[str] = []
49
+ for r in results:
50
+ agent, path, action = r["agent"], r["path"], r["action"]
51
+ if action == "removed":
52
+ print(f"v {agent}: removed from {path}")
53
+ removed_any = True
54
+ elif action == "absent":
55
+ print(f"~ {agent}: nothing registered at {path}")
56
+ else:
57
+ print(f"x {agent}: failed — {r['reason']}")
58
+ failed_any = True
59
+ if r.get("legacy"):
60
+ legacy_paths.append(r["legacy"])
61
+ return removed_any, failed_any, legacy_paths
62
+
63
+
64
+ @never_raise("uninstall failed: {exc}", code=1)
65
+ def run(args: Any) -> int:
66
+ from codeintel import installer as installer_mod
67
+
68
+ agents, untouched = _targets(args.agent, installer_mod)
69
+
70
+ if args.agent == "auto" and not agents:
71
+ # Exit 0, not 1. The requested end state — codeintel is not registered with anything —
72
+ # already holds, and reporting "failure" for a goal that is met would make this command
73
+ # unusable in a teardown script that runs whether or not install ever ran.
74
+ print("codeintel is not registered with any agent on this machine — nothing to remove.")
75
+ print(f" (looked at: {', '.join(untouched)})")
76
+ return 0
77
+
78
+ if getattr(args, "dry_run", False):
79
+ for agent in agents:
80
+ print(_dry_run_line(installer_mod, agent))
81
+ print("\nDry run — nothing was changed.")
82
+ return 0
83
+
84
+ results = installer_mod.Installer().unregister_many(agents)
85
+ removed_any, failed_any, legacy_paths = _report(results)
86
+
87
+ for legacy in dict.fromkeys(legacy_paths):
88
+ print(f"\n! stale entry: {legacy} has an `mcpServers.codeintel` block that this host does "
89
+ f"NOT read (an older codeintel wrote it). It is inert, and safe to delete by hand.")
90
+
91
+ if args.agent == "auto" and untouched:
92
+ print(f"\n- untouched (nothing registered): {', '.join(untouched)}")
93
+
94
+ if removed_any:
95
+ # A host that is already running keeps the server it launched at session start, so the
96
+ # tools stay on screen until it is restarted — the mirror of the note `install` prints,
97
+ # and the same surprise in the other direction.
98
+ print("\nRestart your agent (or start a new session) — a running host does not reload "
99
+ "its MCP config, so codeintel's tools stay visible until it does.")
100
+
101
+ # Named, not done. The index is the expensive artifact and this command does not own it.
102
+ from codeintel.paths import codeintel_home
103
+ print(f"\nThe index is untouched: {codeintel_home()}")
104
+ print(" Remove it with `codeintel reset --all` if you are done with codeintel entirely.")
105
+
106
+ return 1 if failed_any else 0
@@ -280,7 +280,7 @@ def run_doctor(
280
280
  try:
281
281
  from codeintel.providers.graph import GraphProvider
282
282
  engines["graph"] = _probe_engine(
283
- "graph", graph, GraphProvider, lambda p: p.probe(root), on_provider
283
+ "graph", graph, GraphProvider, lambda p: p.probe(root, deep=deep), on_provider
284
284
  )
285
285
  except Exception:
286
286
  engines["graph"] = {"engine": "graph", "status": "fail", "installed": False,
@@ -414,7 +414,10 @@ def render_doctor_text(report: dict) -> str:
414
414
  ready, total, healthy = summ.get("ready", "?"), summ.get("total", "?"), summ.get("healthy")
415
415
  count = c.bold(f"{ready} / {total}")
416
416
  count = c.red(count) if healthy is False else (c.green(count) if healthy else count)
417
- tail = "" if report.get("deep") else c.dim(" (run with --deep to boot-check serena)")
417
+ # Names what --deep actually does. "boot-check serena" described one third of it and the
418
+ # least useful third: booting is the question this tool keeps learning not to stop at.
419
+ tail = "" if report.get("deep") else c.dim(
420
+ " (run with --deep to ask each engine a real query)")
418
421
  out.append("")
419
422
  out.append(f" {count} engines ready for this repo.{tail}")
420
423
  opt_down = [n for n in _ENGINES