Keeps an Arch machine clean, with your agent doing the deciding.
Coding agents leave worktrees, build output, caches and transcripts behind. keeper finds what is safe to remove, proves it, and moves it to a trash you can undo.
Install
paru -S keeper-bin # standalone binary
paru -S keeper # Bun source package
npm i -g @cillianb/keeper # same binary from npm, x64 and arm64
The AUR has both packages, and npm ships the same binary for x64 and arm64. To run it once without installing, use npx @cillianb/keeper scan. Install it before keeper timer install, so the schedule points at a path that stays put.
keeper needs git and pacman-contrib. gh adds pull request proof, expac sizes orphans, notify-send delivers notifications and uv prunes its cache. keeper status lists any that are missing. Removing packages and running paccache use sudo -n, so they need passwordless sudo for those commands.
Set up with your agent
npx skills add cjber/keeper # skill for any agent that reads Agent Skills
The skill is at skills/keeper/SKILL.md. The source package also installs it to /usr/lib/keeper/skills, and the binary package to /usr/share/doc/keeper-bin/skills. In Claude Code, the plugin works too: /plugin marketplace add cjber/keeper, then /plugin install keeper@keeper. Then ask your agent:
Run keeper status, then keeper scan. Show me what is eligible and what is blocked, and why. Plan the low-risk items, then ask me before applying anything else.
Why keeper
- It proves before it removes.A worktree counts as finished only when a merged pull request holds every commit it has, or when every commit is on the default branch. Anything else waits for approval.
- Agents are the primary user.Every command returns JSON when piped, and each exit code names the next step. The wizard is a thin layer over the same calls.
- Agents can protect their work.
keeper holdoutranks every scanner until it expires, so a session can mark a worktree it will return to. - Nothing is final until purge.Paths move to a trash, and
keeper undorestores them. Local branches are deleted only at purge, and only if they have not moved. - It renices instead of killing.When the machine is overloaded, keeper names the commands holding the CPU and can lower their priority, so the work survives.
- Apps are judged from real use.
keeper samplerecords which executables are running, folded to their package. Only packages with a.desktoplauncher count as apps, and atime is never used.
How it works
scanRead-only. Every finding, with the blockers that keep it.agent reviewOptional. Your agent reads PRs and history, then adds holds on unfinished work.planSaves the eligible findings you chose. Apps and personal files need their id, and medium risk needs--max-risk medium.applyRescans, then applies the plan.keeper apply PLAN --yesskips anything that changed since the plan.undoRestores a transaction from the trash.purgeDeletes trash over 14 days old for good and reports the measured space freed.
To run it yourself, keeper opens a terminal wizard over the same calls. Findings are grouped by category with a count and size. A category with many rows starts folded: h and l fold or expand it, pgup and pgdn page through a long list, and space on a category header selects the eligible findings in the whole section. b shows blocked findings, g switches between the list, space by category and free space history, and r rescans everything. keeper timer install then cleans up low-risk items every night.
Commands
keeperOpens the wizard in a terminal.keeper statusFree space, schedule, last run, whether the nightly runs are healthy, holds, trash and missing tools.keeper scanLists findings and their blockers.--cachedreuses recently scanned categories.keeper planSaves a plan of eligible findings. Apps and personal files need their id. Add--max-risk mediumonce medium risk is approved.keeper apply PLAN --yesRescans, then applies what is unchanged.keeper undo TXRestores a transaction from trash.keeper purgeDeletes trash over 14 days old for good.keeper hold PATH --reason RProtects a path for 7 days, or--days N.keeper claim PATHDeclares your own scratch disposable once stale.keeper release PATHDrops a hold or claim.keeper logShows recent mutations and who made them.keeper historyFree space readings and the events that changed it, over 30 days or--days N.keeper sampleRecords free space and which executables are running, as the hourly timer does.keeper schemaDescribes the CLI as JSON.keeper autoApplies low-risk findings, purges old trash and notifies.keeper timer installScheduleskeeper autodaily andkeeper samplehourly with systemd, or--cron.keeper timer removeRemoves the schedule.
Set KEEPER_ACTOR to name yourself in holds, claims and the log.
What it finds
Each scan category, with its risk. Only low-risk findings can run unattended, and report-only findings are never removed.
Code and system
worktreeClean, pushed worktrees and stray directories in a worktree root. Low risk with proof, otherwise medium.aurClones of uninstalled packages, and build output in the others. Low.pacman-cacheOld package versions, keeping the newest. Low.orphansDependencies nothing requires. Medium.agentOutput in configured roots, and paths agents claimed as scratch. Low.buildGit-ignoredtarget,node_modules,.next,.turboand.venvwith no file written for 3 days. Low.transcriptsClaude Code and Codex transcripts over 14 days old. Low.package-cacheUnreferenced uv and pnpm entries, and npm and bun caches over 2G. Low.dockerDangling images and build cache unused for 7 days. Unused volumes are reported only. Low, volumes report only.logsConfigured logs over 50M, cut to their last 5M. Low.snapshotsTimeshift snapshots, which keep deleted data on disk. Report only.loadCommands holding the CPU while the machine is oversubscribed. Medium.memoryCgroups at theirmemory.highcap, and memory pressure. Report only.
Personal and application data
appsExplicitly installed packages that ship a.desktoplauncher and were not seen running for 90 days. Medium, by id only.largeFiles over 500M under$HOME, stale, outside git repos, dotdirs, downloads, screenshots and application trees. Medium, by id only.downloadsStale downloads, finished installers, and partials. Partials over a day old are low. Low or medium, by id only.screenshotsStale images in the PicturesScreenshotsand grim/hyprshot folders. Medium, by id only.duplicatesIdentical files in different personal document trees, keeping the newest copy and offering the rest. Medium, by id only.
Apps, large files, downloads, screenshots and duplicates are personal or application data, so they are only planned when you name their id. keeper plan with no ids never takes them, even with --max-risk medium.
large and duplicates skip application trees: anything under a Wine prefix, a Steam, Lutris, Heroic or GOG library, a ~/Games style library, flatpak data or a VM store, and any directory holding an install marker such as a .exe, .toc or .pak. A duplicate is offered only when the copy keeper keeps is in a different top-level folder of your own documents: Documents, Pictures, Music, Videos, Downloads or Desktop.
These block a worktree whatever its age: uncommitted files, commits on no remote, a running process inside it, an open pull request, a rebase, merge, bisect or cherry-pick in progress, and any hold.
Sizes in scans and plans are uncompressed. Trash sits on the same disk, so nothing is freed until keeper purge, which reports the measured change in free space as freed.