Frees disk space on a Mac by clearing caches that rebuild themselves - npm, pnpm, Gradle, CocoaPods, the Docker build cache, and .turbo/.next folders. Measures first, asks before deleting anything, and checks afterwards that the space was actually freed.
Save it as ~/.claude/skills/cache-cleanup/SKILL.md
Show SKILL.mdHide SKILL.md
cache-cleanup
macOS only, and only caches: things a tool rebuilds or re-downloads on its own. Data is out of scope. That means
app folders in ~/Library/Application Support, git worktrees, node_modules, Docker volumes and
containers, the Docker Desktop VM disk, simulators and runtimes, Android NDKs and images, LLM
models, and the Trash. Report any large data you notice with its size, but never delete it here.
Workflow
- Survey with the block below, in one command. Record the
free:line as the baseline. - Present the groups with their sizes, one line each, and skip anything under 100 MB. Ask with AskUserQuestion (multiSelect), recommending every group except app caches.
- Delete the chosen groups, running each group's preflight check first. Run slow deletions (pnpm store, CocoaPods specs, thousands of build dirs) in the background.
- Verify every group with
duon its targets afterwards. A group that freed nothing is a failure to investigate, not a success. - Report the amount freed per group and in total (
dfbefore and after), what was skipped and why, and anysudocommand the user has to run themselves.
Survey
1s() { [ -e "$2" ] && printf '%-26s %s\n' "$1" "$(du -sh "$2" 2>/dev/null | cut -f1)"; }
2df -h / | awk 'NR==2{print "free: "$4}'
3s npm ~/.npm/_cacache
4s "pnpm store" ~/Library/pnpm/store; ls ~/Library/pnpm/store 2>/dev/null | tr '\n' ' '; echo
5s "pnpm metadata" ~/Library/Caches/pnpm
6s bun ~/.bun/install/cache
7s yarn ~/Library/Caches/Yarn; s "yarn berry" ~/.yarn/berry/cache
8s Homebrew "$(brew --cache 2>/dev/null)"
9s "Gradle caches" ~/.gradle/caches; s "Gradle daemon" ~/.gradle/daemon
10s "Gradle wrappers" ~/.gradle/wrapper/dists
11s "CocoaPods cache" ~/Library/Caches/CocoaPods; s "CocoaPods master specs" ~/.cocoapods/repos/master
12s "Xcode DerivedData" ~/Library/Developer/Xcode/DerivedData
13s "~/Library/Caches" ~/Library/Caches
14s "~/.cache" ~/.cache
15s TMPDIR "$TMPDIR"
16docker system df 2>/dev/null
17for d in ~/Documents ~/Projects ~/Developer ~/code ~/src ~/work; do [ -d "$d" ] && find "$d" \( -name node_modules -o -name .git \) -prune -o -type d \( -name .turbo -o -name .next -o -name target \) -prune -print 2>/dev/null; done \
18 | while read -r p; do [ "${p##*/}" != target ] || [ -f "$(dirname "$p")/Cargo.toml" ] && echo "$p"; done > "$TMPDIR/build-caches.txt"
19tr '\n' '\0' < "$TMPDIR/build-caches.txt" | xargs -0 du -sk 2>/dev/null | awk '{t+=$1;n++} END{printf "build caches: %d dirs, %.1fG\n", n, t/1048576}'
20
A target dir counts only when a Cargo.toml sits next to it, because in other projects a folder
named target can be real source.
Groups
Package managers. Run from /tmp, never inside a project.
- npm:
npm cache clean --force. Ifnpmisn't on PATH, use rm -rf ~/.npm/_cacache instead. Don't trust a silent success, checkduafterwards. - pnpm: cd /tmp && pnpm store prune, plus rm -rf ~/Library/Caches/pnpm. Store versions map to
pnpm majors:
v3is pnpm 7-9,v10is pnpm 10,v11is pnpm 11+. Prune each version with the matching major (npx -y pnpm@10 store prune). Delete a whole store version only when no project pins a pnpm major that uses it (greppackageManagerin package.json files). - bun:
bun pm cache rm. yarn:yarn cache clean, and rm -rf ~/.yarn/berry/cache. - Homebrew: brew cleanup --prune=all -s.
- Gradle: preflight
pgrep -f GradleDaemonmust be empty. Then rm -rf ~/.gradle/caches ~/.gradle/daemon, plus every wrapper dist except the newest. - CocoaPods:
pod cache clean --all. Remove ~/.cocoapods/repos/master only if noPodfilecontains source 'https://github.com/CocoaPods/Specs.git'.
Docker. Run docker builder prune -a -f. Offer docker image prune -a -f as a separate
choice, because images take time to pull again. Never prune volumes or containers here.
Build caches. The paths are in $TMPDIR/build-caches.txt. Keep it in $TMPDIR, which is
private to the user, never in the shared /tmp, since this list feeds rm -rf. Preflight with
pgrep -fl 'next (dev|start|build)|next-server|turbo (run|dev)', pgrep -x cargo and
pgrep -x rustc. Skip any project with a match (resolve each process's cwd with
lsof -a -p <pid> -d cwd). Turbo and Next caches have no size limit, and a single repo can hold
30+ GB.
Xcode. Run rm -rf ~/Library/Developer/Xcode/DerivedData/* and xcrun simctl delete unavailable.
App caches. Clear ~/Library/Caches/* except com.apple.* and ms-playwright (Playwright
browsers are slow to re-download). List the entries in ~/.cache and remove only the ones the
user picks, since some belong to running tools.
Temp. Run find "$TMPDIR" -mindepth 1 -maxdepth 1 -mtime +3 ! -name 'com.apple*' ! -type s -exec rm -rf {} +.
Pitfalls
pgrep -f cargomatches any process with .cargo/bin in its PATH. Usepgrep -xfor binary names.- Background shells may lack
nodeand other PATH entries, so npm or pnpm commands can fail without a word. Always verify the size afterwards. - pnpm self-update and global pnpm commands run inside a repo rewrite
packageManagerin its package.json. Checkgit statusif you ever run them there by mistake. - Never delete the
_tmp_folders in ~/Library/pnpm/.tools, because they hold real pnpm installs. - Root-owned leftovers (for example ~/.cpan after
sudo cpan) fail withPermission denied. Give the user thesudo rmcommand instead of escalating. - macOS Settings > Storage can disagree with
dfanddu, and it updates late. Reportdfanddunumbers, not Settings.