codecortex 0.12.1__tar.gz → 0.13.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 (98) hide show
  1. {codecortex-0.12.1 → codecortex-0.13.1}/PKG-INFO +43 -6
  2. codecortex-0.12.1/src/codecortex.egg-info/PKG-INFO → codecortex-0.13.1/README.md +38 -38
  3. codecortex-0.13.1/pyproject.toml +139 -0
  4. codecortex-0.12.1/README.md → codecortex-0.13.1/src/codecortex.egg-info/PKG-INFO +75 -4
  5. {codecortex-0.12.1 → codecortex-0.13.1}/src/codecortex.egg-info/SOURCES.txt +16 -0
  6. {codecortex-0.12.1 → codecortex-0.13.1}/src/codecortex.egg-info/requires.txt +3 -0
  7. codecortex-0.13.1/src/codeintel/__init__.py +1 -0
  8. codecortex-0.13.1/src/codeintel/__main__.py +273 -0
  9. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/auth.py +10 -9
  10. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/cache.py +7 -8
  11. codecortex-0.13.1/src/codeintel/commands/__init__.py +6 -0
  12. codecortex-0.13.1/src/codeintel/commands/_common.py +46 -0
  13. codecortex-0.13.1/src/codeintel/commands/doctor.py +15 -0
  14. codecortex-0.13.1/src/codeintel/commands/gen_token.py +9 -0
  15. codecortex-0.13.1/src/codeintel/commands/graph.py +31 -0
  16. codecortex-0.13.1/src/codeintel/commands/index.py +63 -0
  17. codecortex-0.13.1/src/codeintel/commands/install.py +77 -0
  18. codecortex-0.13.1/src/codeintel/commands/map.py +36 -0
  19. codecortex-0.13.1/src/codeintel/commands/query.py +55 -0
  20. codecortex-0.13.1/src/codeintel/commands/reset.py +27 -0
  21. codecortex-0.13.1/src/codeintel/commands/serve.py +10 -0
  22. codecortex-0.13.1/src/codeintel/commands/serve_http.py +20 -0
  23. codecortex-0.13.1/src/codeintel/commands/setup.py +21 -0
  24. codecortex-0.13.1/src/codeintel/commands/status.py +42 -0
  25. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/config.py +2 -12
  26. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/doctor.py +8 -7
  27. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/gateway.py +4 -5
  28. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/http_server.py +6 -2
  29. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/indexer.py +6 -7
  30. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/installer.py +4 -5
  31. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/mapper.py +10 -10
  32. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/metrics.py +3 -4
  33. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/onboarding.py +6 -4
  34. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/policy.py +2 -4
  35. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/provider.py +5 -4
  36. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/providers/graph.py +98 -34
  37. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/providers/lsp.py +27 -29
  38. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/providers/semantic.py +3 -4
  39. codecortex-0.13.1/src/codeintel/py.typed +0 -0
  40. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/reindexer.py +17 -3
  41. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/searcher.py +1 -1
  42. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/server.py +10 -6
  43. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/term.py +3 -4
  44. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/verify.py +23 -21
  45. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_chunking.py +3 -2
  46. codecortex-0.13.1/tests/test_cli_commands.py +773 -0
  47. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_doctor.py +1 -3
  48. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_gateway.py +0 -1
  49. codecortex-0.13.1/tests/test_graph_provider.py +310 -0
  50. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_graph_stdin.py +4 -1
  51. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_hardening.py +0 -1
  52. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_http_auth.py +3 -1
  53. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_http_server.py +1 -1
  54. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_lsp_real.py +2 -2
  55. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_mapper.py +2 -4
  56. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_mcp_handshake.py +21 -24
  57. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_mcp_server.py +36 -0
  58. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_never_raise.py +4 -8
  59. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_rbac.py +0 -1
  60. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_reindexer.py +73 -1
  61. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_rerank.py +4 -4
  62. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_semantic_provider.py +2 -10
  63. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_treesitter.py +1 -1
  64. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_verify.py +0 -1
  65. codecortex-0.12.1/pyproject.toml +0 -62
  66. codecortex-0.12.1/src/codeintel/__init__.py +0 -1
  67. codecortex-0.12.1/src/codeintel/__main__.py +0 -577
  68. codecortex-0.12.1/tests/test_graph_provider.py +0 -170
  69. {codecortex-0.12.1 → codecortex-0.13.1}/LICENSE +0 -0
  70. {codecortex-0.12.1 → codecortex-0.13.1}/setup.cfg +0 -0
  71. {codecortex-0.12.1 → codecortex-0.13.1}/src/codecortex.egg-info/dependency_links.txt +0 -0
  72. {codecortex-0.12.1 → codecortex-0.13.1}/src/codecortex.egg-info/entry_points.txt +0 -0
  73. {codecortex-0.12.1 → codecortex-0.13.1}/src/codecortex.egg-info/top_level.txt +0 -0
  74. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/grapher.py +0 -0
  75. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/injector.py +0 -0
  76. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/logconfig.py +0 -0
  77. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/providers/__init__.py +0 -0
  78. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/providers/none.py +0 -0
  79. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/reset.py +0 -0
  80. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/semantic_db.py +0 -0
  81. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/viewer/__init__.py +0 -0
  82. {codecortex-0.12.1 → codecortex-0.13.1}/src/codeintel/viewer/graph_template.html +0 -0
  83. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_cache.py +0 -0
  84. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_cli_help.py +0 -0
  85. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_config.py +0 -0
  86. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_e2e.py +0 -0
  87. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_engine_adoption.py +0 -0
  88. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_enterprise.py +0 -0
  89. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_graph_real.py +0 -0
  90. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_grapher.py +0 -0
  91. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_installer.py +1 -1
  92. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_integration.py +0 -0
  93. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_lsp_provider.py +0 -0
  94. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_model_dimension.py +0 -0
  95. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_onboarding.py +0 -0
  96. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_reset.py +0 -0
  97. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_term.py +0 -0
  98. {codecortex-0.12.1 → codecortex-0.13.1}/tests/test_verify_call.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecortex
