The known-offenders catalog behind Smart suggestions (Clean Up ▸ Smart
suggestions). Adding an offender is a JSON edit — no code change, no rebuild of
the matching logic. The loader and schema live in ../rulePacks.ts; the matcher
that walks a scan lives in ../cleanupRules.ts.
common.json— rules that apply on every OS.macos.json,windows.json,linux.json— that OS's known locations.
Exactly two packs load on any machine: common plus the one for the current
platform. A malformed pack fails the whole catalog with a reason — Smart
suggestions then reports itself unavailable and says why, while the rest of the
app boots normally. Half a catalog would silently stop suggesting things people
rely on, which is the worse failure.
Every rule carries id, kind, title, description, category,
confidence, and the fields its kind requires. Unknown keys are rejected, so a
typo fails loudly instead of silently doing nothing.
| Field | Values |
|---|---|
category |
regenerable, cache, junk |
confidence |
high, medium, low |
action |
trash (default) or advice |
os |
optional subset of darwin, win32, linux |
| Kind | Matches | Extra fields |
|---|---|---|
project-directory |
a folder by name, optionally only when a manifest sits beside it, when it holds a file it always holds, and when it holds none a same-named folder of another kind holds | names, requiresSibling?, requiresChild?, forbidsChild?, restoreCommand |
directory |
a folder by name, with nothing to confirm it | names |
file |
a file by name | names |
location |
absolute known paths | paths |
stale-files |
files under a path, over a size, past an age | withinPath, olderThanDays, minSizeBytes |
package-cache |
a shared package-manager cache (§C6 only) | ecosystem, paths, clearCommand |
Names and sibling patterns are matched case-insensitively, and said in the
"why" sentence as the pack writes them — so write them the way the files are
usually spelled (Cargo.toml, Podfile, .DS_Store). A served group's "why"
names the folders it actually matched, not every name the rule knows. A
sibling pattern ending in .* matches any extension (vite.config.*).
requiresChild names what the folder itself must hold, any one of them, matched
the same way. Give it to a rule whose folder name is also a person's word: a
folder of settings named env sits beside requirements.txt as readily as a
virtualenv does, so the virtualenv rule also needs the pyvenv.cfg that
Python's venv, and virtualenv since version 20, write in every environment they
make, and the coverage rule a report a test tool writes (lcov.info,
clover.xml, …). The "why" says both — "A folder named env holding pyvenv.cfg,
sitting next to requirements.txt."
forbidsChild names what the folder must hold none of, matched the same way:
what only another folder of the same name holds. A project's .gradle sits
beside build.gradle, and so does the Gradle user home when a project lives in
the home folder or CI points GRADLE_USER_HOME at the project's .gradle.
Only the user home holds gradle.properties (often passwords), init scripts,
caches, wrapper and daemon, and no build writes the first two again, so the
Gradle project-cache rule needs what a build writes there (buildOutputCleanup,
file-system.probe or vcs-1) and refuses those. The "why" says it — "holding
buildOutputCleanup, file-system.probe or vcs-1 and no gradle.properties, …".
The orphan scanner reads it too: a folder holding one is no orphan of that
rule either.
A rule may carry ecosystem (npm, cargo, python, …). Only the orphan
scanner reads it — Smart suggestions ignores it entirely, so tagging a rule can
never change what the Clean Up list shows. A tagged project-directory rule
must also carry:
ownerManifest— the file that proves the owning project still exists. Defaults torequiresSibling, which is the same thing for every rule that has one;node_modulesstates it separately because Smart suggestions flags it with or without a manifest.evidence— child names that identify the directory once its owner is already gone. Required for any ecosystem-tagged rule, and the load fails without it:targetis both Rust and Maven, and an orphan that matches no evidence is reported as nothing rather than guessed at.
package-cache rules are never "orphaned" — nothing owns a shared cache. They
are always reclaimable, and clearCommand is the supported way to empty one.
Paths use / separators and these tokens: {home}, {localAppData},
{windir}, {systemDrive}. A path whose token cannot be resolved on this
machine is dropped rather than matched half-expanded.
For a directory: project-directory → directory → location. For a file:
file → stale-files. Within each kind, pack order wins — which is how
target resolves to Rust or Maven by the manifest beside it. A directory
claimed by any rule is reported once and never descended into.
Nor is a folder of installed Python packages, claimed or not: what is in it is
its packages' own files. pip ships a package folder named build, and a rule
that names build would otherwise offer it as build output. Such a folder is
one named site-packages or dist-packages, wherever it is (a virtualenv, a
Python installed for the system), and any folder holding a *.dist-info
folder, the record an installer writes beside each package it puts in place —
uv's cache and pip install --target put packages outside any site-packages.
A scan rooted at a site-packages or dist-packages folder, or anywhere below
one, offers nothing. A scan rooted inside a package folder that only a
.dist-info beside it marks is walked as any folder is: the scan lists nothing
above its root.
Several rules may share an id to merge into one suggestion group (the two
regen-web-build entries do). They must agree on title, description, category,
action and restore command, or the load fails: the group's own text cannot
depend on which rule happened to match first.
TreeMap's only destructive action is move to Trash. Some of the biggest
things on a disk must not be moved to the Trash — the file is the data, or
the OS owns it. Those get action: "advice": the group is listed with its size
so the space is visible, but with no checkboxes, and with adviceCommand
showing the supported way to reclaim it. Windows' Temp folder for the account
(%LOCALAPPDATA%\Temp) is one: running programs keep files in it, and
programs write there expecting the folder itself to exist, so it is no
"Developer cache" to move to the Recycle Bin whole; Windows' Storage settings
clear what nothing is using.
- WinSxS (
C:\Windows\WinSxS). §C8 lists it, and it is genuinely huge, but it is the component store — most of it is hard links into the live system, the size Explorer reports is not the space it occupies, and deleting from it breaks Windows unrecoverably.DISM /Online /Cleanup-Image /StartComponentCleanupis the only correct tool, and it is not something TreeMap should run on a user's behalf. Listing it at all, even as advice, invites someone to try. - Anything under a live database or VM disk beyond the Docker/WSL entries above, which are advice-only for the same reason.