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.