3
- Version: 0.12.1
3
+ Version: 0.13.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
@@ -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 :: 4 - Beta
12
+ Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Operating System :: OS Independent
@@ -29,7 +29,10 @@ Requires-Dist: fastembed>=0.3
29
29
  Requires-Dist: tree-sitter-language-pack>=1.0
30
30
  Provides-Extra: dev
31
31
  Requires-Dist: pytest>=8; extra == "dev"
32
+ Requires-Dist: pytest-cov>=5; extra == "dev"
32
33
  Requires-Dist: numpy>=1.24; extra == "dev"
34
+ Requires-Dist: ruff>=0.16; extra == "dev"
35
+ Requires-Dist: mypy>=1.11; extra == "dev"
33
36
  Dynamic: license-file
34
37
 
35
38
  # codeintel
@@ -108,7 +111,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
108
111
  - **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
109
112
  - **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
110
113
  - **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
111
- - **Fast on repeat, never stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
114
+ - **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
112
115
  - **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
113
116
  - **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
114
117
 
@@ -271,6 +274,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
271
274
 
272
275
  Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
273
276
 
277
+ ### Keeping answers fresh
278
+
279
+ The three engines have genuinely different freshness models, and it's worth knowing which you're
280
+ reading:
281
+
282
+ | Engine | Freshness |
283
+ |---|---|
284
+ | `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
285
+ | `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
286
+ | `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
287
+
288
+ So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
289
+ describes code you just changed — or a symbol you just added comes back
290
+ `reason: "not-in-graph"` — that's the signal to re-run:
291
+
292
+ ```bash
293
+ codeintel index /path/to/repo
294
+ ```
295
+
296
+ The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
297
+ rather than a bare reason.
298
+
274
299
  Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
275
300
 
276
301
  ## Documentation
@@ -294,12 +319,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
294
319
  | `codeintel index [project_root]` | Index a project for semantic search |
295
320
  | `codeintel serve` | Start the MCP server (stdio transport) |
296
321
  | `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) |
