Your disk is full of things a rebuild would bring back. target/,
node_modules/, .venv/, Gradle caches, stale git worktrees β tens to
hundreds of gigabytes of it. Deleting it by hand is a bespoke survey of your
machine plus a small gamble that nothing precious is inside.
reaper does the survey and removes the gamble. It scans your tree, classifies every directory as reclaimable or refused-with-a-reason, and reclaims only what it can prove is safe β from a keyboard TUI or a JSON CLI.
reaper is for agents, by agents. Coding agents fill your disk faster than you ever did β and reaper is the tool they call to clean up after themselves, safely (the contract). Works just as well for humans with a keyboard.
Let your agents cook. Let reaper reap.
macOS / Linux
curl -fsSL https://raw.githubusercontent.com/benelser/reaper/main/install.sh | shWindows (PowerShell)
irm https://raw.githubusercontent.com/benelser/reaper/main/install.ps1 | iexOne line, on your PATH, done β and staying current is reaper update
(checksum-verified, swaps the binary in place). Both installers and the
updater run end-to-end in CI on every push and against every published
release. From source: cargo install --git https://github.com/benelser/reaper reaper.
reaper # interactive: browse this project's bloat, reap what you mark
reaper ~/code # interactive: sweep everything under ~/code
reaper update # get the newest reaper β verified, swapped in place
reaper scan ~ --format json # script it: every verdict as JSON, deletes nothing
reaper scan ~ --ecosystem rust --min-size 1G # narrow to big Rust targets
reaper reap --plan sha256:β¦ --execute # delete exactly what that scan showed β nothing else
reaper undo sha256:β¦ # print the commands that regenerate what you reapedBuilding on top of reaper β or letting an agent drive it? The full typed contract (schemas, refusal codes, exit codes, invariants) is in AGENTS.md.
In the TUI: space marks, a marks everything reapable, x opens the
confirm, a typed y commits, / filters, q quits. Deletion is permanent β
that's why the space comes back instantly β and refused rows can't be marked.
Most cleanup tools assume a directory is safe unless something looks wrong. reaper inverts the heuristic β nothing is safe until everything is proven:
- Only the regenerable is ever a candidate. A directory enters the list
only by matching a rule that knows how it comes back β
cargo build,npm install, a git branch that survives the worktree. Unique data is never on the table to begin with. - Safe is proven, not assumed. Every candidate must affirmatively pass every check: git state clean and pushed, nobody using it, no build writing into it, on the same disk, not a cloud placeholder, old enough that you've moved on. One failed check refuses. One unanswerable check β a permission error, an unreadable repo β also refuses: doubt counts as danger.
- The proof is re-taken at the moment of deletion. Between "you looked" and "it deletes," trees change. reaper re-verifies identity and liveness per directory right before removal, and if anything shifted, it refuses and asks you to re-scan.
That's the whole philosophy. The refusal reasons you see on screen β
dirty(14), unpushed(3), building, locked, fresh(0d<3d) β are just
rule 2 talking. And there is no --force flag to argue with it.
Measured, not vibes β the performance suite runs on every merge:
Traversal engine vs jwalk (a fast parallel walker) |
1.2β1.4Γ faster, on macOS, Linux, and Windows |
| Matching a build dir vs walking into it | 13β28Γ faster β a 15 GB target/ is one candidate, zero descents |
| Real dev tree: 105k dirs, ~1.4M files, 256 candidates | surveyed and fully sized (430 GB) in about a minute |
| Perceived delete | instant β the path vanishes atomically; space drains back in the background |
Under the hood: native bulk directory syscalls per OS, a work-stealing parallel walker, no subprocesses.
27 rules out of the box: Rust, Node (+ .next/.turbo/.nuxt), Python
(venvs, __pycache__, .tox, .mypy_cache, .pytest_cache), Gradle,
Maven, .NET, Go, Xcode, CocoaPods, Zig, Dart, Elixir, PHP, git
worktrees, and a CACHEDIR.TAG catch-all
that finds caches nobody wrote a rule for β on the first real machine it ran
on, it surfaced 59 GB from a build system no rule had named. Every rule is a data row in
rules.toml, so adding an ecosystem is an
edit, not a fork.
Status: v0.1 alpha. The scan β plan β reap β recover loop works and is tested on macOS, Linux, and Windows. Treat
--executewith the respect a permanent delete deserves.
MIT
