# Disk Cleanup

> Measure disk usage on macOS with fast Rust tools and produce a scannable percentage-based report with ranked cleanup candidates. Trigger with "what's using my disk", "disk usage", "free up space", "why is my drive full", or before/after any large cleanup.

- Skill: `davemaynard/disk-cleanup` (Agent Skill)
- Install (CLI): `npx skillmds@latest add davemaynard/disk-cleanup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/davemaynard/disk-cleanup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: davemaynard (https://skillmd.com/u/davemaynard)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/davemaynard/disk-cleanup

---


# /disk-cleanup

Measure real disk usage, report it percentage-based, and rank cleanup candidates
**by reclaimable size × safety** — never by size alone.

## Tools

Both are Rust. Install if missing: `brew install dust dua-cli`

| Tool | Use it for |
|---|---|
| `dua` | Scanning. Parallel walker, ~68 GiB / 500k files in **7.5s**. Best for totals and drill-down. |
| `dust` | Presentation. Native percentage bars — this is the report format. |

Canonical commands:

```bash
# percentage view (the deliverable)
dust -d 1 -n 15 -r ~

# totals + drill-down
dua --format binary ~
dua --format binary /Applications/* | tail -12
dua --format binary ~/Library/Application\ Support/* | tail -8
```

## Step 1 — Establish ground truth first

`df` lies on APFS (snapshots, purgeable). Always cross-check:

```bash
df -h / /System/Volumes/Data
diskutil info /System/Volumes/Data | grep -i -E "free|used|container total"
```

Scan **both** `~` *and* `/Applications`, `/Library`, `/opt`. On this machine
`/Applications` outweighed the entire home directory — a home-only scan
misses the biggest thing.

Then compute `unaccounted = volume_used - sum(measured)` and **show it as a row**.
That row is where the truth you couldn't reach is hiding.

## Step 2 — Two things that break the scan

**Dead FileProvider mounts hang the walk.** A signed-out cloud mount under
`~/Library/CloudStorage/` makes `find`/`dua` hang forever, not error. Exclude it:

```bash
dust -X "SomeCloudMount-name" ~
dua --ignore-dirs ~/Library/CloudStorage/<mount> ~
```

**TCC blocks `~/Pictures`, `~/Library/Containers`, Messages, Mail.** These return
`Operation not permitted` and count as **0**, silently. If the scan reports
IO errors, say so explicitly — the Photos library alone can be 20 GiB+ of
invisible usage. Fix: grant the terminal Full Disk Access
(System Settings → Privacy & Security → Full Disk Access), then rescan.

Never present a report with IO errors as if it were complete.

## Step 3 — VERIFY EVERY CANDIDATE BEFORE PROPOSING IT

This is the step that matters. A directory's *name* does not tell you whether
it holds live data. Check before recommending, and check again before deleting.

```bash
# Is the app actually stale, or used yesterday?
mdls -name kMDItemLastUsedDate -raw "/Applications/Foo.app"

# Container VM dirs (~/.colima, ~/.docker, ~/.lima, ~/.orbstack):
colima status; docker ps -a; docker volume ls; docker system df
```

**Hard-won rule:** `~/.colima` looks like a disposable VM image, but it holds
every running container and named volume. A self-hosted app with a Postgres volume
lives there just as happily as a scratch build. Deleting it destroys the database
with no warning. `docker system df` reporting **0B reclaimable** means nothing there
is safe to remove — check before you treat the directory as cache.

Same trap class:
- `~/.claude` — session history, **never delete**. The bulk is `~/Library/Application Support/Claude/vm_bundles`, which is separate and regenerable.
- `~/Library/Caches` vs `~/Library/Application Support` — the first is regenerable, the second is often real data. Do not lump them.
- Anything under `~/Library/Mobile Documents` — iCloud. Dataless placeholders report 0 blocks; deleting evicts real cloud data.

## Step 4 — Classify

| Tier | Meaning |
|---|---|
| ✅ Safe | Pure cache, regenerates automatically, no state |
| ⚠️ Costed | Regenerates but re-downloads, or app must re-init |
| ❌ Awareness only | Large but live. **List it, never delete it** |

Always include the biggest item even when it's untouchable — knowing a game is
24% of the disk is useful. Just mark it clearly as not-for-deletion.

## Step 5 — Report format

Keep it scannable. Two tables, no prose walls.

```markdown
## Disk totals — X GiB used of Y GiB (Z% full, N GiB free)

| Area | Size | % of used |
|---|---:|---:|
| /Applications | 57.1 GiB | **36%** |
| ~/Library | 28.7 GiB | 18% |
| *unmeasured — system + Photos* | ~28.4 GiB | 18% |

## Top 5 cleanup candidates

| # | Target | Reclaims | Safety |
|---|---|---:|---|
| 1 | `~/Library/Caches` + `~/.cache` | **15.5 GiB** (9.7%) | ✅ Fully regenerable |
| 5 | a large game or app bundle | **38.4 GiB** (24%) | ❌ Awareness only — in active use |
```

Bold the reclaim figures. Give the combined safe total and the resulting
percentage ("~33 GiB, takes you 82% → 68%"). Close with any measurement gap.

## Step 6 — Deleting

Dry-run first: print sizes and paths, get explicit approval, then delete.
Measure before and after and report **actual** reclaimed bytes — not the estimate.

If the user approves a candidate that Step 3 later proves unsafe, **do not delete
it**. Execute every safe item, then say plainly what you skipped and why. Approval
given on a wrong description isn't approval for the real thing.

