We Open-Sourced a Skill So Code Agents Don't Eat Your Drive with Worktrees

We open-sourced another agent skill, git-hygiene. It lives in the same agentloop plugin as the earlier pair design-review / build-phases, in ArcBlock/agent-skills, and it handles a different concrete problem that shows up once agent coding is normal:
The more agents run in parallel, the more working directories pile up. What actually scares you at cleanup time is not the tedium, it is deleting the wrong thing.
That has been happening on my own machine repeatedly over the past few months: one cleanup pass, one repo, dozens of local branches and a dozen-plus worktrees, most left behind by different agents. git-hygiene is built for that, and it is more careful about it than I ever was doing it by hand.
Install and use
These are agent skills, not tied to one particular agent. Claude Code and Codex both run them.
Install (same path as the other agentloop skills in this series):
npx add-skill arcblock/agentloopAfter that, natural language is enough. Default cleanup:
switch to main, pull latest, clean up unused worktrees and branchesOr shorter:
git hygiene
clean up leftover worktreesThe default flow does four things: switch back to the default branch, pull --ff-only to match the remote, classify every local branch and worktree by strength of evidence, and only delete the ones it can prove are safe.
To go one step further and investigate whether kept branches still need a PR, ask explicitly:
are these kept branches supposed to have PRs, verify carefullyThat layer is more expensive, so it is not the default. It only runs when you ask. The full implementation is the skill's own SKILL.md; you can just read it.
One workspace per agent isn't waste, it's the requirement
A single person writing code has one working directory per repo, switches branches, and mostly knows by memory what's safe to delete when the session is done. Agent coding changes the scale of that problem.
My normal state now is several agents running at once: one implementing a feature, one reviewing its PR from a clean context, one fixing something else, none of them stepping on the others. There is exactly one way to keep them from stepping on each other, and that is giving each one its own worktree. Worktrees are a native git feature: one repo can have several independent working directories at once, each checked out to a different branch, so switching one agent's branch never drags along the directory another agent is actively using.
That's the right architecture, not a step you can skip. If several agents shared one working directory, any single git checkout would change what branch everyone else sees as current. That's a real failure mode we've hit. Worktree isolation solves exactly that, and the price is that every isolated working directory is its own full copy of the repo.
How the drive actually fills up
The problem shows up after the work is done. An agent merges its PR, its worktree should go away, but "should" and "does" aren't connected by anything automatic. Nobody is watching for it, and I wasn't comfortable deleting on a hunch either, since I couldn't always tell whether a branch was really merged. Hesitation turns into accumulation.
Accumulation costs real disk space. Storage has bitten me on this before. Back when programs, data, and media all lived together, I have old backup disks and tapes in my garage I'm not even sure how to read anymore 🤣. That was the same pattern: something sits around unmanaged, and the cost only shows up much later, by which point it's too late. This time the pattern shows up faster, just in a different shape. It's not decades before the media is unreadable, it's weeks before the drive is full.
My machine has 1TB. These scattered worktrees ate through it a bit at a time, each one a full copy of a large monorepo, dependencies and build artifacts and test fixtures included. Stack a few dozen of those and you feel it.
What actually scared me about manual cleanup wasn't the tedium
The hard part was never "this is a lot of branches to delete." It was "can I actually tell if this one is safe to delete."
git branch -d (lowercase, only deletes a branch it can confirm is merged, errors out otherwise) doesn't work at all against squash merges. Squashing takes every commit on a branch and compresses them into one new commit before merging. The content matches, but the object is new, so it no longer lines up with the original commits on the branch. Most of our PRs are squash merged, and git branch --merged works by comparing individual commits, so it can't see that connection. It reports an already-merged branch as unmerged. Trusting that report and manually checking PR status one at a time is more work than not cleaning up at all.
Go the other way and reach for the uppercase git branch -D (force delete, doesn't care whether there's unique content), and the risk is deleting real work that was never pushed. That is exactly the failure mode parallel agents produce most often: something gets interrupted halfway through, the branch sits there locally, and you can't tell "this already shipped" from "this isn't done yet" just by looking at it.
How the skill decides
The core of the skill isn't deleting. It is proving something can be deleted first. Every local branch gets sorted by strength of evidence, one branch at a time:
- SAFE: the branch tip is already an ancestor of
main(or the repo default). Content has landed. Safe to delete. - SQUASH_CANDIDATE: not an ancestor, but GitHub shows a
MERGEDPR against it. That's probably the squash case above, so it can't be trusted blindly. It gets a content check first, confirming nothing was missed, before it's removed. - KEEP: neither an ancestor nor proven to be on the default branch (or the worktree is still dirty / locked). Kept as-is and reported back for a person to decide.
- DETACHED_TMP: worktrees sitting under
/tmp-style paths, the obvious leftover review sandboxes that aren't checked out to any branch at all. Identified and handled separately.
The default action only touches the part of "delete" it's most confident about. Anything with unique content stays. No guessing, no betting on it.
If the primary working tree is dirty, it stops: no checkout, no deletes. It only does pull --ff-only; if the history has diverged, it reports that and does not rebase on its own. A remote tracking branch that shows as gone is not enough to delete locally either. Gone only means the remote is gone; the local tip may still have unmerged commits.
One step further: does this branch actually need a belated PR
A new question came up during cleanup. A handful of branches looked like real, legitimate work that had simply never gone through a PR. The old logic would keep marking them as kept forever, and nobody would ever come back to sort them out.
So the skill takes one more step when you ask for it. For each kept branch, check its real status on GitHub first:
- maybe it already merged through a different branch and this local copy never got the memo
- maybe the original PR was closed because something else superseded it
- maybe there's an open PR already being watched by automation and it doesn't need your involvement
- only when none of that is true does it move toward drafting a belated PR
Even then there's a step that can't be skipped: having unique commits isn't proof the content is worth submitting. A lot of the time that work already landed on main through a different path, and the local branch just fell behind.
This pass, out of five branches that all looked legitimate, only one turned out to be genuinely missing, and that's the one that got a PR. The other four had already merged, been superseded, or were already being tracked somewhere else. That layer ships with the skill in agent-skills now; install agentloop and you have it.
Where it doesn't help
It won't merge a PR for you, won't force push, won't run git clean -fdx, and won't touch uncommitted changes. When a branch's status is genuinely unclear, no clean PR evidence and no confident content match, it stays exactly as it was and the decision goes back to a person. It won't gamble on a clean-looking result.
It also doesn't replace tools that create worktrees. Skills like git-worktree are about opening parallel directories; git-hygiene is about shutting them down safely. Different ends of the same loop.
The fit is specific: after a stretch of multi-agent work, after PRs merge, when you want to get back on main and reset the primary workspace without risking work that never got pushed. If you have one working directory and two or three branches, just use git branch -d yourself. You don't need this.
Another piece on the same road
The earlier piece was about plans that drift: review them with a clean context, then build in phases. This one is about parallel workspaces as the right architecture, and about making the judgment behind cleanup something repeatable and trustworthy, not something that depends on one person's memory of what got merged.
They look like different jobs, one about how to do the work and one about how to clean up after it. They're two sides of the same problem: once agent coding raises the scale, the part that used to live in someone's head has to land as a skill.
git-hygiene doesn't decide whether cleanup should happen. It decides how cleanup happens without deleting the wrong thing.
GitHub: github.com/ArcBlock/agent-skills · skill source: plugins/agentloop/skills/git-hygiene
Also in this series:
Referenced here
Products
-
AIGNE
active
A framework for developers building agents. Start from the command line to create and run a project; use the framework documentation for the parts that need detail.