npx skills add ...
npx skills add moonlight-lupin/agent-skills --skill disk-cleanup
Use when df says disk is above 80% full or the user wants to reclaim storage space on a VM.
npx skills add moonlight-lupin/agent-skills --skill disk-cleanup
Reclaim disk space on a Linux VM. The skill runs a triage — survey every space consumer, split targets into safe vs ask, execute the approved set, then verify the delta.
Done: used and available numbers recorded for all mounts for the before/after comparison.
Requires root for apt, journalctl, and /var/log operations. Non-root users can survey but cannot clean system paths.
Build a ranked list of every consumer over ~50M. Survey all mounts, not just /.
Done: every consumer over 50M identified with its size. All mounts surveyed. Inode exhaustion and deleted-but-open files checked.
Split every found target into two buckets. Present the table to the user before executing.
Safe — execute without asking:
| Target | Command | Reclaims |
|---|---|---|
| uv cache | uv cache prune | Up to full cache size |
| pip cache | pip cache purge | Up to full cache size |
| apt cache | apt autoclean | ~100M typical |
| Journal logs | journalctl --vacuum-size=50M | Down to 50M |
| Rotated logs | find /var/log \( -name "*.gz" -o -name "*.1" -o -name "*.old" \) -delete | ~20M typical |
| Browser automation caches | rm -rf ~/.cache/puppeteer ~/.cache/ms-playwright | ~650M; re-downloads if needed |
| Dangling Docker images | docker image prune -f | Varies |
| Docker build cache | docker builder prune -f --filter until=168h --reserved-space 10GB | Often multi-GB; bounded: skips cache younger than 7 days, always keeps 10GB |
Ask first — user must confirm:
| Target | Risk | Command |
|---|---|---|
| git gc aggressive | Destroys recoverable objects (dropped stashes, orphaned commits after bad reset). Aggressive repack temporarily grows disk use. | cd /repo && git gc --aggressive --prune=now |
| apt autoremove | May remove wanted packages. Review the list before confirming. | apt autoremove (without -y) |
| Old DB backups | Confirm current DB is healthy first. Use explicit paths, not globs. | rm /path/to/specific-backup.bak |
| Stale /tmp dirs | Directory mtime does not track activity inside. List before deleting. | find /tmp -maxdepth 1 -mtime +3 -type d -name "*_*" | xargs ls -ld then confirm |
| Docker images for stopped containers | May want to restart later | docker rmi <image> |
| Session checkpoint stores | May contain recoverable state | — |
| Large skill references (PDFs, maps, RAG indexes) | User data | — |
| Large project uploads | User data | — |
| Docker images for running services | Disruptive | — |
Done: table presented with safe-only total and with-ask total. User has approved a set.
Run the commands from the Safe table for each approved target. Capture before/after size for each.
For Docker stopped containers + images (requires two steps):
Done: every approved target cleaned. Before/after size captured for each.
Compare per-mount against baseline from step 1.
Done: total reclaimed reported per mount. Used/free compared against baseline.
| Problem | Cause | Fix |
|---|---|---|
df shows free blocks but writes fail with ENOSPC | Inode exhaustion — millions of small files. Check df -i. | Locate with find <mount> -xdev -type f | head -100000 | wc -l per directory, then clear the offending cache/spool. Disk-size cleanup will not help. |
df says full but du shows nothing | A process holds a deleted file open. Check lsof +L1. | Identify the holding process with lsof +L1, restart it (or truncate via cat /dev/null > /proc/<pid>/fd/<n>). No file deletion reclaims this. |
git gc reclaims nothing | No orphaned objects — just large history | Accept the size or run git repack -ad for marginal compression |
| tmp_pack files persist after gc | Git process still running or lock file | fuser .git/objects/pack/*.pack to find the process, retry after it ends |
| Docker rmi fails "image is in use" | Stopped container still references it | docker rm the container first, then docker rmi |
apt autoremove lists wanted packages | Auto-removable deps flagged incorrectly | Review the list before confirming; skip with apt-mark manual <pkg> |
journalctl --vacuum-size fails | Journald service issue | systemctl restart systemd-journald then retry |
uv cache prune fails | uv not in PATH | Use python -m uv cache prune |
docker rm $(docker ps -a --filter "status=exited" --filter "ancestor=IMAGE" -q)
docker rmi IMAGE