codecortex 0.12.1__tar.gz → 0.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codecortex-0.12.1/src/codecortex.egg-info → codecortex-0.13.0}/PKG-INFO +18 -3
- {codecortex-0.12.1 → codecortex-0.13.0}/README.md +13 -1
- codecortex-0.13.0/pyproject.toml +139 -0
- {codecortex-0.12.1 → codecortex-0.13.0/src/codecortex.egg-info}/PKG-INFO +18 -3
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codecortex.egg-info/SOURCES.txt +16 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codecortex.egg-info/requires.txt +3 -0
- codecortex-0.13.0/src/codeintel/__init__.py +1 -0
- codecortex-0.13.0/src/codeintel/__main__.py +273 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/auth.py +10 -9
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/cache.py +7 -8
- codecortex-0.13.0/src/codeintel/commands/__init__.py +6 -0
- codecortex-0.13.0/src/codeintel/commands/_common.py +46 -0
- codecortex-0.13.0/src/codeintel/commands/doctor.py +15 -0
- codecortex-0.13.0/src/codeintel/commands/gen_token.py +9 -0
- codecortex-0.13.0/src/codeintel/commands/graph.py +31 -0
- codecortex-0.13.0/src/codeintel/commands/index.py +63 -0
- codecortex-0.13.0/src/codeintel/commands/install.py +77 -0
- codecortex-0.13.0/src/codeintel/commands/map.py +36 -0
- codecortex-0.13.0/src/codeintel/commands/query.py +55 -0
- codecortex-0.13.0/src/codeintel/commands/reset.py +27 -0
- codecortex-0.13.0/src/codeintel/commands/serve.py +10 -0
- codecortex-0.13.0/src/codeintel/commands/serve_http.py +20 -0
- codecortex-0.13.0/src/codeintel/commands/setup.py +21 -0
- codecortex-0.13.0/src/codeintel/commands/status.py +42 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/config.py +2 -12
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/doctor.py +8 -7
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/gateway.py +4 -5
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/http_server.py +6 -2
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/indexer.py +6 -7
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/installer.py +4 -5
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/mapper.py +10 -10
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/metrics.py +3 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/onboarding.py +6 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/policy.py +2 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/provider.py +5 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/providers/graph.py +27 -25
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/providers/lsp.py +27 -29
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/providers/semantic.py +3 -4
- codecortex-0.13.0/src/codeintel/py.typed +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/searcher.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/server.py +10 -6
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/term.py +3 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/verify.py +23 -21
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_chunking.py +3 -2
- codecortex-0.13.0/tests/test_cli_commands.py +773 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_doctor.py +1 -3
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_gateway.py +0 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_hardening.py +0 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_http_auth.py +3 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_http_server.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_lsp_real.py +2 -2
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_mapper.py +2 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_mcp_handshake.py +21 -24
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_mcp_server.py +36 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_never_raise.py +4 -8
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_rbac.py +0 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_reindexer.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_rerank.py +4 -4
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_semantic_provider.py +2 -10
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_treesitter.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_verify.py +0 -1
- codecortex-0.12.1/pyproject.toml +0 -62
- codecortex-0.12.1/src/codeintel/__init__.py +0 -1
- codecortex-0.12.1/src/codeintel/__main__.py +0 -577
- {codecortex-0.12.1 → codecortex-0.13.0}/LICENSE +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/setup.cfg +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codecortex.egg-info/entry_points.txt +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codecortex.egg-info/top_level.txt +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/grapher.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/injector.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/logconfig.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/providers/__init__.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/providers/none.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/reindexer.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/reset.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/semantic_db.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/viewer/__init__.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/src/codeintel/viewer/graph_template.html +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_cache.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_cli_help.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_config.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_e2e.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_engine_adoption.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_enterprise.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_graph_provider.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_graph_real.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_graph_stdin.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_grapher.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_installer.py +1 -1
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_integration.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_lsp_provider.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_model_dimension.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_onboarding.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_reset.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_term.py +0 -0
- {codecortex-0.12.1 → codecortex-0.13.0}/tests/test_verify_call.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -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 ::
|
|
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
|
|
@@ -414,7 +417,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
|
414
417
|
git clone https://github.com/hamilton-sky/codeintel.git
|
|
415
418
|
cd codeintel
|
|
416
419
|
pip install -e .[dev]
|
|
417
|
-
|
|
420
|
+
|
|
421
|
+
pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
|
|
422
|
+
ruff check src tests # lint
|
|
423
|
+
mypy # types (src/ only)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
|
|
427
|
+
installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
|
|
428
|
+
take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
|
|
429
|
+
desk and fails in CI. To see CI's shape before you push:
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
|
|
418
433
|
```
|
|
419
434
|
|
|
420
435
|
**Release gate.** The unit suite runs against the source tree, so it cannot see a packaging break, a
|
|
@@ -380,7 +380,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
|
380
380
|
git clone https://github.com/hamilton-sky/codeintel.git
|
|
381
381
|
cd codeintel
|
|
382
382
|
pip install -e .[dev]
|
|
383
|
-
|
|
383
|
+
|
|
384
|
+
pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
|
|
385
|
+
ruff check src tests # lint
|
|
386
|
+
mypy # types (src/ only)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
|
|
390
|
+
installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
|
|
391
|
+
take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
|
|
392
|
+
desk and fails in CI. To see CI's shape before you push:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
|
|
384
396
|
```
|
|
385
397
|
|
|
386
398
|
**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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -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 ::
|
|
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
|
|
@@ -414,7 +417,19 @@ docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
|
414
417
|
git clone https://github.com/hamilton-sky/codeintel.git
|
|
415
418
|
cd codeintel
|
|
416
419
|
pip install -e .[dev]
|
|
417
|
-
|
|
420
|
+
|
|
421
|
+
pytest tests/ -q # ~494 tests, ~35s; fails under 83% coverage
|
|
422
|
+
ruff check src tests # lint
|
|
423
|
+
mypy # types (src/ only)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Your local run is not CI's run.** A dev machine usually has `codebase-memory-mcp` and `uvx`
|
|
427
|
+
installed; CI has neither, so the live graph/LSP tests skip there *and* the never-raise envelopes
|
|
428
|
+
take different `reason`/`hint` paths. A bug reachable only on the no-backend path passes at your
|
|
429
|
+
desk and fails in CI. To see CI's shape before you push:
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
env PATH="$(dirname "$(which python)"):/usr/bin:/bin" pytest -q
|
|
418
433
|
```
|
|
419
434
|
|
|
420
435
|
**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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.13.0"
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import difflib
|
|
3
|
+
import sys
|
|
4
|
+
from importlib import import_module
|
|
5
|
+
|
|
6
|
+
from codeintel import __version__
|
|
7
|
+
|
|
8
|
+
# Commands grouped by what you are trying to DO. argparse lists them in declaration order with no
|
|
9
|
+
# grouping, which turns "what can this thing do?" into reading twelve lines to find the one verb you
|
|
10
|
+
# wanted. Each entry is (command, one-line description).
|
|
11
|
+
_COMMAND_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
|
|
12
|
+
("Understand your code", [
|
|
13
|
+
("query", "Ask one question — search, callers, callees, impact, chain, symbol, hotspots"),
|
|
14
|
+
("map", "Write CODE_INTEL.md — a committable architecture overview"),
|
|
15
|
+
("graph", "Interactive call-graph viewer (--html), or the graph as JSON"),
|
|
16
|
+
]),
|
|
17
|
+
("Set up", [
|
|
18
|
+
("setup", "Prepare backends + index this repo (--all does everything automatable)"),
|
|
19
|
+
("index", "Index a project for semantic search"),
|
|
20
|
+
("install", "Register codeintel with the AI agents installed on this machine"),
|
|
21
|
+
]),
|
|
22
|
+
("Check health", [
|
|
23
|
+
("doctor", "Per-engine health + index status, with the fix for each gap"),
|
|
24
|
+
("status", "Engine readiness and index age at a glance"),
|
|
25
|
+
("reset", "Clear the semantic index (recover from a corrupt or stale DB)"),
|
|
26
|
+
]),
|
|
27
|
+
("Run as a server", [
|
|
28
|
+
("serve", "Start the MCP server over stdio — what an agent host launches"),
|
|
29
|
+
("serve-http", "Start the HTTP transport (loopback only unless --allow-remote)"),
|
|
30
|
+
("gen-token", "Print a secure random bearer token for serve-http / RBAC"),
|
|
31
|
+
]),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
_COMMANDS = [name for _group, items in _COMMAND_GROUPS for name, _desc in items]
|
|
35
|
+
|
|
36
|
+
# Each command's body lives in codeintel.commands.<module> as `run(args) -> int`. The mapping is
|
|
37
|
+
# spelled out rather than derived from the command name so the target of any command is greppable,
|
|
38
|
+
# and the import happens at dispatch time so `codeintel serve` never pays for the semantic
|
|
39
|
+
# engine's imports (nor serve-http for the graph's).
|
|
40
|
+
_MODULES = {
|
|
41
|
+
"query": "query",
|
|
42
|
+
"map": "map",
|
|
43
|
+
"graph": "graph",
|
|
44
|
+
"setup": "setup",
|
|
45
|
+
"index": "index",
|
|
46
|
+
"install": "install",
|
|
47
|
+
"doctor": "doctor",
|
|
48
|
+
"status": "status",
|
|
49
|
+
"reset": "reset",
|
|
50
|
+
"serve": "serve",
|
|
51
|
+
"serve-http": "serve_http",
|
|
52
|
+
"gen-token": "gen_token",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_EXAMPLES = [
|
|
56
|
+
("codeintel setup --all .", "prepare backends and index this repo"),
|
|
57
|
+
("codeintel install", "register with the agents you have"),
|
|
58
|
+
("codeintel query --op callers --target my_function", "who calls it?"),
|
|
59
|
+
("codeintel doctor", "why is a query coming back empty?"),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def render_help() -> str:
|
|
64
|
+
"""The `codeintel` / `codeintel help` screen: grouped, colored, with real examples.
|
|
65
|
+
|
|
66
|
+
Color comes from codeintel.term, so it auto-degrades on a pipe, under NO_COLOR, and on a dumb
|
|
67
|
+
terminal — same as every other human-facing command."""
|
|
68
|
+
from codeintel.term import c
|
|
69
|
+
|
|
70
|
+
width = max(len(name) for name in _COMMANDS)
|
|
71
|
+
out = [
|
|
72
|
+
c.bold("codeintel") + c.dim(f" {__version__}")
|
|
73
|
+
+ c.dim(" — code intelligence for AI agents: graph + LSP + semantic search"),
|
|
74
|
+
"",
|
|
75
|
+
c.dim("usage: ") + "codeintel <command> [options]",
|
|
76
|
+
]
|
|
77
|
+
for group, items in _COMMAND_GROUPS:
|
|
78
|
+
out.append("")
|
|
79
|
+
out.append(" " + c.bold(group))
|
|
80
|
+
for name, desc in items:
|
|
81
|
+
out.append(" " + c.cyan(name.ljust(width)) + " " + desc)
|
|
82
|
+
|
|
83
|
+
out.append("")
|
|
84
|
+
out.append(" " + c.bold("Examples"))
|
|
85
|
+
# Width from the content, not a guess — a hardcoded column silently loses its gutter the moment
|
|
86
|
+
# one example grows past it, and ljust() will not pad below the string's own length.
|
|
87
|
+
cmd_width = max(len(cmd) for cmd, _why in _EXAMPLES) + 2
|
|
88
|
+
for cmd, why in _EXAMPLES:
|
|
89
|
+
out.append(" " + cmd.ljust(cmd_width) + c.dim("# " + why))
|
|
90
|
+
|
|
91
|
+
out.append("")
|
|
92
|
+
out.append(" " + c.dim("codeintel <command> --help") + " full options for one command")
|
|
93
|
+
out.append(" " + c.dim("docs: https://github.com/hamilton-sky/codeintel"))
|
|
94
|
+
return "\n".join(out)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _suggest(unknown: str) -> list[str]:
|
|
98
|
+
"""Commands a typo probably meant. Close matches first, then prefix matches — `gragh` should
|
|
99
|
+
land on `graph`, and a bare `serv` on both `serve` and `serve-http`."""
|
|
100
|
+
close = difflib.get_close_matches(unknown, _COMMANDS, n=3, cutoff=0.5)
|
|
101
|
+
prefix = [cmd for cmd in _COMMANDS if cmd.startswith(unknown) and cmd not in close]
|
|
102
|
+
return (close + prefix)[:3]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _unknown_command(name: str) -> int:
|
|
106
|
+
"""Report an unrecognized command with a way forward. argparse's own error dumps the full list
|
|
107
|
+
of choices and stops there, which is a dead end for a one-character typo."""
|
|
108
|
+
from codeintel.term import c_err as e
|
|
109
|
+
|
|
110
|
+
print(e.red(f"unknown command: {name!r}"), file=sys.stderr)
|
|
111
|
+
matches = _suggest(name)
|
|
112
|
+
if matches:
|
|
113
|
+
joined = " or ".join(e.cyan(m) for m in matches)
|
|
114
|
+
print(f"\n did you mean {joined}?", file=sys.stderr)
|
|
115
|
+
print("\n " + e.dim("run `codeintel help` to see every command"), file=sys.stderr)
|
|
116
|
+
return 2
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main() -> None:
|
|
120
|
+
parser = argparse.ArgumentParser(prog="codeintel")
|
|
121
|
+
parser.add_argument("--version", action="version", version=f"codeintel {__version__}")
|
|
122
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
123
|
+
|
|
124
|
+
# Shared flags for the human-facing (styled) commands.
|
|
125
|
+
color_parent = argparse.ArgumentParser(add_help=False)
|
|
126
|
+
color_parent.add_argument("--no-color", action="store_true", help="Disable ANSI color output")
|
|
127
|
+
color_parent.add_argument("--ascii", action="store_true", help="Use ASCII-only glyphs")
|
|
128
|
+
|
|
129
|
+
subparsers.add_parser("serve", help="Start the MCP server")
|
|
130
|
+
|
|
131
|
+
# index subcommand
|
|
132
|
+
index_parser = subparsers.add_parser("index", help="Index a project for semantic search")
|
|
133
|
+
index_parser.add_argument(
|
|
134
|
+
"project_root",
|
|
135
|
+
nargs="?",
|
|
136
|
+
default=None,
|
|
137
|
+
help="Project root directory (default: cwd)",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# query subcommand
|
|
141
|
+
query_parser = subparsers.add_parser("query", help="Query the code intelligence engine")
|
|
142
|
+
query_parser.add_argument("--op", required=True, help="Query operation (e.g. search, symbol)")
|
|
143
|
+
query_parser.add_argument("--target", required=True, help="Query target")
|
|
144
|
+
query_parser.add_argument("--engine", default="auto", help="Engine to use (default: auto)")
|
|
145
|
+
query_parser.add_argument(
|
|
146
|
+
"--project-root",
|
|
147
|
+
default=None,
|
|
148
|
+
help="Project root directory (default: cwd)",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# status subcommand
|
|
152
|
+
status_parser = subparsers.add_parser("status", help="Show code intelligence engine status")
|
|
153
|
+
status_parser.add_argument(
|
|
154
|
+
"project_root",
|
|
155
|
+
nargs="?",
|
|
156
|
+
default=None,
|
|
157
|
+
help="Project root directory (default: cwd)",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# serve-http subcommand
|
|
161
|
+
http_parser = subparsers.add_parser("serve-http", help="Start the HTTP transport server")
|
|
162
|
+
http_parser.add_argument("--port", type=int, default=8766, help="Port to listen on (default: 8766)")
|
|
163
|
+
http_parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
|
|
164
|
+
http_parser.add_argument("--allow-remote", action="store_true",
|
|
165
|
+
help="Permit binding a non-loopback host (use with --token, or the "
|
|
166
|
+
"endpoint is UNAUTHENTICATED)")
|
|
167
|
+
http_parser.add_argument("--token", default=None,
|
|
168
|
+
help="Require this bearer token on every request (or set "
|
|
169
|
+
"CODEINTEL_HTTP_TOKEN). Strongly recommended with --allow-remote.")
|
|
170
|
+
|
|
171
|
+
# install subcommand
|
|
172
|
+
install_parser = subparsers.add_parser("install", help="Register codeintel with AI agents")
|
|
173
|
+
install_parser.add_argument(
|
|
174
|
+
"--agent",
|
|
175
|
+
choices=["auto", "claude", "codex", "gemini", "zed", "all"],
|
|
176
|
+
default="auto",
|
|
177
|
+
help="Agent to register with (default: auto — only agents installed on this machine; "
|
|
178
|
+
"`all` forces every supported agent)",
|
|
179
|
+
)
|
|
180
|
+
install_parser.add_argument(
|
|
181
|
+
"--no-verify",
|
|
182
|
+
action="store_true",
|
|
183
|
+
help="Skip the post-registration MCP handshake (verification is on by default)",
|
|
184
|
+
)
|
|
185
|
+
install_parser.add_argument(
|
|
186
|
+
"--relative-command",
|
|
187
|
+
action="store_true",
|
|
188
|
+
help="Register the bare `codeintel` name instead of its absolute path (the absolute path "
|
|
189
|
+
"is the default because a GUI-launched agent does not inherit your shell's PATH)",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# map subcommand
|
|
193
|
+
map_parser = subparsers.add_parser("map", help="Generate CODE_INTEL.md orientation file")
|
|
194
|
+
map_parser.add_argument("project_root", nargs="?", default=None)
|
|
195
|
+
map_parser.add_argument("--inject", action="store_true", help="Inject reference block into CLAUDE.md/AGENTS.md")
|
|
196
|
+
map_parser.add_argument("--budget", type=int, default=32768, help="Byte budget for CODE_INTEL.md (default: 32768)")
|
|
197
|
+
|
|
198
|
+
# graph subcommand — interactive call-graph view (HTML) or the raw {nodes,edges} JSON
|
|
199
|
+
graph_parser = subparsers.add_parser(
|
|
200
|
+
"graph", help="Build an interactive call-graph view (--html) or emit the graph as JSON — "
|
|
201
|
+
"works on any indexed repo")
|
|
202
|
+
graph_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
|
|
203
|
+
graph_parser.add_argument("--html", action="store_true",
|
|
204
|
+
help="Write a self-contained interactive HTML viewer (default: print JSON)")
|
|
205
|
+
graph_parser.add_argument("--out", default=None, help="Output path for --html (default: codeintel-graph.html)")
|
|
206
|
+
graph_parser.add_argument("--limit", type=int, default=220, help="Max call edges to include (default: 220)")
|
|
207
|
+
|
|
208
|
+
# doctor subcommand
|
|
209
|
+
doctor_parser = subparsers.add_parser("doctor", parents=[color_parent],
|
|
210
|
+
help="Diagnose engine health + index status for a repo")
|
|
211
|
+
doctor_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
|
|
212
|
+
doctor_parser.add_argument("--deep", action="store_true",
|
|
213
|
+
help="Also boot-check serena (slower; first boot pulls it via uvx)")
|
|
214
|
+
doctor_parser.add_argument("--json", action="store_true",
|
|
215
|
+
help="Emit the structured JSON report instead of the table")
|
|
216
|
+
|
|
217
|
+
# setup subcommand
|
|
218
|
+
setup_parser = subparsers.add_parser("setup", parents=[color_parent],
|
|
219
|
+
help="Prepare backends and optionally index this repo")
|
|
220
|
+
setup_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
|
|
221
|
+
setup_parser.add_argument("--all", action="store_true", dest="all_steps",
|
|
222
|
+
help="One-command setup: do everything automatable (uv + deps + index + "
|
|
223
|
+
"warm serena). Idempotent — skips what's already installed.")
|
|
224
|
+
setup_parser.add_argument("--install-uv", action="store_true",
|
|
225
|
+
help="Run `pip install uv` (provides uvx for the LSP engine)")
|
|
226
|
+
setup_parser.add_argument("--install-deps", action="store_true",
|
|
227
|
+
help="Run `pip install -e .` (semantic engine deps)")
|
|
228
|
+
setup_parser.add_argument("--index", action="store_true",
|
|
229
|
+
help="Index this repo now (first run downloads the ~50MB model)")
|
|
230
|
+
setup_parser.add_argument("--warm", action="store_true", help="Boot serena now (first run pulls it via uvx; slow)")
|
|
231
|
+
setup_parser.add_argument("--json", action="store_true", help="Emit the structured JSON report")
|
|
232
|
+
|
|
233
|
+
# reset subcommand
|
|
234
|
+
reset_parser = subparsers.add_parser("reset", parents=[color_parent],
|
|
235
|
+
help="Clear the semantic index (recover from a corrupt/stale DB)")
|
|
236
|
+
reset_parser.add_argument("project_root", nargs="?", default=None, help="Project root (default: cwd)")
|
|
237
|
+
reset_parser.add_argument("--all", action="store_true",
|
|
238
|
+
help="Clear the ENTIRE index (all projects), not just this repo")
|
|
239
|
+
reset_parser.add_argument("--yes", "-y", action="store_true", help="Skip the confirmation prompt")
|
|
240
|
+
reset_parser.add_argument("--json", action="store_true", help="Emit the structured JSON report")
|
|
241
|
+
|
|
242
|
+
subparsers.add_parser("gen-token", help="Print a secure random bearer token (for serve-http / RBAC auth.toml)")
|
|
243
|
+
subparsers.add_parser("help", help="Show every command, grouped, with examples")
|
|
244
|
+
|
|
245
|
+
# Intercept an unrecognized command BEFORE argparse, whose error prints the full choice list and
|
|
246
|
+
# stops — a dead end for a one-character typo. Only a bare word is claimed here; anything
|
|
247
|
+
# starting with `-` (--version, --help) still goes to argparse.
|
|
248
|
+
argv = sys.argv[1:]
|
|
249
|
+
if argv and not argv[0].startswith("-") and argv[0] not in [*_COMMANDS, "help"]:
|
|
250
|
+
sys.exit(_unknown_command(argv[0]))
|
|
251
|
+
if not argv or argv[0] == "help":
|
|
252
|
+
from codeintel import term
|
|
253
|
+
term.configure(no_color=False, ascii_mode=None)
|
|
254
|
+
print(render_help())
|
|
255
|
+
sys.exit(0)
|
|
256
|
+
|
|
257
|
+
args = parser.parse_args()
|
|
258
|
+
|
|
259
|
+
from codeintel import term
|
|
260
|
+
term.configure(
|
|
261
|
+
no_color=getattr(args, "no_color", False),
|
|
262
|
+
ascii_mode=(True if getattr(args, "ascii", False) else None),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
module = _MODULES.get(args.command)
|
|
266
|
+
if module is None: # unreachable via argparse; a bare `codeintel` is handled above
|
|
267
|
+
print(render_help())
|
|
268
|
+
sys.exit(0)
|
|
269
|
+
sys.exit(import_module(f"codeintel.commands.{module}").run(args))
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
main()
|
|
@@ -25,7 +25,6 @@ import logging
|
|
|
25
25
|
import os
|
|
26
26
|
import pathlib
|
|
27
27
|
import tomllib # stdlib on Python 3.11+ (the project's minimum)
|
|
28
|
-
from typing import Dict, List, Optional
|
|
29
28
|
|
|
30
29
|
from codeintel.policy import TieringPolicy
|
|
31
30
|
|
|
@@ -39,7 +38,7 @@ class TokenAuth:
|
|
|
39
38
|
"""Resolves a bearer token to a role and builds the matching op policy. Tokens are held as
|
|
40
39
|
sha256 hashes; a presented token is hashed and looked up in O(1). Immutable; thread-safe."""
|
|
41
40
|
|
|
42
|
-
def __init__(self, token_hash_to_role:
|
|
41
|
+
def __init__(self, token_hash_to_role: dict[str, str], role_ops: dict[str, list[str]]) -> None:
|
|
43
42
|
self._tokens = token_hash_to_role
|
|
44
43
|
self._role_ops = role_ops
|
|
45
44
|
|
|
@@ -47,7 +46,7 @@ class TokenAuth:
|
|
|
47
46
|
def enabled(self) -> bool:
|
|
48
47
|
return bool(self._tokens)
|
|
49
48
|
|
|
50
|
-
def role_for(self, presented_token: str) ->
|
|
49
|
+
def role_for(self, presented_token: str) -> str | None:
|
|
51
50
|
"""The role for a presented token, or None if it isn't a known token."""
|
|
52
51
|
if not presented_token:
|
|
53
52
|
return None
|
|
@@ -58,7 +57,7 @@ class TokenAuth:
|
|
|
58
57
|
"""A role with ops ``["*"]`` (or none) is unrestricted → omit it from the rules, since
|
|
59
58
|
TieringPolicy treats a role absent from its rules as full-access. Every other role maps to
|
|
60
59
|
its explicit op allowlist (an empty list = deny all ops, a fail-safe default)."""
|
|
61
|
-
rules:
|
|
60
|
+
rules: dict[str, list[str]] = {}
|
|
62
61
|
for role, ops in self._role_ops.items():
|
|
63
62
|
if _ALL in ops:
|
|
64
63
|
continue # unrestricted
|
|
@@ -66,7 +65,7 @@ class TokenAuth:
|
|
|
66
65
|
return TieringPolicy(enabled=bool(rules), rules=rules)
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
def _auth_config_path() ->
|
|
68
|
+
def _auth_config_path() -> pathlib.Path | None:
|
|
70
69
|
env = os.environ.get("CODEINTEL_AUTH_CONFIG", "").strip()
|
|
71
70
|
if env:
|
|
72
71
|
p = pathlib.Path(env)
|
|
@@ -88,8 +87,9 @@ def load_auth() -> TokenAuth:
|
|
|
88
87
|
logger.warning("auth: %s could not be parsed (%s) — RBAC is OFF", path, type(exc).__name__)
|
|
89
88
|
return TokenAuth({}, {})
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
roles_section = data.get("roles")
|
|
91
|
+
raw_roles: dict = roles_section if isinstance(roles_section, dict) else {}
|
|
92
|
+
role_ops: dict[str, list[str]] = {}
|
|
93
93
|
for role, ops in raw_roles.items():
|
|
94
94
|
if isinstance(ops, list):
|
|
95
95
|
role_ops[str(role)] = [str(o) for o in ops]
|
|
@@ -99,8 +99,9 @@ def load_auth() -> TokenAuth:
|
|
|
99
99
|
logger.warning("auth: role %r ops must be a list (got %r) — denying all ops for it", role, ops)
|
|
100
100
|
role_ops[str(role)] = []
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
tokens_section = data.get("tokens")
|
|
103
|
+
raw_tokens: dict = tokens_section if isinstance(tokens_section, dict) else {}
|
|
104
|
+
token_hash_to_role: dict[str, str] = {}
|
|
104
105
|
for tok, role in raw_tokens.items():
|
|
105
106
|
role = str(role)
|
|
106
107
|
# A token mapped to an undefined role fails safe: define it as deny-all (empty op list).
|