All 260 entries in packages/clean/src/categories.ts are fixed absolute paths. That covers global, per-tool caches well — but the largest reclaimable category on a developer machine is per-project build output scattered across the filesystem, and a static path list structurally cannot express it.
The code already knows this. categories.ts:176:
// Zig keeps a global cache next to the local `.zig-cache` each project owns.
// The XDG path is used by zig itself, the Library path by Homebrew builds.
t('zig-global-cache', 'Zig Global Cache', `${HOME}/.cache/zig`, ...)The comment names the per-project caches, then only the global one gets a target — because there is no way to express the other.
Evidence
Measured on a working developer Mac. What the fixed-path targets cover:
| Target | Size |
|---|---|
~/Library/Caches | 864M |
~/.bun/install/cache | 991M |
~/Library/Developer/Xcode/DerivedData | 91M |
~/.cache/zig (global) | 114M |
| Total | ~1.9 GB |
What it cannot see:
| Pattern | Dirs | Size |
|---|---|---|
.zig-cache | 32 | 13.1G |
node_modules | 18 | 7.4G |
zig-out | 5 | 0.5G |
build | 2 | 0.3G |
dist | 6 | 0.1G |
| Total | ~21.4 GB |
The uncovered category is roughly 11× the covered one. The Zig numbers make the point sharpest: the global cache this tool cleans was 114 MB, while the 32 per-project caches it cannot see held 13.1 GB — a 115× difference on the same toolchain.
(node_modules was counted at -maxdepth 3, so the true figure is higher — nested workspace packages weren't included.)
Proposed: a second scanner type
Not more entries — a different kind of target. Alongside the fixed-path list, a pattern scanner with:
- Configurable roots, defaulting to likely code locations (
~/Documents,~/Code,~/Projects,~/src,~/dev) and user-editable. Never scan$HOMEwholesale. - A depth limit, so a monorepo with thousands of nested matches doesn't take minutes to scan.
- Named patterns with per-pattern risk levels —
.zig-cache,zig-out,target,.next,.turbo,.parcel-cache,dist,build,__pycache__,.gradle. - An age threshold, so a project built this morning isn't offered up.
- Results grouped by project, not a flat list of 32 paths. "
home— 24 caches, 8.2 GB" is actionable; a wall of paths isn't.
Risk tiers matter here
These are not equally safe, and the UI should say so:
- Safe —
.zig-cache,zig-out,target,.next,.turbo,.parcel-cache,__pycache__. Pure build output, regenerated automatically, no network needed. - Careful —
dist,build. Usually build output, but some projects commit them or ship from them. Check for a matching entry in.gitignorebefore offering. - Separate feature —
node_modules. Deleting it requires a reinstall that needs network and can fail against a changed registry. Worth its own treatment rather than inclusion in a routine sweep; tracked separately.
Worth surfacing
Rebuild cost is the only real downside, and it should be stated. Clearing a 3.6 GB craft/packages/zig/.zig-cache means the next build is a cold compile measured in minutes. Showing "frees 3.6 GB · next build will be slower" lets the user make that trade knowingly.
Keeping the global cache (~/.cache/zig) while clearing project-local ones is the right default for exactly this reason — the global cache makes the subsequent rebuilds faster, and it's two orders of magnitude smaller.