edr - less context, faster agents

Agents are bottlenecked on context, not inference speed. edr gives them the right context instead of all the context:
- Smaller reads. Read one function instead of the file around it. Or get just a class API: signatures without implementation.
- Fewer calls. Batch reads, searches, edits, and writes into one round-trip.
- Only changed output. Re-reads and re-runs return diffs, not repeated output. Zero config.
Works with any agent that can run shell commands. Fully local, no telemetry.
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.24+ 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.
Example
Read a function, find its callers, edit it:
# Without edr: grep to find it, read the range, grep for callers, read each caller
# 5+ tool calls, ~25KB of context
# With edr:
edr read src/scheduler.py:run # just the function (not the file)
edr refs run --callers # callers, import-resolved
edr edit src/scheduler.py \
--old "def run(self):" \
--new "def run(self, retries=3):" # auto re-indexes, verifies build
3 calls, ~3KB of context.
Batched: gather everything in one call, mutate in one call:
# 1. Read three APIs + 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. 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. Re-run tests - only the diff comes back
edr delta -- pytest
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. Large files (>200 lines) auto-skeleton on first read, showing structure instead of content. Get a class API with --signatures (85% fewer tokens). --expand includes dep signatures inline. Add a method with --inside ClassName without reading the file. Scope edits to a symbol with --in Symbol. Use --read-back to get updated context in the edit response. edr prepare Symbol returns body, callers, deps, tests, and hash in one call. Edits re-index immediately and auto-verify the build (Go, Node, Rust, Make).
Batching. -r, -s, -m, -e, -w combine reads, searches, maps, edits, and writes in one CLI call. One call to gather context, one to apply mutations.
Sessions. 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 delta -- 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. -r read, -s search, -m map, -e edit, -w write. Modifier flags follow each op. Plan what you need, then combine into one call:
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 -m --dir src/ # Symbol map of a directory
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 context, one to mutate:
edr -r f.go --sig -r g.go:Func --expand -s "pattern" --text -m --dir cmd/
edr -r f.go:Sym -e f.go --old "x" --new "y" -r f.go:Sym # post-edit read
edr -e f.go --old "a" --new "b" -e g.go --old "c" --new "d"
edr -w f.go --content "..." --mkdir
Batch modifiers
Read (after -r): --sig, --skeleton, --full, --expand[=deps], --symbols, --lines 10:50, --budget N
Search (after -s): --text, --regex, --context N, --in f.go:Sym, --include "*.go", --limit N
Map (after -m): --dir, --lang, --grep, --glob, --type, --budget N
Edit (after -e): --old "x" --new "y", --where Sym (resolves file), --in Sym (scope), --all, --delete, --dry-run, --fuzzy, --read-back, @file for metacharacters
Write (after -w): --content "...", --inside Class, --after Sym, --append, --mkdir
Verify: -V (auto after edits). --command "cmd", --level test
Standalone commands
| Command |
Example |
map |
edr map, edr map --dir src/ --type function --lang go --grep pat |
prepare |
edr prepare Symbol — body, callers, deps, tests, hash in one call |
refs |
edr refs Symbol, --impact, --callers, --deps, --chain target |
rename |
edr rename Old New --dry-run, edr rename --text "old" "new" --word --include "*.go" |
verify |
edr verify, edr verify --test, edr verify --command "cmd" |
delta |
edr delta -- make test — shows only what changed. --reset, --full |
status |
edr status, edr status --focus "goal" |
undo |
edr undo — revert the last edit/write (auto-checkpointed) |
reset |
edr reset, --index, --session |
setup |
edr setup, edr setup --force |
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, Swift, Scala, Zig, Lua, Bash/Shell, C#, Kotlin
Import-aware refs: Go, Python, JavaScript, TypeScript, Java, Kotlin, Scala, C#, PHP, Swift (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: under 1s on small repos, ~15s on large ones (vitess, 3200 files). Incremental re-index after edits: ~12ms/file.
Benchmarks
9 scenarios (read a symbol, find refs, orient in codebase, edit a function, etc.) against real repos. We measure tool response bytes, the raw text entering the agent's context window.
The baseline models a skilled agent using Claude Code's built-in tools: Grep to find symbols before reading, Read with line ranges around grep matches (not whole files), Edit/Write confirmations. Orient and multi-file read use whole-file reads (there's no shortcut for understanding a module or reading a file). edr uses symbol reads, --signatures, refs, map, and batch flags.
Median reduction: 83%. edr loses on plain text search (structured JSON adds overhead vs raw grep), but wins everywhere else. Biggest gains on structured operations (refs, map, signatures). 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 |
13,019B (whole file) |
1,486B (--signatures) |
89% |
| Read a specific function |
3,026B / 2 calls (grep + range read) |
1,182B (symbol read) |
61% |
| Find references |
9,086B / 4 calls (grep + 3 range reads) |
179B (refs) |
98% |
| Search with context |
614B (grep -C3) |
1,027B (structured) |
-67% |
| Orient in codebase |
52,470B / 4 calls (glob + 3 reads) |
393B (map) |
99% |
| Edit a function |
1,403B / 3 calls (grep + range + edit) |
394B (batch) |
72% |
| Add method to a class |
5,393B / 3 calls (grep + range + write) |
249B (--inside) |
95% |
| Multi-file read |
39,397B / 3 calls |
22,028B (batched) |
44% |
| Explore a symbol |
25,006B / 4 calls (grep + 3 range reads) |
555B |
98% |
| Total |
149,414B / 25 calls |
27,493B / 9 calls |
82% |
Scenarios and methodology in bench/scenarios/. Reproduce: bash bench/run_real_repo_benchmarks.sh (~10 min). Regenerate tables: bash bench/gen_readme_table.sh.
Contributing
See CONTRIBUTING.md. Bug reports and PRs welcome on GitHub.
License
MIT