Your disk fills up.
An agent opens by itself and fixes it.
A watcher checks free space every 15 minutes. Below the trigger it opens a workspace, starts Claude Code in it, and hands it one job: get you back above the goal, then close itself. Above the trigger it costs one statfs call.
$ bun install && bun linkthe premise
Deleting is easy. Deciding is not.
What a script gets wrong
target/ in a repo you build weekly comes back for free. target/ in a repo you archived two years ago is pure garbage. To du they are the same two letters and a slash.
How the work splits
The watcher decides when — cheap, deterministic, a syscall and an if. The agent decides what — contextual, and it stops and asks when it does not know.
the whole api
One command. It answers its own question.
There is no status subcommand to remember, because “is this thing on?” is answered by the command you already typed. The two thresholds live inside the launchd plist’s own arguments — no config file, nothing to drift out of sync with the thing that actually runs.
the guardrail
It will not delete your files.
The safe list is what a build regenerates: node_modules, target, .venv, build caches. Applied in slices, largest first, re-measuring between each. Outside that list it deletes nothing without asking — an 8 GB .dmg and an 8 GB source video look identical from here.
Always --physical on APFS
An OrbStack disk image reports 8.8 TB logical on a 256 GB SSD. Chase the logical number and you delete the wrong thing.
Uncommitted work is not garbage
node_modules/ comes back with a build. An untracked file comes back with nothing. git status --porcelain first.
what actually broke
Three failure modes, measured.
command not found: herdrlaunchd starts with a minimal PATH. The first real trigger died on it. The plist now freezes the activating shell's PATH.
agent_pane_busyA pane exists before its shell reaches a prompt. Starting the agent now polls instead of sleeping a guessed amount.
the watcher dying of successA finished workspace that fails to close gates every future check, forever. --check now reaps a done workspace and carries on.