README
¶
adit-code
Finds the files in your codebase that are structurally expensive to work with — for humans and AI agents alike — and tells you exactly what to fix.
wc -l tells you a file is big. adit tells you it has 51 parameters on one
function, nesting 16 levels deep, and 8 ambiguous names that pollute every
grep. Each finding maps to a specific refactoring action.
An adit is a horizontal tunnel driven into a hillside to provide access to a mine. adit-code tunnels into your codebase to find the structural hot spots.
What It Reports
$ adit score --pretty .
adit v0.1.0
File Lines Size Nest Reads Unneeded Noise Blast
────────────────────────────────────────────────────────────────────
engine.py 3200 D 8 12 4 8 8
handlers.py 890 B 5 7 3 2 3
utils.py 420 A 3 2 0 0 15
Co-locate (single-consumer imports):
TRUST_HINTS (const) constants.py:15 -> handlers.py
Ambiguous names:
_validate 3 defs: engine.py:42 handlers.py:88 auth.py:15
Uncommented (large files, 0 comments — AI agents benefit from comments):
engine.py (3200 lines)
Import cycles: none
Every finding is actionable:
| Metric | What to do |
|---|---|
| Lines (Grade D/F) | Split this file |
| Max Nesting (>6) | Extract nested logic into helpers |
| Max Params (>10) | Use a config object |
| Grep Noise | Rename _validate — 5 other files define the same name |
| Unnecessary Reads | Move X from constants.py to handlers.py — only consumer |
| Import Cycles | Break the A→B→A cycle |
| Blast Radius | 30 files import from here — verify edits carefully |
| No Comments | Add comments — agents benefit from intent documentation |
Validated Against Real Agent Behavior
Correlated against SWE-bench agent trajectories — 1,840 files across 49 repos, 10,000 agent runs. File size is the strongest predictor of agent tool call cost (median Spearman +0.474, positive on all 49 repos). The structural metrics tell you what specifically to fix:
| Metric | Median per-repo | Positive repos |
|---|---|---|
| Lines | +0.474 | 49/49 |
| Max Nesting | +0.344 | 48/49 |
| Max Params | +0.311 | 46/49 |
| Grep Noise | +0.241 | 38/49 |
| Blast Radius | +0.165 | 40/49 |
| Unnecessary Reads | +0.135 | 39/49 |
File size does most of the predictive work. The structural metrics don't
predict better — they tell you why a file is expensive and what to
change. wc -l says "this file is 3,200 lines." adit says "split it,
and while you're at it, that function with 15 parameters needs a config
object and those 8 levels of nesting need extracting."
Install
go install github.com/justinstimatze/adit-code/cmd/adit@latest
Or build from source:
git clone https://github.com/justinstimatze/adit-code.git
cd adit-code
go build ./cmd/adit
Single binary. Analyzes Python, TypeScript, and Go with one tool — no need for separate linters per language. Uses tree-sitter for parsing. No runtime dependencies.
Some of what adit checks (nesting depth, parameter count) overlaps with
language-specific linters like pylint or eslint. adit's value is the
cross-language consistency, the co-location and grep noise metrics that
no linter checks, and the --diff mode for CI regression gates.
Quick Start
adit score . # JSON (default — for CI and AI tools)
adit score --pretty . # Human-readable table
adit score --diff HEAD~1 --pretty . # What changed and what got worse
adit enforce . # CI gate — exit 1 if thresholds exceeded
adit enforce --diff HEAD~1 . # Only enforce on changed files
adit mcp # MCP server for Claude Code / Codex
What It Measures
Five metrics. No composite score — each answers a different question.
Context Reads — How many files must the AI read to understand this file?
Flags single-consumer imports (names imported by only one file) that should
be co-located. Each unnecessary import is a wasted Read tool call.
Grep Noise — How much search noise surrounds this file? Counts ambiguous
names this file defines or imports that are also defined elsewhere. _validate
in 5 files = 4 extra grep results to wade through.
File Size — How many reads and re-reads does this file cost? AI tools read files in chunks, not all at once. Larger files require more reads and the AI loses coherence across distant methods. Grades A (<500 lines) through F (5000+) based on empirically validated thresholds.
Blast Radius — How many files reference this one? High-blast files are central definitions that get read often for context.
Import Cycles — Circular dependencies that waste AI context (A reads B, B reads A, loop).
Configuration
Create adit.toml in your project root:
[thresholds]
max_file_lines = 3000
max_context_reads = 10
max_unnecessary_reads = 3
max_ambiguity_defs = 3
max_blast_radius = 20
max_cycle_length = 0 # 0 = no cycles allowed
[scan]
languages = ["python", "typescript"]
exclude = ["test_*.py", "*.test.ts", "**/__pycache__/**", "**/node_modules/**"]
# Looser thresholds for specific paths
[per-path."test_*.py"]
max_file_lines = 5000
Also reads [tool.adit] from pyproject.toml. Config is auto-discovered by
walking up from the target path.
AI Agent Integration
MCP Server (Claude Code, Codex, any MCP-compatible agent)
{
"mcpServers": {
"adit": { "command": "adit", "args": ["mcp"] }
}
}
7 tools: adit_score_repo, adit_score_file, adit_relocatable,
adit_ambiguous, adit_blast_radius, adit_cycles, adit_diff.
CLI + JSON (any agent that shells out)
JSON-default output. Any agent that can run commands and parse JSON works:
adit score . # full analysis
adit enforce --diff $REF # PR gate
CLAUDE.md / AGENTS.md
Add to your project's AI instruction file:
## Structural Health
This project uses adit for code structure analysis.
- Before committing: run `adit enforce .` — fix violations, don't skip.
- When adit reports relocatable imports: move the definition to the consumer file.
- When adit reports ambiguous names: rename to be more distinctive.
Pre-commit Hook
repos:
- repo: local
hooks:
- id: adit
name: adit enforce
entry: adit enforce
language: system
types_or: [python, ts, tsx, go]
CLI Reference
adit score [PATHS...] # JSON (default)
adit score --pretty [PATHS...] # Human table
adit score --diff REF [PATHS...] # Compare against git ref
adit score --min-grade C . # Only files at grade C or worse
adit score --sort blast . # Sort: blast|reads|noise|size|name
adit score --quiet . # Metrics only, no recommendations
adit score --silent . # No output, exit code only
adit enforce [PATHS...] # Exit 1 if thresholds exceeded
adit enforce --diff REF . # Only enforce on changed files
adit enforce --silent . # Exit code only
adit mcp # MCP server (stdio)
Exit codes: 0 = pass, 1 = issues found, 2 = tool error.
Performance
Fast on most repos: ~150 files/sec. Cross-file analysis (co-location, blast radius, grep noise) is O(n²) and dominates on large repos:
| Repo size | Time |
|---|---|
| <200 files | <1s |
| ~500 files | ~3s |
| ~1,000 files | ~15s |
| ~4,500 files (Sentry) | ~5 min |
Deeper Validation
Beyond the headline correlations, session-level analysis reveals:
File size effect — AI tool calls by file size grade:
| Grade | Avg Tool Calls | vs Grade A |
|---|---|---|
| A (<500 lines) | 41 | 1x |
| B (500-1500) | 204 | 5x |
| C (1500-3000) | 2,059 | 50x |
| F (5000+) | 6,710 | 163x |
Re-reads per session — The AI re-reads large files repeatedly within a single session. Avg reads-per-session by grade: A=5.8, B=13.2, C=29.6, F=54.0.
Edit success rate — Files with more imports have higher re-edit rates (r=+0.437). Re-edit rates by grade: A=59%, B=77%, D=87%, F=89%. More coupled files are harder to get right on the first try.
Across open source projects
adit scored against 33 open source projects across Python, TypeScript, and Go. Generated files (migrations, protobuf, etc.) are auto-detected and excluded.
Python
| Project | Files | A | B | C | D | F | Nest | Params |
|---|---|---|---|---|---|---|---|---|
| Sentry | 4,226 | 3,932 | 268 | 21 | 4 | 1 | 13 | 29 |
| PyTorch | 2,142 | 1,608 | 375 | 104 | 33 | 22 | 11 | 53 |
| yt-dlp | 1,125 | 1,056 | 55 | 10 | 3 | 1 | 11 | 15 |
| CPython stdlib | 1,110 | 903 | 147 | 45 | 9 | 6 | 11 | 26 |
| SymPy | 923 | 640 | 189 | 67 | 22 | 5 | 13 | 22 |
| Salt | 910 | 646 | 198 | 45 | 15 | 6 | 16 | 51 |
| Django | 853 | 774 | 65 | 13 | 1 | - | 11 | 25 |
| OpenStack Nova | 776 | 681 | 64 | 23 | 5 | 3 | 10 | 47 |
| Ansible | 577 | 498 | 68 | 10 | 1 | - | 12 | 25 |
| pandas | 452 | 333 | 74 | 26 | 13 | 6 | 10 | 35 |
| scikit-learn | 383 | 239 | 108 | 29 | 6 | 1 | 9 | 24 |
| Odoo | 344 | 292 | 39 | 10 | 2 | 1 | 12 | 15 |
| Scrapy | 177 | 165 | 12 | - | - | - | 8 | 15 |
| Celery | 161 | 138 | 21 | 2 | - | - | 11 | 30 |
| matplotlib | 156 | 87 | 38 | 21 | 8 | 2 | 9 | 24 |
| FastAPI | 48 | 41 | 4 | 1 | 2 | - | 11 | 38 |
| Flask | 24 | 17 | 6 | 1 | - | - | 7 | 10 |
TypeScript
| Project | Files | A | B | C | D | F | Nest | Params |
|---|---|---|---|---|---|---|---|---|
| TypeScript compiler | 709 | 557 | 96 | 31 | 12 | 13 | 18 | 26 |
| TypeORM | 491 | 441 | 34 | 8 | 6 | 2 | 19 | 4 |
| Prisma client | 422 | 414 | 7 | - | - | 1 | 8 | 6 |
| Hono | 208 | 192 | 14 | 2 | - | - | 13 | 6 |
| Deno std path | 98 | 97 | 1 | - | - | - | 21 | 4 |
| tRPC server | 82 | 77 | 5 | - | - | - | 8 | 3 |
| React Router | 68 | 53 | 10 | 3 | 1 | 1 | 9 | 21 |
Go
| Project | Files | A | B | C | D | F | Nest | Params |
|---|---|---|---|---|---|---|---|---|
| CockroachDB sql | 2,231 | 1,826 | 325 | 52 | 16 | 12 | 13 | 22 |
| Kubernetes pkg | 1,980 | 1,804 | 148 | 24 | 2 | 2 | 9 | 40 |
| Terraform | 1,201 | 1,087 | 105 | 7 | 2 | - | 11 | 8 |
| Docker daemon | 944 | 866 | 74 | 4 | - | - | 10 | 9 |
| GitHub CLI | 389 | 366 | 22 | 1 | - | - | 8 | 10 |
| geppetto | 220 | 206 | 14 | - | - | - | 11 | 8 |
| glazed | 210 | 197 | 13 | - | - | - | 9 | 6 |
| TiDB executor | 193 | 128 | 53 | 9 | 2 | 1 | 11 | 16 |
| go-go-mcp | 144 | 137 | 7 | - | - | - | 9 | 5 |
Key findings:
- Salt has the worst parameter complexity (51 params) and deepest nesting (16 levels). PyTorch follows with 53 max params. Nova has 47.
- TypeScript compiler and TypeORM have the deepest nesting in TS (18-19). Deno std reaches 21 despite tiny file sizes.
- Go projects have lower max params (5-40) and moderate nesting (8-13). CockroachDB is the exception with 12 Grade F files.
Reproduce on your own repos:
adit score --pretty /path/to/project
License
MIT. All dependencies MIT or BSD-2. See INFLUENCES.md for full citation record, prior art search, and license audit.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
adit
command
|
|
|
adit-validate
command
adit-validate: Correlate adit metrics with git history to validate that the metrics predict actual editing cost.
|
adit-validate: Correlate adit metrics with git history to validate that the metrics predict actual editing cost. |
|
internal
|
|