edr

Coding agents burn context on full-file reads, redundant calls, and output they've already seen. edr fixes this three ways:
- Symbol-level ops — read one function instead of a 400-line file. 85% smaller.
- Batching — read three files and search in one call instead of four.
- Deltas — re-read a file, re-run tests? Only what changed comes back.
Works with any agent that can run shell commands. Fully local, no telemetry.
Example
Add a retries parameter to a scheduler class. Without edr: 6 calls, ~59KB of context. With edr:
# 1. Symbol-level: read three signatures + search, one call
edr -r src/scheduler.py:Scheduler --sig \
-r src/config.py:parse_config \
-r src/worker.py:Worker --sig \
-s "retry"
# 2. Batched edit: two files, auto-verifies build
edr -e src/scheduler.py --old "def run(self):" --new "def run(self, retries=3):" \
-e src/config.py --old '"timeout": 30' --new '"timeout": 30, "retries": 3'
# 3. Deltas: run tests, fix a bug, run again — only the diff comes back
edr run -- pytest
# → [no changes, 80 lines] or just the diff of what changed
3 calls. Re-reads of files already in context: zero tokens.
Install
brew install jordw/tap/edr
edr setup
edr setup indexes your project, adds .edr/ to .gitignore, and installs agent instructions to your global config (~/.claude/CLAUDE.md, ~/.cursor/rules/edr.mdc, or ~/.codex/AGENTS.md). Instructions auto-update when edr is rebuilt. They teach the agent to use edr instead of built-in file tools.
Other install methods
Pre-built binary (review the script first):
curl -fsSL https://raw.githubusercontent.com/jordw/edr/main/install.sh | sh
From source (requires Go 1.25+ and a C/C++ compiler for tree-sitter):
CGO_ENABLED=1 go install github.com/jordw/edr@latest
edr setup
CGO_ENABLED=1 is required — tree-sitter grammars are C libraries. You need gcc and g++ installed.
Cloud agents and CI (installs Go/gcc if needed, builds, indexes):
git clone https://github.com/jordw/edr.git && ./edr/setup.sh
The index lives in .edr/ at the repo root and rebuilds automatically if deleted.
How it works
edr parses your codebase with tree-sitter and stores symbols in a SQLite index. This gives agents three capabilities they don't have with raw file tools:
Symbol-level operations. Read one function instead of a 400-line file. Get a class API with --signatures (85% fewer tokens). Add a method with --inside ClassName without reading the file. Scope edits to a symbol with --in Symbol to avoid false matches. Use --fuzzy for whitespace-tolerant matching. Edits re-index immediately and auto-verify the build (Go, Node, Rust, Make).
Batching. -r, -s, -e, -w combine reads, searches, edits, and writes in one CLI call. One call to gather context, one to apply mutations.
Deltas for everything. edr tracks what the agent has already seen — files, symbols, search results, command output — and only shows what changed. Second read of an unchanged file: 0 tokens. edr run -- make test after a fix: only the diff from the previous run, unchanged lines collapsed. Same principle for builds, linters, any command. Zero config — sessions activate automatically.
Commands
Batch flags — the primary interface:
edr -r file[:Symbol] # Read file or symbol
edr -r file:Class --sig # Signatures only (no bodies)
edr -s "pattern" # Search symbols or text (--text)
edr -e file --old "x" --new "y" # Edit with auto re-index + verify
edr -w file --inside Class # Add method/field without reading
Combine freely — one call to gather, one to mutate:
edr -r src/config.go:parseConfig --sig -r src/main.go:Server -s "handleRequest"
edr -e src/config.go --old "old" --new "new" -w src/new_test.go --content "..."
Standalone commands:
| Command |
Example |
read |
edr read file:Symbol, --signatures, --lines 10:50 |
search |
edr search "pattern" --text, --in file:Symbol, --regex |
map |
edr map, edr map --dir src/ --type function --lang go --grep pat |
edit |
edr edit file --old "x" --new "y", --fuzzy, --in Symbol, --delete |
write |
edr write file --inside Class --content "...", --after Symbol, --append |
refs |
edr refs Symbol, --impact, --callers, --deps, --chain target |
rename |
edr rename old new --dry-run |
verify |
edr verify, edr verify --level test |
run |
edr run -- make test — diffs against previous run |
session |
edr session new |
setup |
edr setup, edr setup --force |
reindex |
edr reindex |
Output uses plain mode: one JSON header line followed by raw-text body.
Languages
edr reads and edits any text file. Symbol-aware features (symbol reads, --signatures, refs, map) require a supported language:
Symbol indexing: Go, Python, JavaScript/JSX, TypeScript/TSX, Rust, Java, C, C++, Ruby, PHP, Zig, Lua, Bash/Shell, C#, Kotlin
Import-aware refs: Go, Python, JavaScript, TypeScript (others fall back to text matching)
Limitations
- Tree-sitter, not LSP. Fast, no build step, works on broken code, zero config. The tradeoff: no type information. Refs use import-path matching, not type resolution, so cross-package references may produce false positives. For agent workloads — read, edit, search — structural parsing is enough.
- macOS and Linux only. Windows is not planned.
- C/C++ compiler required when building from source (tree-sitter grammars). Homebrew and the install script use pre-built binaries.
- First index: 1-3s on small repos, ~30s on large ones (vitess, 1.5M LOC). Incremental re-index after edits: ~50ms/file. Subsequent reads/searches add ~5-20ms of overhead vs raw
cat/grep for tree-sitter parsing and SQLite lookup.
Benchmarks
We run 9 scenarios (read a symbol, find refs, orient in codebase, edit a function, etc.) against real repos and measure tool response bytes — the raw amount of text that enters the agent's context window.
The baseline uses the tools agents actually have: whole-file cat, grep -rn, find + read. No symbol extraction, no batching. edr uses symbol reads, --signatures, refs, map, and batch flags.
Median reduction: 93% across repos. edr loses on plain text search (structured JSON adds overhead vs raw grep — see breakdown below), but wins everywhere else. Call counts are summed across all 9 scenarios; each edr scenario is 1 call.
Per-scenario breakdown (urfave/cli)
| Scenario |
Baseline |
edr |
Reduction |
| Understand a class API |
10,072B (whole file) |
1,592B (--signatures) |
84% |
| Read a specific function |
15,307B (whole file) |
1,463B (symbol read) |
90% |
| Find references |
67,101B / 4 calls |
865B / 1 call (refs) |
99% |
| Search with context |
614B (grep -C3) |
1,812B (structured) |
-195% |
| Orient in codebase |
11,481B / 2 calls |
2,235B / 1 call (map) |
81% |
| Edit a function |
10,172B / 2 calls |
680B / 1 call (batch) |
93% |
| Add method to a class |
10,072B / 2 calls |
184B / 1 call (--inside) |
98% |
| Multi-file read |
30,794B / 3 calls |
2,606B / 1 call (batched) |
92% |
| Explore a symbol |
41,285B / 4 calls |
4,562B / 1 call |
89% |
| Total |
196,898B / 20 calls |
15,999B / 9 calls |
92% |
Scenarios and methodology in bench/scenarios/. Reproduce: bash bench/run_real_repo_benchmarks.sh (~10 min).
Contributing
See CONTRIBUTING.md. Bug reports and PRs welcome on GitHub.
License
MIT