2026-09-06 5 min read

Cleaning up 80GB of "System Data" turned into a Claude Code skill

A few weeks ago I opened Storage settings on my Mac and "System Data" was sitting at 80-something gigabytes. Not one big offender, just years of package manager caches, old node_modules, Xcode leftovers, and Docker images I'd forgotten I ever pulled. The category macOS uses as a catch-all for "we're not going to tell you what this actually is."

I did not want to go poking through it by hand. That category is exactly where you find a stray Terraform state file or a .git directory sitting next to something genuinely disposable, and I did not trust myself to tell them apart at 1am with a rm -rf half-typed.

So I had Claude Code do the sorting instead: scan everything read-only first, classify each thing into safe-to-delete-and-fully-regenerable, needs-a-human, or never-touch, show me sizes and the reason for each classification, and only delete what I actually said yes to. First pass got back about 43GB. A follow-up on a project with a bloated node_modules history found another 9GB of duplicated Terraform provider binaries and stale Python virtualenvs.

That part went fine. What I did not expect was how much I'd learn by turning it into something reusable.

Making it repeatable surfaced bugs a one-off script never would have

I run this kind of cleanup maybe once every couple of months, which is exactly often enough to forget the reasoning each time. So I turned it into a Claude Code skill: a SKILL.md with the classification rules, plus a scan script and a guardrailed delete script that re-checks every path against an allow/deny list independently of whatever the model decided, so a misread instruction can't turn into an accidental delete.

To actually trust it, I ran it through Claude Code's skill-creator eval loop: real subagents executing the skill against realistic prompts, a baseline run without the skill for comparison, and a human pass over the outputs. That process caught two things I would not have caught by just reading the instructions back to myself.

The second was worse, because it fails silently. macOS blocks listing the contents of ~/.Trash without Full Disk Access, and it does this quietly — the directory listing just comes back empty, indistinguishable from an actually-empty Trash unless you check stderr specifically. A naive item count would report "0 items, nothing to do" when the honest answer is "I don't have permission to know." I only found this by creating fake files in Trash and watching ls fail while cat on a known path inside it still worked. Both the scan and the delete script now probe for that failure explicitly and say so, instead of reporting a false empty.

Why this needed reasoning, not just a script

A fixed shell script can tell you node_modules is big. It cannot tell you that this particular node_modules is safe because there's a pnpm-lock.yaml next to it, while the one in your scratch folder with no lockfile at all should probably be a question, not a deletion. It cannot explain why ~/Library/Caches is safe to clear on macOS specifically. And it definitely cannot have the conversation about a .venv with no requirements.txt, or a Podman VM that might still be running something.

The scripts do the parts that should be deterministic — measuring sizes, enforcing the guardrails on delete — so a bug there is a script bug you can write a regression test for, not an instruction that quietly stopped being followed three context windows later.

Where it ended up

The skill is public on GitHub now. Install is a git clone into ~/.claude/skills/, then you just talk to it normally — "my disk is full, what's safe to delete" is enough, no slash command to remember. It always shows the full plan and asks before deleting anything, even if you phrase the request as blanket permission, because deciding what "junk" actually means safely was the entire point of building it in the first place.

If you try it and it gets something wrong on your machine, that's useful information — open an issue, or send a PR.

$