glebis/disk-cleanup
Scan and clean macOS caches, package-manager data, crash dumps, and app caches to reclaim disk space. Deterministic — a config registry (targets.json) plus two scripts (survey.py read-only, clean.py executor) do all the measuring and deleting; the agent only relays a compressed summary and makes the few human-judgment calls. IMPORTANT — use this skill whenever the user's request on macOS involves: freeing disk space, cleaning/clearing caches, "disk is full", "clean up my Mac", "free up space", "what's eating my disk", "running low on disk", needing space for an install, or any low-storage complaint. Covers the whole workflow survey → choose → clean → empty Trash.
npx skills add https://github.com/glebis/claude-skills --skill disk-cleanup
Deterministic by design. All target knowledge lives in targets.json; all measuring and
deleting lives in scripts/survey.py (read-only) and scripts/clean.py (executor, **dry-run by
default). They run headless with zero dependencies** (stdlib only) — a user can run them in a
terminal without any agent. The agent's job is small: run the scripts, relay the compressed
output, and decide the handful of things that need human judgment.
python3 scripts/survey.py # read-only: sizes, risk, flags, uncategorized. Touches nothing.
python3 scripts/survey.py --json # same, machine-readable (preferred for the agent)
python3 scripts/clean.py --preset safe # DRY-RUN plan (default — nothing deleted)
python3 scripts/clean.py --preset safe --go # execute (safe risk only)
python3 scripts/clean.py --preset full --allow-medium --go --empty-trash # safe+medium, then empty Trash
python3 scripts/clean.py --ids cargo-registry-cache,go-mod-cache --go # specific targets
python3 scripts/clean.py --preset safe --skip ollama-models --go # exclude one
trash is used for all file removal (never rm); freed space sits in Trash until emptied
(--empty-trash, or the user empties it). Sizes are du estimates — approximate on APFS.
safe runs automatically; medium needs --allow-medium; never isrefused even if named by id. advisory targets only print guidance, never execute.
realpath → must resolve under anallowed_roots entry → must not be a symlink → never $HOME or /. Anything failing is
skipped and reported, not deleted.
clean.py prints the plan and touches nothing unless --go.python3 scripts/survey.py --json. Relay the compressed summary: disk free,safe/medium recoverable totals, any flags (e.g. crash-loop), and the top targets.
Do not dump the whole JSON.
safe total and runclean.py --preset safe --go (offer --empty-trash). Safe targets are regenerable.
refuse to auto-decide):
medium targets (ML models, device support, project node_modules) — confirm before --allow-medium.ML-model targets (ollama-models, huggingface-models) carry a last_used_days field
(newest file atime under the target, aggregate across all models in that store — not
per-model) as a "how stale is this" signal; surface it before suggesting deletion.
uncategorized discoveries — unknown dirs >100 MB; ask or investigate before adding.advisory notes — surface them (Telegram cache, simulators via simctl, uv/tools,Chrome whole-dir, Xcode Archives, mo clean deep-clean); never act on them automatically.
For mole-deep-clean: suggest the user run mo clean themselves (interactive TUI, permanent
deletes, sudo for system caches) — never invoke it from the agent. **Never shell out to mo
at all** (not even --dry-run): it's TUI-only and blocks waiting for a real terminal even in
dry-run mode — confirmed hanging under a piped subprocess, stdin=DEVNULL, and even a
script(1)-allocated pty. A mole flag in survey.py's output only reads the mtime of
mole's own leftover ~/.config/mole/clean-list.txt (last-run recency), never invokes it.
clean.py with the resolved selection. Relay the result (freed_human, disk before→after).Add or correct targets by editing targets.json — no code change needed. Each target:
{id, category, risk, method, paths|find, regenerates, priority, note}. Methods:
trash — trash literal paths (globs allowed).find-trash — exact-name dir sweep with a min_mb floor (crash dumps, project node_modules).command — run a CLI (npm cache clean…); set scope_path so freed bytes can be measured.simctl — xcrun simctl delete unavailable (removes only sims for uninstalled runtimes; safe).downloads-scan — config-driven (config.json → downloads_scan): files older than age_dayswhose name doesn't match exclude_patterns. The dry-run lists every file by name for review.
advisory — never executes; only prints guidance.Keep installed software at risk: never (learned the hard way: uv/tools, uv/python,
~/.rustup/toolchains, ~/.bun, ~/.deno are NOT caches). Every non-advisory target's paths
must resolve under allowed_roots or preflight will (correctly) refuse them.
config.json ships generic, public-safe defaults. Anything personal — names, family
names, a non-English tax/legal/financial vocabulary — or machine-specific goes in
config.local.json (gitignored). load_config() deep-merges it over config.json:
lists are unioned (local terms only *add* protection to the Downloads exclude list), scalars
override. See config.local.example.json for the shape.
Setup mode — when the user first uses the skill, asks to personalize it, or has sensitive
files in ~/Downloads, offer to build config.local.json by asking (one short batch):
steuer, rechnung, vertrag).node_modules sweep) and any extra app caches.Then write config.local.json (copy config.local.example.json and fill it in). Confirm what
was saved. Never commit it.
Per-machine paths in targets.json (allowed_roots, the node-modules-projects find root
~/ai_projects) are examples — adjust them to the user's layout. Targets whose paths don't
exist on this machine simply measure 0 and are skipped.
docker images / docker ps -as /docker system df -v, let the user pick per-name (docker rm/rmi/volume rm/builder prune),
or blunt docker system prune -a -f. A named volume removed = data gone; confirm by name.
(Everything else — simulators via the simctl method, Downloads via downloads-scan,
crash dumps, all caches — now runs through the scripts.)
Take glebis/disk-cleanup from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.