297
- | `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
322
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
298
323
  | `codeintel status [project_root]` | Show engine availability and index age |
299
324
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
300
325
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
301
326
  | `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) |
302
- | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
327
+ | `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
303
328
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
304
329
 
305
330
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
@@ -414,7 +439,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
414
439
  git clone https://github.com/hamilton-sky/codeintel.git
415
440
  cd codeintel
416
441
  pip install -e .[dev]
417
- pytest tests/ -q # ~410 tests, ~35s (live graph/LSP backend tests skip when absent)
442
+
443
+ pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
444
+ ruff check src tests # lint
445
+ mypy # types (src/ only)
446
+ ```
447
+
448
+ **Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
449
+ installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
450
+ take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
451
+ desk and fails in CI. To see CI's shape before you push:
452
+
453
+ ```bash
454
+ env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
418
455
  ```
419
456
 
420
457
  **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
@@ -1,37 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: codecortex
3
- Version: 0.12.1
4
- Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
- Author: Shammai Hamilton
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/hamilton-sky/codeintel
8
- Project-URL: Repository, https://github.com/hamilton-sky/codeintel
9
- Project-URL: Issues, https://github.com/hamilton-sky/codeintel/issues
10
- Project-URL: Changelog, https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md
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 :: 4 - Beta
13
- Classifier: Intended Audience :: Developers
14
- Classifier: Environment :: Console
15
- Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Classifier: Topic :: Software Development :: Quality Assurance
22
- Classifier: Typing :: Typed
23
- Requires-Python: >=3.11
24
- Description-Content-Type: text/markdown
25
- License-File: LICENSE
26
- Requires-Dist: mcp>=1.0
27
- Requires-Dist: sqlite-vec>=0.1
28
- Requires-Dist: fastembed>=0.3
29
- Requires-Dist: tree-sitter-language-pack>=1.0
30
- Provides-Extra: dev
31
- Requires-Dist: pytest>=8; extra == "dev"
32
- Requires-Dist: numpy>=1.24; extra == "dev"
33
- Dynamic: license-file
34
-
35
1
  # codeintel
36
2
 
37
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.
@@ -108,7 +74,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
108
74
  - **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
109
75
  - **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
110
76
  - **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
111
- - **Fast on repeat, never stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
77
+ - **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
112
78
  - **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
113
79
  - **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
114
80
 
@@ -271,6 +237,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
271
237
 
272
238
  Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
273
239
 
240
+ ### Keeping answers fresh
241
+
242
+ The three engines have genuinely different freshness models, and it's worth knowing which you're
243
+ reading:
244
+
245
+ | Engine | Freshness |
246
+ |---|---|
247
+ | `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
248
+ | `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
249
+ | `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
250
+
251
+ So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
252
+ describes code you just changed — or a symbol you just added comes back
253
+ `reason: "not-in-graph"` — that's the signal to re-run:
254
+
255
+ ```bash
256
+ codeintel index /path/to/repo
257
+ ```
258
+
259
+ The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
260
+ rather than a bare reason.
261
+
274
262
  Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
275
263
 
276
264
  ## Documentation
@@ -294,12 +282,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
294
282
  | `codeintel index [project_root]` | Index a project for semantic search |
295
283
  | `codeintel serve` | Start the MCP server (stdio transport) |
296
284
  | `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) |
297
- | `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
285
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
298
286
  | `codeintel status [project_root]` | Show engine availability and index age |
299
287
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
300
288
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
301
289
  | `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) |
