Your agents leave the mess behind.

Every task gets its own git worktree. None of them get cleaned up. On the machine this was built against that is 42 worktrees and 76 GB.

Free and MIT licensed. Requires macOS 15 on Apple Silicon.

Coppice
42 worktrees · 76.61 GB
Sweep 22.4 GB
ENV_Connect
t3code-6da8e6e7
t3code/general-help-request
19.6 MB
t3code-99ca1d92
t3code/fix-cli-npm-publish
19.5 MB
t3code-5b9b4d42
feat/convex-direct-web
2.32 GB
t3code-ca476e75
t3code/align-subpages-to-theme
6.18 GB
t3code-69fa9055
t3code/react-doctor-audit
1.49 GB
agent-a6ac4f5dc7
fix/updater-rollback
0 B

This is one laptop, on an ordinary Tuesday.

42
worktrees
76 GB
sitting on disk
37
held real work
22 GB
safe to reclaim

Nobody plans this. You spin up a worktree to try something, the branch merges, and the directory stays. Multiply by four agents working in parallel and the disk quietly fills with the residue of work you already finished.

So why not just delete them?

Because the obvious version of this tool destroys your work.

A script that finds old worktrees and removes the clean ones sounds safe enough. It asks git whether the directory is clean, git says yes, the directory goes. That is the whole idea, and it is wrong in a way you only find out afterwards.

A worktree holding this file

.env.local

reports as perfectly clean, because it is gitignored. Git has never seen it and holds no copy. Neither does your remote.

Delete that worktree and the secrets are gone. Not recoverable from a branch, not on the remote, not in a stash. The one check that would have caught it is the check a naive cleaner never thinks to make, and you learn about it when something stops authenticating.

Coppice exists because the careful version of this is genuinely fiddly. Eleven conditions decide whether a worktree is safe, and every one of them has to be rechecked at the instant of deletion. A background scanner's verdict is already minutes old by the time you press a button, and in those minutes an agent can open the very worktree you picked.

Three operations, priced by what they cost you.

Sweep

Reversible

Deletes build output inside worktrees. node_modules, .next, target, DerivedData. Source, git history and local config are never touched. It runs on dirty worktrees too, because a dependency folder is not your work.

The undo is the install command you already type.

Prune

Frees nothing

Clears git metadata for worktrees whose directories are already gone. Git considers these dead, so nothing on disk is touched. What you get back is a worktree list you can actually read.

Ten of them were already dead on this machine.

Remove

Permanent

Deletes the worktree itself, prunes the metadata, and optionally deletes the branch. Gated behind every rule below, then a typed confirmation naming the worktree.

There is no Remove All anywhere in the app, on purpose.

Eleven reasons it will stop you.

Every one is recomputed at the moment of deletion, never trusted from the scan that built the list.

A process is working in it

An open agent session, editor or dev server. This one also stops a sweep.

The main worktree

A repository's own working copy is never removable.

Outside your folders

Paths beyond the roots you configured are refused.

Uncommitted changes

Modified and staged files are work that exists nowhere else yet.

Untracked files

Something new that git has never seen is still yours.

Unpushed commits

Work that has not reached a remote has no second copy.

Ahead of the default branch

No upstream at all, and commits missing from main.

Gitignored config

The .env.local case. Git calls the worktree clean and it is lying.

A locked worktree

Someone ran git worktree lock. That was a deliberate act.

An operation in progress

A half-finished rebase, merge, cherry-pick or bisect.

A dirty submodule

Changes nested inside a submodule count as changes.

Three refuse absolutely. The other eight are your call.

A live process, the main worktree and the folder boundary corrupt something no confirmation can undo, so there is no button for them. The rest is your own work. Worktrees are scratch space, and refusing forever would make the tool useless on exactly the ones worth deleting, so each of those offers an override that states precisely what it destroys and shows whether the branch's pull request has already merged.

It never polls, so it never costs you anything.

A timer re-scanning 42 worktrees every minute would burn battery all day to learn nothing almost every time. Coppice watches with FSEvents, so the kernel wakes it only when a directory actually changes, and events coalesce so an install writing a hundred thousand files arrives as one callback.

0.0%
CPU when idle
99 MB
memory
3 s
to judge 42 worktrees
0
dependencies

You are about to let software delete your work.

There is one honest answer to why you should trust it, and it is not a promise on a landing page. It is the source.

Read the rules yourself

The eleven conditions are one Swift file. You can read what counts as unsafe, disagree with it, and see the tests that build real git repositories to prove each one fires.

Judgment should be arguable

Deciding which blockers can be overridden is a taste call, not a fact. Someone will find it too cautious and someone too loose. Both should be able to open an issue with the line number in hand.

MIT, so take what is useful

Lift the verdict engine into your own tooling, vendor it, fork it, ship it commercially. No copyleft and no permission needed. The problem is not specific to this app.

It is also the only way this stays correct. Agent tools change their worktree layouts constantly, and the person who notices first is whoever it broke for.

Install

Stable

installs clean, then updates itself
brew install --cask rafay99-epic/apps/coppice

Nightly

runs beside Stable, own icon and settings
brew install --cask rafay99-epic/apps/coppice-nightly

Prefer a direct download? The disk image is not notarized, because there is no paid Apple Developer account behind it. Right click and choose Open the first time.