| name | dev-clean-wsl |
| description | Reclaim disk space on WSL2 by purging dev caches and orphaned build artifacts, then guide the user through compacting the ext4.vhdx from Windows. Use when: (1) the user says 'dev-clean-wsl', 'clean wsl', 'wsl disk full', 'free disk space', 'reclaim space', 'purge caches', or 'disk almost full' on a WSL machine; (2) a build or tool fails with ENOSPC / no space left; (3) the user wants to shrink the WSL virtual disk. Covers cargo target/registry, pnpm/npm caches, node_modules, and the vhdx-compaction step that actually returns space to the Windows C: drive. |
| argument-hint | [code-root] |
dev-clean-wsl
Free space on a WSL2 machine, then hand the user the one step they must run
themselves to make that space real.
The crux (why WSL is different)
On WSL2 the Linux filesystem is a single ext4.vhdx file that lives on the
Windows C: drive. Two consequences drive this whole skill:
/ looking healthy is not enough. The disk that actually fills up is
usually C:, not the Linux /. The scan reports both — read C: first.
- Deleting files inside WSL does not shrink the vhdx. The vhdx only grows; freed
blocks stay allocated until the vhdx is compacted from Windows. So every purge
below is "banked" inside WSL and becomes real free space on
C: only after the
compaction step. Always finish with that step — skipping it makes the cleanup
look like it did nothing from Windows' side.
wsl --shutdown (required for compaction) kills this session, so compaction is
always handed to the user as commands to run in a Windows terminal — never run
it from inside WSL.
Step 1 — Scan (read-only)
bash $HOME/.claude/skills/dev-clean-wsl/scripts/scan.sh [code-root]
code-root is optional (defaults to $HOME/repos, else $HOME) — the directory
searched for node_modules and orphaned target/ dirs. The deep home scan takes
~1 minute. It deletes nothing and prints: / vs C: usage, the vhdx path + size,
top home consumers, and per-category reclaimable sizes.
Step 2 — Present findings and confirm
Turn the scan into a short table grouped by safety, then get the user's pick
(AskUserQuestion works well for multi-select). Do not delete large or ambiguous
things silently — these caches are recoverable but rebuilds/re-downloads cost time.
Safety tier, highest-value-and-safest first:
| Tier | Item | Why safe |
|---|
| Free (zero cost) | Orphaned in-repo target/ when a global target-dir redirect is active | Nothing builds there anymore — pure dead weight (often the single biggest win) |
| Free (zero cost) | Orphaned pnpm store/vN (older than the active version) | pnpm store prune never touches old store versions; they are abandoned |
| Safe | cargo debug/incremental, release/incremental | Pure incremental cache; only slightly slows the next build |
| Safe | cargo registry/src + registry/cache | Re-fetched on demand (keep registry/index — refresh is slow) |
| Safe | pnpm store prune on the active store | Drops only unreferenced packages |
| Safe | npm cache clean --force (~/.npm) | Re-downloaded on demand |
| Costs a rebuild | active cargo target debug/deps (the big GB) | Only cargo clean / rm reclaims it; forces a full cold rebuild |
| Reinstallable | idle projects' node_modules | pnpm/npm install restores them; clear projects you are not actively on |
Leave alone unless the user explicitly names them:
- User data, not cache — e.g.
~/youtube-dl, ~/Downloads, media. Never auto-delete.
- Published/intentional build outputs —
dist/, packaged release tarballs.
These are project-specific and may be wanted; do not treat a generic dist/ as cache.
Step 3 — Purge (only what the user confirmed)
Safety rules:
- Relative-path deletes only.
cd into the parent, then rm -rf ./name —
never rm -rf /absolute/path. This is a guardrail against a mistyped absolute path.
- Check nothing is using a dir before clearing it. Before wiping a
target/,
build dir, or CI-runner workspace, confirm no live build/process owns it
(ps -eo cmd | grep -iE 'cargo|Runner\.Worker'). Clearing a dir mid-build breaks it.
- Confirm "orphaned" before claiming zero-cost. An in-repo
target/ is orphaned
only if a global target-dir redirect (env CARGO_TARGET_DIR or
~/.cargo/config.toml [build] target-dir) points elsewhere AND its newest artifact
predates the switch. The scan prints both signals.
Per-category commands (adapt paths from the scan output):
cd /path/to/repo && rm -rf ./target
cd "$(dirname "$(pnpm store path)")" && rm -rf ./v10
cd "$ACTIVE_TARGET/debug" && rm -rf ./incremental
cd "$HOME/.cargo/registry" && rm -rf ./src ./cache
pnpm store prune
npm cache clean --force
cd /path/to/idle-project && rm -rf ./node_modules
For the big active cargo debug/deps, only offer cargo clean / deleting the whole
target dir if the user accepts a full cold rebuild — call out the tradeoff explicitly.
After purging, re-run a quick df -h / and re-measure the touched dirs so the
reported total reflects reality.
Step 4 — Hand off the vhdx compaction (the step that frees C:)
This is the finale every run must end on. Pull the vhdx path from the scan output and
give the user a ready-to-paste block for a Windows PowerShell terminal (not WSL):
wsl --shutdown
# Easiest (modern WSL — also keeps it sparse going forward):
wsl --manage <DistroName> --set-sparse true # DistroName from: wsl -l -v
# …or a reliable one-time compaction via diskpart:
diskpart
select vdisk file="C:\Users\<you>\AppData\Local\wsl\{GUID}\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
Set expectations honestly: compaction reclaims only the free blocks inside the
filesystem (i.e. roughly what was just purged), so the vhdx — and C: usage — stays
about as large as the real data still inside WSL. State the rough before/after C:
free numbers from the scan so the user knows what to expect.
If the scan could not find the vhdx, have the user locate it on Windows with
Get-ChildItem -Recurse $env:LOCALAPPDATA -Filter ext4.vhdx.
Notes
- Self-hosted CI runners sometimes show up as large dirs. Their
_work/ is
ephemeral CI workspace (safe to clear when idle); fully removing a runner means
stopping + uninstalling its root-owned systemd service first
(sudo ./svc.sh stop && sudo ./svc.sh uninstall), which needs the user's sudo
password — hand them those commands rather than guessing.
- This skill assumes WSL2. On a non-WSL host the scan still reports reclaimable
caches, but the vhdx/compaction step does not apply — the script flags this.