From devops-tools
Audits macOS disk usage, prunes developer caches (brew, uv, pip, npm, cargo), finds stale large files, and triages Downloads. Activates on disk space or cleanup queries.
How this skill is triggered — by the user, by Claude, or both
Slash command
/devops-tools:disk-hygieneThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Audit disk usage, clean developer caches, find forgotten large files, and triage Downloads on macOS.
Audit disk usage, clean developer caches, find forgotten large files, and triage Downloads on macOS.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Use this skill when:
1. Run disk overview (df -h / and major directories)
2. Audit developer caches (uv, brew, pip, npm, cargo, rustup, Docker)
3. Scan in-repo build artifacts (Rust target/, .venv, node_modules — often the biggest, see Phase 2.5)
4. Scan for forgotten large files (>50MB, not accessed in 180+ days)
5. Present findings with AskUserQuestion for cleanup choices
6. Execute selected cleanups
7. Report space reclaimed
1. Measure current cache sizes
2. Run safe cache cleanups (brew, uv, pip, npm)
3. Report space reclaimed
1. List Downloads contents with dates and sizes
2. Categorize into groups (media, dev artifacts, personal docs, misc)
3. Present AskUserQuestion multi-select for deletion/move
4. Execute selected actions
1. Scan home directory for large files not accessed in 180+ days
2. Group by location and type (media, ISOs, dev artifacts, documents)
3. Present findings sorted by size
4. Offer cleanup options via AskUserQuestion
Get the lay of the land before diving into specifics.
/usr/bin/env bash << 'OVERVIEW_EOF'
echo "=== Disk Overview ==="
df -h /
echo ""
echo "=== Major Directories ==="
du -sh ~/Library/Caches ~/Library/Logs ~/Library/Application\ Support \
~/.Trash ~/Downloads ~/Documents ~/Desktop ~/Movies ~/Music ~/Pictures \
2>/dev/null | sort -rh
echo ""
echo "=== Developer Tool Caches ==="
du -sh ~/.docker ~/.npm ~/.cargo ~/.rustup ~/.local ~/.cache \
~/.conda ~/.pyenv ~/.local/share/mise 2>/dev/null | sort -rh
OVERVIEW_EOF
| Cache | Location | Typical Size | Clean Command |
|---|---|---|---|
| uv | ~/Library/Caches/uv/ | 5-15 GB | uv cache clean |
| Homebrew | ~/Library/Caches/Homebrew/ | 3-10 GB | brew cleanup --prune=all |
| pip | ~/Library/Caches/pip/ | 0.5-2 GB | pip cache purge |
| npm | ~/.npm/_cacache/ | 0.5-2 GB | npm cache clean --force |
| cargo | ~/.cargo/registry/cache/ | 1-5 GB | cargo cache -a (needs cargo-cache) |
| rustup | ~/.rustup/toolchains/ | 2-10 GB | rustup toolchain uninstall <name> (list with rustup toolchain list) |
| mise | ~/.local/share/mise/installs/<tool>/<version>/ | 0.2-2 GB each | mise uninstall <tool>@<version> (list with mise ls) |
| Docker | Docker.app | 5-30 GB | docker system prune -a |
| Playwright | ~/Library/Caches/ms-playwright/ | 0.5-2 GB | npx playwright uninstall |
| sccache | ~/Library/Caches/Mozilla.sccache/ | 1-3 GB | rm -rf ~/Library/Caches/Mozilla.sccache |
| go-build | ~/Library/Caches/go-build/ | 5-25 GB | go clean -cache (or rm -rf if go not on PATH) |
| huggingface | ~/.cache/huggingface/ | 1-10 GB | rm -rf ~/.cache/huggingface/hub/<model> |
/usr/bin/env bash << 'CACHE_CLEAN_EOF'
set -euo pipefail
echo "=== Measuring current cache sizes ==="
echo "uv: $(du -sh ~/Library/Caches/uv/ 2>/dev/null | cut -f1 || echo 'N/A')"
echo "Homebrew: $(du -sh ~/Library/Caches/Homebrew/ 2>/dev/null | cut -f1 || echo 'N/A')"
echo "pip: $(du -sh ~/Library/Caches/pip/ 2>/dev/null | cut -f1 || echo 'N/A')"
echo "npm: $(du -sh ~/.npm/_cacache/ 2>/dev/null | cut -f1 || echo 'N/A')"
echo ""
echo "=== Cleaning ==="
brew cleanup --prune=all 2>&1 | tail -3
uv cache clean --force 2>&1
pip cache purge 2>&1
npm cache clean --force 2>&1
CACHE_CLEAN_EOF
| Issue | Cause | Solution |
|---|---|---|
uv cache lock held | Another uv process running | Use uv cache clean --force |
brew cleanup skips formulae | Linked but not latest | Safe to ignore, or brew reinstall <pkg> |
pip cache purge permission denied | System pip vs user pip | Use python -m pip cache purge |
| Docker not running | Docker Desktop not started | Start Docker.app first, or skip |
The single most-missed category — check it on EVERY audit. Compiler and dependency output lives inside your repos, not under ~/Library, so the Phase 1/2 scans never see it. A single active Rust repo's target/ routinely hits 10-35 GB; across a dev tree these artifacts can dwarf every cache combined (one real audit: 62 GB of target/ + 20 GB of .venv). All of it regenerates on the next build — the only cost is recompile / re-sync time.
| Artifact | Dir name | Typical size | Regenerated by |
|---|---|---|---|
| Rust build | target/ | 1-35 GB each | cargo build |
| Python venv | .venv/ | 0.1-2 GB each | uv sync / uv venv |
| Node modules | node_modules/ | 0.1-0.5 GB each | npm / bun install |
| Zig cache | .zig-cache/, zig-cache/ | 0.1-1 GB each | next zig build |
/usr/bin/env bash << 'ARTIFACT_SCAN_EOF'
ROOTS=(~/eon ~/own ~/src ~/code ~/projects)
for n in target .venv node_modules .zig-cache zig-cache; do
echo "=== $n (top 10 by size) ==="
find "${ROOTS[@]}" -maxdepth 5 -type d -name "$n" -prune 2>/dev/null \
-exec du -sh {} \; 2>/dev/null | sort -rh | head -10
done
ARTIFACT_SCAN_EOF
Rust target/ — guard against false matches. Only delete a target/ that has a sibling Cargo.toml, so you never nuke an unrelated folder literally named "target":
/usr/bin/env bash << 'TARGET_CLEAN_EOF'
ROOTS=(~/eon ~/own)
find "${ROOTS[@]}" -maxdepth 5 -type d -name target -prune 2>/dev/null | while read -r t; do
[ -f "$(dirname "$t")/Cargo.toml" ] && rm -rf "$t" && echo "cleaned: $t"
done
TARGET_CLEAN_EOF
.venv / node_modules are safe to bulk-delete by name (regenerated on next uv sync / install):
find ~/eon ~/own -maxdepth 5 -type d -name .venv -prune -exec rm -rf {} +
Caveats:
pgrep -fl 'cargo build|rustc|zig build' first — never delete artifacts for a repo whose build/test is currently running.cargo clean (run per-repo) is the tool-native equivalent of rm -rf target if you prefer.Find large files that have not been accessed in 180+ days.
/usr/bin/env bash << 'STALE_EOF'
echo "=== Large forgotten files (>50MB, untouched 180+ days) ==="
echo ""
# Scan home directory (excluding Library, node_modules, .git, hidden dirs)
find "$HOME" -maxdepth 4 \
-not -path '*/\.*' \
-not -path '*/Library/*' \
-not -path '*/node_modules/*' \
-not -path '*/.git/*' \
-type f -atime +180 -size +50M 2>/dev/null | \
while read -r f; do
mod_date=$(stat -f '%Sm' -t '%Y-%m-%d' "$f" 2>/dev/null)
size=$(du -sh "$f" 2>/dev/null | cut -f1)
echo "${mod_date} ${size} ${f}"
done | sort
echo ""
echo "=== Documents & Desktop (>10MB, untouched 180+ days) ==="
find "$HOME/Documents" "$HOME/Desktop" \
-type f -atime +180 -size +10M 2>/dev/null | \
while read -r f; do
mod_date=$(stat -f '%Sm' -t '%Y-%m-%d' "$f" 2>/dev/null)
size=$(du -sh "$f" 2>/dev/null | cut -f1)
echo "${mod_date} ${size} ${f}"
done | sort
STALE_EOF
| Type | Typical Location | Example |
|---|---|---|
| Windows/Linux ISOs | Documents, Downloads | .iso files from VM setup |
| CapCut/iMovie exports | Movies/ | Large .mp4 renders |
| Phone video transfers | Pictures/, DCIM/ | .MOV files from iPhone |
| Old Zoom recordings | Documents/ | .aac, .mp4 from meetings |
| Orphaned downloads | Documents/ | CFNetworkDownload_*.mp4 |
| Screen recordings | Documents/, Desktop/ | Capto/QuickTime .mov |
| TTS debug WAV | ~/.local/share/tts-debug-wav/, ~/.local/share/kokoro-debug*/ | Debug-mode TTS audio captures — can grow 1-2 GB/day if debug mode left on. Safe to rm -rf the contents. Root cause for ~/.local/share/tts-debug-wav (claude-tts-companion): retention is gated by a compile-time #if DEBUG in AfplayPlayer.swift — there is NO runtime env/config toggle. A RELEASE build deletes each WAV after playback via PlaybackDelegate. The permanent fix is reinstalling the companion as a release build (make in the plugin dir, which runs swift build -c release), not a pruner script. For other TTS tools, look for a tts-prune mise task or tighter retention config |
Use AskUserQuestion with multi-select to let the user choose what to clean.
~/Downloads with dates and sizes/usr/bin/env bash << 'DL_LIST_EOF'
echo "=== Downloads by date and size ==="
find "$HOME/Downloads" -maxdepth 1 \( -type f -o -type d \) ! -path "$HOME/Downloads" | \
while read -r f; do
mod_date=$(stat -f '%Sm' -t '%Y-%m-%d' "$f" 2>/dev/null)
size=$(du -sh "$f" 2>/dev/null | cut -f1)
echo "${mod_date} ${size} $(basename "$f")"
done | sort
DL_LIST_EOF
When presenting Downloads cleanup options, use this pattern:
| Tool | Wall Time | CPU Usage | Interactive Delete | Install |
|---|---|---|---|---|
| dust | 20.4s | 637% (parallel) | No (view only) | brew install dust |
| gdu-go | 28.8s | 845% (very parallel) | Yes (TUI) | brew install gdu |
| dua-cli | 37.1s | 237% (moderate) | Yes (staged safe delete) | brew install dua-cli |
| ncdu | 96.6s | 43% (single-thread) | Yes (TUI) | brew install ncdu |
dust for quick "where is my space going?" - fastest scanner, tree outputdua i or gdu-go for interactive exploration with deletion# dust - instant tree overview
dust -d 2 ~ # depth 2
dust -r ~/Library # reverse sort (smallest first)
# dua - interactive TUI with safe deletion
dua i ~ # navigate, mark, delete with confirmation
# gdu-go - ncdu-like TUI, fast on SSDs
gdu-go ~ # full TUI with delete support
gdu-go -n ~ # non-interactive (for scripting/benchmarks)
brew install dust dua-cli gdu
Note: gdu installs as gdu-go to avoid conflict with coreutils.
Ordered by typical space reclaimed (highest first):
| Action | Typical Savings | Risk | Command |
|---|---|---|---|
Rust target/ dirs (Phase 2.5) | 10-60 GB+ | None (cold rebuild on next cargo build) | find ROOTS -type d -name target + Cargo.toml-sibling guard |
Python .venv dirs (Phase 2.5) | 5-20 GB | None (re-sync via uv sync) | find ROOTS -type d -name .venv -prune -exec rm -rf {} + |
go clean -cache | 5-25 GB | None (re-downloads) | go clean -cache |
uv cache clean | 5-15 GB | None (re-downloads) | uv cache clean --force |
brew cleanup --prune=all | 3-10 GB | None (re-downloads) | brew cleanup --prune=all |
| Delete movie files in Downloads | 2-10 GB | Check first | Manual after AskUserQuestion |
| Prune old rustup toolchains | 2-5 GB | Keep current | rustup toolchain list then rustup toolchain uninstall <name> |
| Prune stale mise toolchains | 0.5-3 GB | Cross-check .mise.toml pins first | mise ls, then mise uninstall <tool>@<version> |
npm cache clean --force | 0.5-2 GB | None (re-downloads) | npm cache clean --force |
pip cache purge | 0.5-2 GB | None (re-downloads) | pip cache purge |
| Docker system prune | 5-30 GB | Removes stopped containers | docker system prune -a |
| Empty Trash | Variable | Irreversible | rm -rf ~/.Trash/* |
After modifying this skill:
/usr/bin/env bash << 'EOF' wrapper$HOME)| Issue | Cause | Solution |
|---|---|---|
uv cache clean hangs | Lock held by running uv | Use --force flag |
brew cleanup frees 0 bytes | Already clean or formulae linked | Run brew cleanup --prune=all |
find reports permission denied | System Integrity Protection | Add 2>/dev/null to suppress |
gdu command not found | Installed as gdu-go | Use gdu-go (coreutils conflict) |
dust shows different size than df | Counting method differs | Normal - df includes filesystem overhead |
| Stale file scan is slow | Deep directory tree | Limit -maxdepth or exclude more paths |
| Docker not accessible | Desktop app not running | Start Docker.app or skip Docker cleanup |
parse error near TASK_ID=$(pueue add ...) from heredoc with spaced paths | A user shell hook (e.g. pueue submission) re-parses the command string and breaks on ${var}/Path With Spaces/* globs inside heredocs | Write multi-line scripts to /tmp/<name>.sh first via Write tool, then invoke as bash /tmp/<name>.sh — bypasses the inline heredoc → hook re-quote path entirely |
| Removing a mise toolchain triggers immediate auto-reinstall | A project's .mise.toml pins the version you just removed; mise restores it on next invocation from that project | Before mise uninstall <tool>@<version>, grep all reachable .mise.toml and mise.toml files for the version. If pinned, leave it alone or update the pin first. Same applies to rustup toolchains vs. rust-toolchain.toml files in projects. |
If the user's shell environment has bash hooks that intercept tool calls (pueue, asciinema, etc.) and the heredoc pattern fails with cryptic parse errors, write the script to a temp file and invoke it:
# Instead of: bash << 'EOF' ... EOF
# Use: Write tool → /tmp/<task>.sh, then:
bash /tmp/<task>.sh
Single-line bash invocations like du -sh "$HOME/Library/Application Support"/Google/* 2>/dev/null | sort -rh | head work fine even with hooks installed — only multi-line heredocs containing spaced-path globs are problematic.
After this skill completes, reflect before closing the task:
Do NOT defer. The next invocation inherits whatever you leave behind.
npx claudepluginhub terrylica/cc-skills --plugin devops-toolsAnalyzes macOS disk usage and provides safe, interactive cleanup recommendations with user confirmation before deletions.
Identifies and cleans disk hogs like Rust target/, node_modules, Python venvs, git garbage, Docker images, Homebrew caches, and hidden temps. Use when disk space is low or full.
macOS disk-usage forensics and space recovery on APFS. Use when df reports a disk near-full, hunting what's eating space, reclaiming OrbStack/Docker, or pruning caches and local snapshots.