claude-context-compiler 0.1.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.
- claude_context_compiler-0.1.0/.claude/settings.local.json +25 -0
- claude_context_compiler-0.1.0/CLAUDE.md +140 -0
- claude_context_compiler-0.1.0/PKG-INFO +184 -0
- claude_context_compiler-0.1.0/README.md +149 -0
- claude_context_compiler-0.1.0/SRS-context-compiler.md +219 -0
- claude_context_compiler-0.1.0/SUC-context-compiler.md +473 -0
- claude_context_compiler-0.1.0/openspec/specs/classifier/spec.md +70 -0
- claude_context_compiler-0.1.0/openspec/specs/entry-node-matching/spec.md +100 -0
- claude_context_compiler-0.1.0/openspec/specs/indexer/spec.md +86 -0
- claude_context_compiler-0.1.0/openspec/specs/mcp-server/spec.md +142 -0
- claude_context_compiler-0.1.0/openspec/specs/rationale/spec.md +118 -0
- claude_context_compiler-0.1.0/openspec/specs/scorer/spec.md +111 -0
- claude_context_compiler-0.1.0/openspec/specs/traversal/spec.md +88 -0
- claude_context_compiler-0.1.0/pyproject.toml +76 -0
- claude_context_compiler-0.1.0/src/context_compiler/__init__.py +3 -0
- claude_context_compiler-0.1.0/src/context_compiler/cli.py +108 -0
- claude_context_compiler-0.1.0/src/context_compiler/indexer/__init__.py +0 -0
- claude_context_compiler-0.1.0/src/context_compiler/indexer/graph.py +95 -0
- claude_context_compiler-0.1.0/src/context_compiler/indexer/indexer.py +232 -0
- claude_context_compiler-0.1.0/src/context_compiler/indexer/parser.py +519 -0
- claude_context_compiler-0.1.0/src/context_compiler/models.py +65 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/__init__.py +0 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/classifier.py +114 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/entry_nodes.py +242 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/rationale.py +111 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/scorer.py +171 -0
- claude_context_compiler-0.1.0/src/context_compiler/retrieval/traversal.py +271 -0
- claude_context_compiler-0.1.0/src/context_compiler/server/__init__.py +0 -0
- claude_context_compiler-0.1.0/src/context_compiler/server/mcp_server.py +178 -0
- claude_context_compiler-0.1.0/tests/__init__.py +0 -0
- claude_context_compiler-0.1.0/tests/conftest.py +9 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/config/__init__.py +0 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/config/mqtt_settings.py +7 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/mqtt/__init__.py +0 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/mqtt/dispatcher.py +19 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/mqtt/retry_handler.py +24 -0
- claude_context_compiler-0.1.0/tests/fixtures/python_repo/tests/test_retry.py +26 -0
- claude_context_compiler-0.1.0/tests/fixtures/typescript_repo/src/auth.test.ts +23 -0
- claude_context_compiler-0.1.0/tests/fixtures/typescript_repo/src/auth.ts +19 -0
- claude_context_compiler-0.1.0/tests/fixtures/typescript_repo/src/tokenStore.ts +15 -0
- claude_context_compiler-0.1.0/tests/test_classifier.py +137 -0
- claude_context_compiler-0.1.0/tests/test_entry_nodes.py +191 -0
- claude_context_compiler-0.1.0/tests/test_indexer.py +245 -0
- claude_context_compiler-0.1.0/tests/test_mcp_server.py +182 -0
- claude_context_compiler-0.1.0/tests/test_scorer_rationale.py +230 -0
- claude_context_compiler-0.1.0/tests/test_traversal.py +170 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:claude.ai)",
|
|
5
|
+
"WebSearch",
|
|
6
|
+
"WebFetch(domain:github.com)",
|
|
7
|
+
"WebFetch(domain:aider.chat)",
|
|
8
|
+
"WebFetch(domain:arxiv.org)",
|
|
9
|
+
"WebFetch(domain:openspec.dev)",
|
|
10
|
+
"Bash(uv run:*)",
|
|
11
|
+
"Bash(python3:*)",
|
|
12
|
+
"Bash(tail:*)",
|
|
13
|
+
"Bash(/Users/punakkals/work/context-compiler/.venv/bin/pip install:*)",
|
|
14
|
+
"Bash(.venv/bin/pip install:*)",
|
|
15
|
+
"Bash(.venv/bin/pytest:*)",
|
|
16
|
+
"Bash(.venv/bin/python:*)",
|
|
17
|
+
"Bash(echo EXIT: $?:*)",
|
|
18
|
+
"Bash(.venv/bin/context-compiler explain:*)",
|
|
19
|
+
"Bash(ls:*)",
|
|
20
|
+
"Bash(head:*)",
|
|
21
|
+
"Bash(/Users/punakkals/work/context-compiler/.venv/bin/python:*)",
|
|
22
|
+
"Bash(claude mcp:*)"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# context-compiler
|
|
2
|
+
|
|
3
|
+
A local-first MCP server that indexes Python and TypeScript repositories into a dependency graph and returns the smallest correct context bundle for a given coding task — with a one-line rationale for every included file.
|
|
4
|
+
|
|
5
|
+
## What this project does
|
|
6
|
+
|
|
7
|
+
- Parses `.py`, `.ts`, `.tsx` files using tree-sitter into a KuzuDB graph
|
|
8
|
+
- Classifies a task string (`"fix the retry logic"`) into BUG_FIX / NEW_FEATURE / REFACTOR using keyword scoring — no LLM calls
|
|
9
|
+
- Finds entry nodes using BM25 over tokenised symbol names + file paths (fuzzy + optional semantic fallback)
|
|
10
|
+
- Traverses the graph with a strategy tuned per task type
|
|
11
|
+
- Scores candidates and compiles a token-budget-aware bundle
|
|
12
|
+
- Returns the bundle with a rationale string per file and an excluded list with reasons
|
|
13
|
+
- Exposes everything as an MCP server over stdio (no HTTP, no port)
|
|
14
|
+
|
|
15
|
+
## Spec-driven development
|
|
16
|
+
|
|
17
|
+
All component specs live in `openspec/specs/`. Read the relevant spec before implementing any component.
|
|
18
|
+
|
|
19
|
+
| Component | Spec | Source requirements |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| Indexer | `openspec/specs/indexer/spec.md` | FR-01, UC-01 |
|
|
22
|
+
| Classifier | `openspec/specs/classifier/spec.md` | FR-02, UC-08 |
|
|
23
|
+
| Entry node matching | `openspec/specs/entry-node-matching/spec.md` | FR-03.1 (hard problem) |
|
|
24
|
+
| Traversal | `openspec/specs/traversal/spec.md` | FR-03, UC-02/03/04 |
|
|
25
|
+
| Scorer | `openspec/specs/scorer/spec.md` | FR-04, UC-07 |
|
|
26
|
+
| Rationale | `openspec/specs/rationale/spec.md` | FR-05, UC-06 |
|
|
27
|
+
| MCP server | `openspec/specs/mcp-server/spec.md` | FR-06, UC-09/10 |
|
|
28
|
+
|
|
29
|
+
Full requirements: `SRS-context-compiler.md`
|
|
30
|
+
Full use cases: `SUC-context-compiler.md`
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/context_compiler/
|
|
36
|
+
├── indexer/
|
|
37
|
+
│ ├── parser.py # tree-sitter extraction (Python + TypeScript)
|
|
38
|
+
│ └── graph.py # KuzuDB schema + read/write
|
|
39
|
+
├── retrieval/
|
|
40
|
+
│ ├── classifier.py # keyword scorer → TaskType + confidence
|
|
41
|
+
│ ├── entry_nodes.py # BM25 + fuzzy + optional fastembed
|
|
42
|
+
│ ├── traversal.py # BFS per task type
|
|
43
|
+
│ ├── scorer.py # composite scoring + budget enforcement
|
|
44
|
+
│ └── rationale.py # template-based rationale generation
|
|
45
|
+
├── server/
|
|
46
|
+
│ └── mcp_server.py # FastMCP stdio server (get_context, refresh)
|
|
47
|
+
├── cli.py # index + explain + serve CLI entry points
|
|
48
|
+
└── models.py # Pydantic v2 models (ContextBundle, RefreshResult, Node, Edge)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Key design decisions
|
|
52
|
+
|
|
53
|
+
**No LLM calls in the critical path.** Classification, traversal, scoring, and rationale are all deterministic. The optional fastembed model is a static local ONNX model — not an API call.
|
|
54
|
+
|
|
55
|
+
**stdio transport, not HTTP.** The MCP server communicates via stdin/stdout. No port is opened. Claude Code spawns it as a subprocess.
|
|
56
|
+
|
|
57
|
+
**Multi-entry BFS.** Entry node matching returns top-K candidates (default K=5), not top-1. BFS runs from all K nodes and results are merged. Nodes reachable from multiple entry points score higher.
|
|
58
|
+
|
|
59
|
+
**Fastembed is optional.** Install with `pip install context-compiler[semantic]` to enable embedding-based semantic fallback for entry node matching. Base install uses BM25 + fuzzy only.
|
|
60
|
+
|
|
61
|
+
**Hard token ceiling.** The bundle never exceeds the token budget (`CC_TOKEN_BUDGET` env var or `budget` parameter). Partial file inclusion is not permitted.
|
|
62
|
+
|
|
63
|
+
**Deterministic output.** Same repo state + same task = byte-identical bundle. Score ties broken by file path (lexicographic ascending).
|
|
64
|
+
|
|
65
|
+
## Non-negotiable constraints
|
|
66
|
+
|
|
67
|
+
- Source code, graph data, and task strings MUST NOT leave the machine
|
|
68
|
+
- No LLM API calls in the classification, traversal, scoring, or rationale steps
|
|
69
|
+
- The MCP server MUST NOT crash on bad input, missing graph, or parse errors — always return a structured response
|
|
70
|
+
- The bundle MUST NEVER exceed the token budget
|
|
71
|
+
|
|
72
|
+
## Tech stack
|
|
73
|
+
|
|
74
|
+
| Concern | Library | Why |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| AST parsing | `tree-sitter`, `tree-sitter-python`, `tree-sitter-typescript` | Production-grade, no language server needed |
|
|
77
|
+
| Graph storage | `kuzu` | Embedded, no server process, Python bindings |
|
|
78
|
+
| BM25 retrieval | `rank-bm25` | Pure Python, zero native deps |
|
|
79
|
+
| Fuzzy matching | `rapidfuzz` | C extension, fast, widely available |
|
|
80
|
+
| Semantic fallback | `fastembed` (optional) | ONNX runtime, no PyTorch, 23MB model |
|
|
81
|
+
| MCP server | `fastmcp` | Anthropic-maintained, stdio support |
|
|
82
|
+
| CLI | `click` | Standard, well-tested |
|
|
83
|
+
| Models | `pydantic` v2 | Native FastMCP support, JSON Schema export |
|
|
84
|
+
| Packaging | `uv` / `pyproject.toml` | Supports `uvx context-compiler` invocation |
|
|
85
|
+
|
|
86
|
+
## CLI commands
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Index a repository
|
|
90
|
+
uvx context-compiler index --repo ./my-project
|
|
91
|
+
|
|
92
|
+
# Inspect what bundle a task would produce
|
|
93
|
+
uvx context-compiler explain --repo ./my-project --task "fix the retry logic"
|
|
94
|
+
|
|
95
|
+
# Start the MCP server (Claude Code spawns this automatically)
|
|
96
|
+
uvx context-compiler serve --repo ./my-project
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Environment variables
|
|
100
|
+
|
|
101
|
+
| Variable | Default | Description |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `CC_TOKEN_BUDGET` | `8000` | Default token budget for get_context |
|
|
104
|
+
| `CC_REPO_PATH` | required | Repository path (overridden by --repo flag) |
|
|
105
|
+
|
|
106
|
+
## Performance targets
|
|
107
|
+
|
|
108
|
+
| Operation | Target |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `get_context` response | ≤ 500ms p95 |
|
|
111
|
+
| Initial index (10k files) | ≤ 120 seconds |
|
|
112
|
+
| Entry node matching (BM25) | ≤ 50ms |
|
|
113
|
+
| Server startup | ≤ 3 seconds |
|
|
114
|
+
|
|
115
|
+
## Graph data model
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Node
|
|
119
|
+
id: string # "{file_path}::{symbol_name}"
|
|
120
|
+
file_path: string
|
|
121
|
+
symbol_name: string | null
|
|
122
|
+
symbol_type: FILE | FUNCTION | CLASS | METHOD
|
|
123
|
+
line_start: int
|
|
124
|
+
line_end: int
|
|
125
|
+
token_count: int # ceil(char_count / 4)
|
|
126
|
+
last_modified: timestamp
|
|
127
|
+
language: PYTHON | TYPESCRIPT
|
|
128
|
+
|
|
129
|
+
Edge
|
|
130
|
+
source_id: string → Node.id
|
|
131
|
+
target_id: string → Node.id
|
|
132
|
+
edge_type: CALLS | IMPORTS | COVERS | DEFINED_IN
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Before implementing any component
|
|
136
|
+
|
|
137
|
+
1. Read the relevant spec in `openspec/specs/<component>/spec.md`
|
|
138
|
+
2. Check all scenarios pass — they are the acceptance criteria
|
|
139
|
+
3. Do not add features not in the spec
|
|
140
|
+
4. Do not make LLM API calls from any non-server component
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: claude-context-compiler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local context compiler for AI coding assistants — smallest correct context bundle with rationale
|
|
5
|
+
Project-URL: Repository, https://github.com/punakkals/context-compiler
|
|
6
|
+
Author: Punakkals
|
|
7
|
+
License: Apache-2.0
|
|
8
|
+
Keywords: claude,code-intelligence,context,llm,mcp,tree-sitter
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: click>=8.0
|
|
19
|
+
Requires-Dist: fastmcp>=2.0
|
|
20
|
+
Requires-Dist: kuzu>=0.7
|
|
21
|
+
Requires-Dist: pydantic>=2.0
|
|
22
|
+
Requires-Dist: rank-bm25>=0.2.2
|
|
23
|
+
Requires-Dist: rapidfuzz>=3.0
|
|
24
|
+
Requires-Dist: tree-sitter-python>=0.23
|
|
25
|
+
Requires-Dist: tree-sitter-typescript>=0.23
|
|
26
|
+
Requires-Dist: tree-sitter>=0.23
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: datamodel-code-generator>=0.25; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Provides-Extra: semantic
|
|
33
|
+
Requires-Dist: fastembed>=0.4; extra == 'semantic'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# context-compiler
|
|
37
|
+
|
|
38
|
+
A local-first MCP server that indexes your Python and TypeScript codebase into a dependency graph and returns the **smallest correct context bundle** for any coding task — with a one-line rationale for every included file.
|
|
39
|
+
|
|
40
|
+
No cloud. No LLM API calls. No data leaves your machine.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## The problem
|
|
45
|
+
|
|
46
|
+
When you ask Claude to fix a bug or add a feature, it reads files by guessing which ones are relevant. It over-reads (wastes tokens) or misses the file that actually matters. The bigger the codebase, the worse this gets.
|
|
47
|
+
|
|
48
|
+
## How it works
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Your task: "fix the keycloak token expiry"
|
|
52
|
+
↓
|
|
53
|
+
Classify → BUG_FIX
|
|
54
|
+
↓
|
|
55
|
+
Find entry nodes → keycloak.py (BM25 + docstring matching)
|
|
56
|
+
↓
|
|
57
|
+
Traverse graph → keycloak.py + secured_view.py + test_keycloak_steps.py
|
|
58
|
+
↓
|
|
59
|
+
Score + budget → 870 tokens (within 8000 limit)
|
|
60
|
+
↓
|
|
61
|
+
Return bundle with rationale per file
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Everything — classification, traversal, scoring, rationale — is deterministic. Same repo + same task = same bundle, every time.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Index a repository
|
|
72
|
+
uvx context-compiler index --repo ./my-project
|
|
73
|
+
|
|
74
|
+
# Preview what context a task would produce
|
|
75
|
+
uvx context-compiler explain --repo ./my-project --task "fix the retry logic"
|
|
76
|
+
|
|
77
|
+
# Start the MCP server (Claude Code does this automatically)
|
|
78
|
+
uvx context-compiler serve --repo ./my-project
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Requires Python 3.11+.
|
|
82
|
+
|
|
83
|
+
### Optional: semantic fallback
|
|
84
|
+
|
|
85
|
+
Install the optional fastembed model (23MB ONNX, no PyTorch) for better matching when task terms don't appear in symbol names:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install "context-compiler[semantic]"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Claude Code integration
|
|
94
|
+
|
|
95
|
+
**1. Register the MCP server:**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
claude mcp add --scope user context-compiler uvx -- context-compiler serve --repo /path/to/your/repo
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**2. Add to your repo's `CLAUDE.md`:**
|
|
102
|
+
|
|
103
|
+
```markdown
|
|
104
|
+
## Context retrieval
|
|
105
|
+
|
|
106
|
+
Before reading any source files, call `get_context` with the task description.
|
|
107
|
+
Read only the files it returns.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**3. Use it:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
> Fix the keycloak token expiry bug
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Claude calls `get_context("fix the keycloak token expiry bug")`, gets back the exact files to read, and starts working — no guessing.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## MCP tools
|
|
121
|
+
|
|
122
|
+
### `get_context(task, budget=8000)`
|
|
123
|
+
|
|
124
|
+
Returns the minimal file bundle for a coding task.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"files": ["admin/keycloak.py", "admin/views/secured_view.py"],
|
|
129
|
+
"rationale": [
|
|
130
|
+
"Included Keycloak as primary task location (matched 'keycloak')",
|
|
131
|
+
"Included SecuredView._has_role because it is called by Keycloak (depth 1)"
|
|
132
|
+
],
|
|
133
|
+
"token_estimate": 870,
|
|
134
|
+
"tokens_saved": 0,
|
|
135
|
+
"task_type": "BUG_FIX",
|
|
136
|
+
"confidence": 1.0
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `refresh(changed_files)`
|
|
141
|
+
|
|
142
|
+
Re-indexes the repository after file changes.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## What makes it different
|
|
147
|
+
|
|
148
|
+
**Task-type-aware traversal.** A bug fix traverses inbound callers and test coverage at depth 2. A new feature traverses imports and sibling modules. A refactor traverses everything at depth 3. No other tool adjusts retrieval strategy based on what you're actually trying to do.
|
|
149
|
+
|
|
150
|
+
**Rationale per file.** Every included file has a one-line explanation of why it's there. You can see what Claude will read before it reads it.
|
|
151
|
+
|
|
152
|
+
**Hard token budget.** The bundle never exceeds the limit. Partial file inclusion is not permitted.
|
|
153
|
+
|
|
154
|
+
**Local-first.** Embedded KuzuDB graph, no server, no port, no auth. Works offline.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Supported languages
|
|
159
|
+
|
|
160
|
+
| Language | Parsing | Docstrings |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| Python | tree-sitter-python | ✓ (first line of docstring) |
|
|
163
|
+
| TypeScript / TSX | tree-sitter-typescript | ✓ (JSDoc `/** */`) |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Environment variables
|
|
168
|
+
|
|
169
|
+
| Variable | Default | Description |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `CC_REPO_PATH` | required | Path to indexed repository |
|
|
172
|
+
| `CC_TOKEN_BUDGET` | `8000` | Default token budget for `get_context` |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Tech stack
|
|
177
|
+
|
|
178
|
+
[tree-sitter](https://tree-sitter.github.io/) · [KuzuDB](https://kuzudb.com/) · [BM25 (rank-bm25)](https://github.com/dorianbrown/rank_bm25) · [rapidfuzz](https://github.com/maxbachmann/RapidFuzz) · [FastMCP](https://github.com/jlowin/fastmcp) · [fastembed](https://github.com/qdrant/fastembed) (optional)
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
Apache 2.0
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# context-compiler
|
|
2
|
+
|
|
3
|
+
A local-first MCP server that indexes your Python and TypeScript codebase into a dependency graph and returns the **smallest correct context bundle** for any coding task — with a one-line rationale for every included file.
|
|
4
|
+
|
|
5
|
+
No cloud. No LLM API calls. No data leaves your machine.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The problem
|
|
10
|
+
|
|
11
|
+
When you ask Claude to fix a bug or add a feature, it reads files by guessing which ones are relevant. It over-reads (wastes tokens) or misses the file that actually matters. The bigger the codebase, the worse this gets.
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Your task: "fix the keycloak token expiry"
|
|
17
|
+
↓
|
|
18
|
+
Classify → BUG_FIX
|
|
19
|
+
↓
|
|
20
|
+
Find entry nodes → keycloak.py (BM25 + docstring matching)
|
|
21
|
+
↓
|
|
22
|
+
Traverse graph → keycloak.py + secured_view.py + test_keycloak_steps.py
|
|
23
|
+
↓
|
|
24
|
+
Score + budget → 870 tokens (within 8000 limit)
|
|
25
|
+
↓
|
|
26
|
+
Return bundle with rationale per file
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Everything — classification, traversal, scoring, rationale — is deterministic. Same repo + same task = same bundle, every time.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Index a repository
|
|
37
|
+
uvx context-compiler index --repo ./my-project
|
|
38
|
+
|
|
39
|
+
# Preview what context a task would produce
|
|
40
|
+
uvx context-compiler explain --repo ./my-project --task "fix the retry logic"
|
|
41
|
+
|
|
42
|
+
# Start the MCP server (Claude Code does this automatically)
|
|
43
|
+
uvx context-compiler serve --repo ./my-project
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Requires Python 3.11+.
|
|
47
|
+
|
|
48
|
+
### Optional: semantic fallback
|
|
49
|
+
|
|
50
|
+
Install the optional fastembed model (23MB ONNX, no PyTorch) for better matching when task terms don't appear in symbol names:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "context-compiler[semantic]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Claude Code integration
|
|
59
|
+
|
|
60
|
+
**1. Register the MCP server:**
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
claude mcp add --scope user context-compiler uvx -- context-compiler serve --repo /path/to/your/repo
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**2. Add to your repo's `CLAUDE.md`:**
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
## Context retrieval
|
|
70
|
+
|
|
71
|
+
Before reading any source files, call `get_context` with the task description.
|
|
72
|
+
Read only the files it returns.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**3. Use it:**
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
> Fix the keycloak token expiry bug
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Claude calls `get_context("fix the keycloak token expiry bug")`, gets back the exact files to read, and starts working — no guessing.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## MCP tools
|
|
86
|
+
|
|
87
|
+
### `get_context(task, budget=8000)`
|
|
88
|
+
|
|
89
|
+
Returns the minimal file bundle for a coding task.
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"files": ["admin/keycloak.py", "admin/views/secured_view.py"],
|
|
94
|
+
"rationale": [
|
|
95
|
+
"Included Keycloak as primary task location (matched 'keycloak')",
|
|
96
|
+
"Included SecuredView._has_role because it is called by Keycloak (depth 1)"
|
|
97
|
+
],
|
|
98
|
+
"token_estimate": 870,
|
|
99
|
+
"tokens_saved": 0,
|
|
100
|
+
"task_type": "BUG_FIX",
|
|
101
|
+
"confidence": 1.0
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `refresh(changed_files)`
|
|
106
|
+
|
|
107
|
+
Re-indexes the repository after file changes.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What makes it different
|
|
112
|
+
|
|
113
|
+
**Task-type-aware traversal.** A bug fix traverses inbound callers and test coverage at depth 2. A new feature traverses imports and sibling modules. A refactor traverses everything at depth 3. No other tool adjusts retrieval strategy based on what you're actually trying to do.
|
|
114
|
+
|
|
115
|
+
**Rationale per file.** Every included file has a one-line explanation of why it's there. You can see what Claude will read before it reads it.
|
|
116
|
+
|
|
117
|
+
**Hard token budget.** The bundle never exceeds the limit. Partial file inclusion is not permitted.
|
|
118
|
+
|
|
119
|
+
**Local-first.** Embedded KuzuDB graph, no server, no port, no auth. Works offline.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Supported languages
|
|
124
|
+
|
|
125
|
+
| Language | Parsing | Docstrings |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| Python | tree-sitter-python | ✓ (first line of docstring) |
|
|
128
|
+
| TypeScript / TSX | tree-sitter-typescript | ✓ (JSDoc `/** */`) |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Environment variables
|
|
133
|
+
|
|
134
|
+
| Variable | Default | Description |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `CC_REPO_PATH` | required | Path to indexed repository |
|
|
137
|
+
| `CC_TOKEN_BUDGET` | `8000` | Default token budget for `get_context` |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Tech stack
|
|
142
|
+
|
|
143
|
+
[tree-sitter](https://tree-sitter.github.io/) · [KuzuDB](https://kuzudb.com/) · [BM25 (rank-bm25)](https://github.com/dorianbrown/rank_bm25) · [rapidfuzz](https://github.com/maxbachmann/RapidFuzz) · [FastMCP](https://github.com/jlowin/fastmcp) · [fastembed](https://github.com/qdrant/fastembed) (optional)
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
Apache 2.0
|