302
- | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
290
+ | `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
303
291
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
304
292
 
305
293
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
@@ -414,7 +402,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
414
402
  git clone https://github.com/hamilton-sky/codeintel.git
415
403
  cd codeintel
416
404
  pip install -e .[dev]
417
- pytest tests/ -q # ~410 tests, ~35s (live graph/LSP backend tests skip when absent)
405
+
406
+ pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
407
+ ruff check src tests # lint
408
+ mypy # types (src/ only)
409
+ ```
410
+
411
+ **Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
412
+ installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
413
+ take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
414
+ desk and fails in CI. To see CI's shape before you push:
415
+
416
+ ```bash
417
+ env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
418
418
  ```
419
419
 
420
420
  **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
@@ -0,0 +1,139 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ # Distribution name is `codecortex` because `codeintel` is taken on PyPI and `codeintel-*` names
7
+ # are rejected as "too similar" to it. The import package and the CLI are still `codeintel`
8
+ # (i.e. `pip install codecortex` installs the `codeintel` command).
9
+ name = "codecortex"
10
+ dynamic = ["version"]
11
+ description = "Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents."
12
+ readme = "README.md"
13
+ requires-python = ">=3.11"
14
+ license = "MIT"
15
+ license-files = ["LICENSE"]
16
+ authors = [{ name = "Shammai Hamilton" }]
17
+ keywords = [
18
+ "mcp", "model-context-protocol", "code-intelligence", "code-search", "llm", "agents",
19
+ "lsp", "semantic-search", "knowledge-graph", "static-analysis", "developer-tools",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 5 - Production/Stable",
23
+ "Intended Audience :: Developers",
24
+ "Environment :: Console",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Software Development :: Libraries :: Python Modules",
31
+ "Topic :: Software Development :: Quality Assurance",
32
+ "Typing :: Typed",
33
+ ]
34
+ dependencies = [
35
+ "mcp>=1.0",
36
+ "sqlite-vec>=0.1",
37
+ "fastembed>=0.3",
38
+ "tree-sitter-language-pack>=1.0", # def-aligned chunking for non-Python languages (P3)
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/hamilton-sky/codeintel"
43
+ Repository = "https://github.com/hamilton-sky/codeintel"
44
+ Issues = "https://github.com/hamilton-sky/codeintel/issues"
45
+ Changelog = "https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md"
46
+
47
+ [project.scripts]
48
+ codeintel = "codeintel.__main__:main"
49
+
50
+ [project.optional-dependencies]
51
+ dev = ["pytest>=8", "pytest-cov>=5", "numpy>=1.24", "ruff>=0.16", "mypy>=1.11"]
52
+
53
+ # Single source of truth for the version: read from codeintel.__version__ (AST-parsed, no import).
54
+ [tool.setuptools.dynamic]
55
+ version = { attr = "codeintel.__version__" }
56
+
57
+ [tool.setuptools.packages.find]
58
+ where = ["src"]
59
+
60
+ # Ship the self-contained interactive graph viewer template with the wheel, and the PEP 561 marker
61
+ # without which the `Typing :: Typed` classifier is a promise the installed package cannot keep —
62
+ # type checkers ignore an installed package's annotations entirely unless py.typed is present.
63
+ [tool.setuptools.package-data]
64
+ "codeintel.viewer" = ["*.html"]
65
+ "codeintel" = ["py.typed"]
66
+
67
+ [tool.ruff]
68
+ line-length = 120
69
+ target-version = "py311"
70
+
71
+ [tool.ruff.lint]
72
+ select = [
73
+ "E", "W", # pycodestyle
74
+ "F", # pyflakes
75
+ "I", # import sorting
76
+ "UP", # pyupgrade — this is a >=3.11 package, so modern syntax is always available
77
+ "B", # bugbear
78
+ "C4", # comprehensions
79
+ "SIM", # simplify
80
+ "PIE", "PERF", "RUF",
81
+ "S", # bandit — the HTTP transport is a real network surface
82
+ ]
83
+ ignore = [
84
+ # The never-raise contract IS the architecture: providers and handlers degrade to a `reason`
85
+ # string rather than propagate, and the best-effort refresh paths (graph reindex, map rewrite)
86
+ # are deliberately allowed to fail silently. These three rules flag that design at ~190 sites,
87
+ # so enforcing them would mean 190 `noqa`s asserting the same thing the module docstrings say.
88
+ "S110", # try-except-pass
89
+ "S112", # try-except-continue
90
+ "SIM105", # "use contextlib.suppress" — see below
91
+ "S603", # subprocess call — every one is an explicit argv list, never a shell string
92
+ ]
93
+ # On SIM105 specifically: the suppressed blocks carry their reason on the `pass` line itself
94
+ # (`pass # code_embeddings is created lazily at first embed — may not exist yet`), and
95
+ # contextlib.suppress has nowhere to put that. Losing 13 such explanations to gain a context
96
+ # manager is a bad trade in code whose whole contract is degrading quietly.
97
+
98
+ [tool.ruff.lint.per-file-ignores]
99
+ # Tests stand up fake servers on loopback, assert on hardcoded fixture tokens, and shell out to
100
+ # the CLI under test. Flagging that as a security finding is noise.
101
+ "tests/*" = [
102
+ "S101", "S104", "S105", "S106", "S108", "S404", "S607",
103
+ "S310", # urlopen against the loopback test server this same test just started
104
+ "E402", # a few modules import after a skip guard, so a missing backend skips, not errors
105
+ "E501", # fixture payloads (graph JSON, serena responses) are copied verbatim; rewrapping
106
+ # them would make them stop matching what the real backend returns
107
+ "E702", # `a = 1; b = 2` setup one-liners
108
+ "SIM115", # ad-hoc open() in assertions, inside tmp_path — nothing to leak
109
+ "UP031", # %-format is the readable choice for templating source into a fake server
110
+ ]
111
+
112
+ [tool.mypy]
113
+ python_version = "3.11"
114
+ files = ["src/codeintel"]
115
+ # Deliberately not --strict: the provider seams cross into untyped third-party backends (mcp,
116
+ # fastembed, sqlite-vec, tree-sitter), where demanding annotations buys noise rather than safety.
117
+ # These flags catch the errors that actually bite — unreachable branches, bad returns, silent Any
118
+ # propagation out of our own typed code.
119
+ warn_redundant_casts = true
120
+ warn_unused_ignores = true
121
+ warn_unreachable = true
122
+ warn_no_return = true
123
+ no_implicit_optional = true
124
+ check_untyped_defs = true
125
+
126
+ [[tool.mypy.overrides]]
127
+ # Don't type-check the backends themselves, and don't follow into them: `python_version = "3.11"`
128
+ # makes mypy parse their stubs under 3.11 syntax rules, and numpy's (reached transitively through
129
+ # fastembed) use 3.12 `type` statements — an error in a dependency's own stubs, not in our code.
130
+ module = ["mcp.*", "fastembed.*", "sqlite_vec.*", "tree_sitter_language_pack.*", "numpy.*"]
131
+ ignore_missing_imports = true
132
+ follow_imports = "skip"
133
+
134
+ [tool.pytest.ini_options]
135
+ # The floor sits below the current number (85.3% with every backend present) on purpose: the live
136
+ # graph/LSP tests skip when codebase-memory-mcp and uvx/serena are absent, which is exactly the
137
+ # case in CI, and that costs ~0.4 points. A floor pinned to today's measurement would go red on a
138
+ # runner's toolchain rather than on a real coverage regression. Ratchet it up deliberately.
139
+ addopts = "--cov=codeintel --cov-report=term-missing:skip-covered --cov-fail-under=83"
@@ -1,3 +1,40 @@
1
+ Metadata-Version: 2.4
2
+ Name: codecortex
3
+ Version: 0.13.1
4
+ Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
5
+ Author: Shammai Hamilton
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hamilton-sky/codeintel
8
+ Project-URL: Repository, https://github.com/hamilton-sky/codeintel
9
+ Project-URL: Issues, https://github.com/hamilton-sky/codeintel/issues
10
+ Project-URL: Changelog, https://github.com/hamilton-sky/codeintel/blob/main/CHANGELOG.md
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
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Environment :: Console
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: mcp>=1.0
27
+ Requires-Dist: sqlite-vec>=0.1
28
+ Requires-Dist: fastembed>=0.3
29
+ Requires-Dist: tree-sitter-language-pack>=1.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8; extra == "dev"
32
+ Requires-Dist: pytest-cov>=5; extra == "dev"
33
+ Requires-Dist: numpy>=1.24; extra == "dev"
34
+ Requires-Dist: ruff>=0.16; extra == "dev"
35
+ Requires-Dist: mypy>=1.11; extra == "dev"
36
+ Dynamic: license-file
37
+
1
38
  # codeintel
2
39
 
3
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.
@@ -74,7 +111,7 @@ The agent hands `result` straight to the model. If the graph backend isn't insta
74
111
  - **It never throws.** Every call returns the same JSON envelope; a missing or broken backend degrades to `null` *with a reason*. No exceptions, no 500s, no malformed output for the agent to trip over — so you never wrap `code.query` in a `try`.
75
112
  - **One tool, not three.** Register a single MCP server and it auto-routes each question to graph, LSP, or semantic — instead of wiring up three backends with three response shapes and three failure modes.
76
113
  - **Degrades instead of breaking.** No graph backend installed? That engine returns `null` and the agent falls back to grep. The semantic engine needs nothing external, so codeintel is useful the moment it's installed and only gets sharper as you add backends.
77
- - **Fast on repeat, never stale.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index answers stay both quick *and* fresh. The cache is bounded (LRU), so a long-running server holds steady memory.
114
+ - **Fast on repeat, and the cache never lies.** A content-hash cache returns instantly for unchanged code and self-invalidates when a background reindex advances the index, so you never read a cached answer for code that moved on. The cache is bounded (LRU), so a long-running server holds steady memory. (The *cache* is always consistent with the index; how current the index itself is depends on the engine — see [Keeping answers fresh](#keeping-answers-fresh).)
78
115
  - **Concurrency-safe.** The HTTP transport handles requests on threads, so one slow query (an LSP session warming, a first-time index) can't block every other agent.
79
116
  - **Honest about its own health.** `codeintel doctor` answers three separate questions per engine — *installed?* *runnable?* *is this repo indexed?* — with the single command to fix each gap, so "installed" is never mistaken for "working". And a readiness claim is one a query can actually honor: install a missing backend mid-session and the running server picks it up on the next call, rather than reporting the engine healthy while quietly routing around it until you restart the host.
80
117
 
@@ -237,6 +274,28 @@ Every `Gateway.query()` call returns a dict with exactly these keys:
237
274
 
238
275
  Run `codeintel doctor` at any time to see which engines are actually ready for a repo and how to fix the ones that aren't.
239
276
 
277
+ ### Keeping answers fresh
278
+
279
+ The three engines have genuinely different freshness models, and it's worth knowing which you're
280
+ reading:
281
+
282
+ | Engine | Freshness |
283
+ |---|---|
284
+ | `lsp` | **Live.** Reads your files at query time — always current, no refresh needed. |
285
+ | `semantic` | **Incremental.** A background reindex re-embeds only what changed; `codeintel status` shows the index age. |
286
+ | `graph` | **Snapshot.** Built by `codeintel index` and stale until the next one. |
287
+
288
+ So a `callers`/`impact`/`hotspots` answer is only as current as your last index. If a result
289
+ describes code you just changed — or a symbol you just added comes back
290
+ `reason: "not-in-graph"` — that's the signal to re-run:
291
+
292
+ ```bash
293
+ codeintel index /path/to/repo
294
+ ```
295
+
296
+ The reply names the fix when it can: a missing symbol now returns a `hint` with the exact command
297
+ rather than a bare reason.
298
+
240
299
  Pass `--engine auto` (the default) and codeintel chooses the best engine per operation. Pass `--engine both` or `--engine all` to fan out to multiple engines and merge results.
241
300
 
242
301
  ## Documentation
@@ -260,12 +319,12 @@ Full system docs live in [`docs/`](docs/) — start with the index:
260
319
  | `codeintel index [project_root]` | Index a project for semantic search |
261
320
  | `codeintel serve` | Start the MCP server (stdio transport) |
262
321
  | `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) |
263
- | `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
322
+ | `codeintel query --op OP --target TARGET [--engine auto] [--project-root DIR]` | Run a single query and print the result |
264
323
  | `codeintel status [project_root]` | Show engine availability and index age |
265
324
  | `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
266
325
  | `codeintel map [project_root]` | Generate the `CODE_INTEL.md` orientation file |
267
326
  | `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) |
268
- | `codeintel reset [project_root] [--all] [--yes]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
327
+ | `codeintel reset [project_root] [--all] [--yes] [--json]` | Clear the semantic index (this repo, or `--all`) to recover from a corrupt/stale DB |
269
328
  | `codeintel gen-token` | Print a secure random bearer token (for `serve-http` / RBAC `auth.toml`) |
270
329
 
271
330
  Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--no-color` / `NO_COLOR` and `--ascii`, and auto-degrade to plain text when piped.
@@ -380,7 +439,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
380
439
  git clone https://github.com/hamilton-sky/codeintel.git
381
440
  cd codeintel
382
441
  pip install -e .[dev]
383
- pytest tests/ -q # ~410 tests, ~35s (live graph/LSP backend tests skip when absent)
442
+
443
+ pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
444
+ ruff check src tests # lint
445
+ mypy # types (src/ only)
446
+ ```
447
+
448
+ **Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
449
+ installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
450
+ take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
451
+ desk and fails in CI. To see CI's shape before you push:
452
+
453
+ ```bash
454
+ env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
384
455
  ```
385
456
 
386
457
  **Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
@@ -25,6 +25,7 @@ src/codeintel/metrics.py
25
25
  src/codeintel/onboarding.py
26
26
  src/codeintel/policy.py
27
27
  src/codeintel/provider.py
28
+ src/codeintel/py.typed
28
29
  src/codeintel/reindexer.py
29
30
  src/codeintel/reset.py
30
31
  src/codeintel/searcher.py
@@ -32,6 +33,20 @@ src/codeintel/semantic_db.py
32
33
  src/codeintel/server.py
33
34
  src/codeintel/term.py
34
35
  src/codeintel/verify.py
36
+ src/codeintel/commands/__init__.py
37
+ src/codeintel/commands/_common.py
38
+ src/codeintel/commands/doctor.py
39
+ src/codeintel/commands/gen_token.py
40
+ src/codeintel/commands/graph.py
41
+ src/codeintel/commands/index.py
42
+ src/codeintel/commands/install.py
43
+ src/codeintel/commands/map.py
44
+ src/codeintel/commands/query.py
45
+ src/codeintel/commands/reset.py
46
+ src/codeintel/commands/serve.py
47
+ src/codeintel/commands/serve_http.py
48
+ src/codeintel/commands/setup.py
49
+ src/codeintel/commands/status.py
35
50
  src/codeintel/providers/__init__.py
36
51
  src/codeintel/providers/graph.py
37
52
  src/codeintel/providers/lsp.py
@@ -41,6 +56,7 @@ src/codeintel/viewer/__init__.py
41
56
  src/codeintel/viewer/graph_template.html
42
57
  tests/test_cache.py
43
58
  tests/test_chunking.py
59
+ tests/test_cli_commands.py
44
60
  tests/test_cli_help.py
45
61
  tests/test_config.py
46
62
  tests/test_doctor.py
@@ -5,4 +5,7 @@ tree-sitter-language-pack>=1.0
5
5
 
6
6
  [dev]
7
7
  pytest>=8
8
+ pytest-cov>=5
8
9
  numpy>=1.24
10
+ ruff>=0.16
11
+ mypy>=1.11
@@ -0,0 +1 @@
1
+ __version__ = "0.13.1"