Alpheus
A river through the stables. He sweeps the agent's debris out of the working tree before you commit.
Alpheus (Ἀλφειός) is the river god whose torrent Heracles diverted through the Augean stables, washing thirty years of dung out to sea in a single afternoon. The stables were not filthy because anyone wanted them that way. Nobody had got round to it.
- TypeScript
- Bun
- Ink
- Claude Code
- hooks
GitHub (opens in a new tab)README (opens in a new tab)Changelog (opens in a new tab)
What he does
Alpheus reads what the working tree has changed and names the parts that were never meant to ship. A debug print added to trace a fault. A // @ts-ignore that made a type error go away. Four lines of dead code left commented out in case they come back. A path into somebody's home directory. A temp.json nobody deleted.
None of that is deliberate. An agent fixing one bug across eight files leaves a print in all eight, the suite turns green, git commit -am takes the lot, and the print ships. A diff can tell you those lines were added. It cannot tell you which of them you meant.
He parses the additions in the staged and the unstaged diff, and checks untracked files against scratch heuristics. Additions only: code that was already in the file is not his business, so a console.log that has sat in the repository for a year stays exactly where it is.
Detection is deterministic. Patterns per language, comment classification, and a language map keyed on the file extension. No model, no tokens, no network request, and the same working tree answers the same way twice.
The five classes
| Class | What he catches |
|---|---|
LOG | Debug output added to watch execution: console.log/debug/dir/time, Python print, breakpoint, pdb.set_trace, logging.debug, Rust dbg! and println!, Go fmt.Print* and log.Print*, PHP var_dump, print_r, dd, shell set -x and echo "DEBUG:..." |
SUPPRESS | A checker told to be quiet: @ts-ignore, @ts-expect-error, @ts-nocheck, eslint-disable, biome-ignore, prettier-ignore, # noqa, # type: ignore, #[allow(...)], //nolint, @phpstan-ignore, @psalm-suppress |
TOMBSTONE | Three or more consecutive commented-out lines that still read as code |
PATH | A hardcoded home directory: /home/<user>/, /Users/<user>/, C:\Users\<user>\ |
SCRATCH | An untracked leftover: *.tmp, *.scratch and *.bak anywhere, plus temp.*, scratch.*, dump.json, debug.log and throwaway scripts like t.py at the repository root or under scratch/ and tmp/ |
Seven languages are mapped by extension: TypeScript, JavaScript, Python, Rust, Go, PHP and shell. A file in anything else is still read for workstation paths and dead code, because neither of those needs to know what language it is looking at.
Reviewing before you purge
alpheus on its own opens a split pane: the findings on the left, the surrounding source on the right with the line to be removed marked where it sits. ↑/↓ or j/k move, space toggles one finding, a toggles all of them, enter purges what is selected, and q or escape leaves without touching a file.
There is a non-interactive half for the cases where nobody is watching. alpheus check exits 1 when it finds anything, which is what makes it usable in CI or a git hook, and --json hands the same findings to something that will do the reading. alpheus clean purges the lot in one go, and --dry-run says what that would have removed without removing it.
Undoing it
Before a single line is removed, every affected file is copied into .alpheus/backups/<timestamp>_<id>/ alongside a manifest.json that records the sha256 of each file as it was. alpheus restore puts the tree back byte for byte, and alpheus backups lists what there is to go back to. A deleted scratch file is copied out first as well, so it comes back too.
Two details matter more than they look. .alpheus/ is appended to .git/info/exclude rather than to your .gitignore, so the snapshots never appear in git status and never turn up in a commit of their own, and the repository you share with other people does not grow a line about a tool only you run. And edits are applied bottom-up by line number, so removing one line never moves the line the next finding points at.
In Claude Code
Alpheus is a plugin as well as a CLI. /alpheus runs the scan and hands the findings to the agent to present. A PreToolUse hook watches Bash calls for a git commit and, when the tree still holds findings, names the first five of them before the commit runs.
That hook warns and stops there. The commit still goes through, and what it prints lands in your transcript rather than in the model's context, so it is a note to you and not an instruction to Claude.
Quick start
Needs Bun 1.1 or newer and git 2.25 or newer.
claude plugin marketplace add https://aranea-development.nl/plugins/marketplace.json
claude plugin install alpheus@araneaHooks bind when a session starts, so start a new session after installing.
If the install stops at ssh: connect to host github.com port 22, git is cloning the plugin from its GitHub repository over SSH and cannot reach GitHub that way on that machine. The message points at access rights, but the repository is public and the transport is what failed. One line switches git to HTTPS, then install again:
git config --global --add url."https://github.com/".insteadOf "git@github.com:"The CLI stands on its own, with or without Claude Code:
bun install -g github:AraneaDev/alpheusWhat he does not do
He does not judge. Rust's println! is how a program writes its output as well as how somebody traces a fault, and Alpheus flags both. So does a print in a Python script whose whole job is to print. That is the reason the review pane exists: the tool finds candidates, and which of them are debris is yours to say.
He never commits and never stages. He cleans a working tree and stops there, and the decision to commit stays with you.
He makes no network request, sends no telemetry and calls no model. Everything he knows, he read off your disk.
Alpheus is pre-release. The version you install is the one on the releases page, and the shape of the findings is still free to change.