SPEC.md · v2, C#
disk-clean-checklist — project spec
Current status
Scan → checklist → select → execute → warnings on both scan and delete failures → before/after free space. Goal expanded 2026-07-02 to target Linux/Mac too — see “Cross-platform MVP scope” before starting Unit 4 or the Avalonia port. No open safety issues — the WSL node_modules scope bug is fixed, see “Resolved.”
Native node_modules/build-artifact scanning (NativeBuildDirs / FindNativeBuildDirs, Scanners.cs:156 / :1019) shipped after being spec’d 2026-07-09 — wired into Program.cs and MainWindow.xaml.cs, tested in ScannersTests.cs. The “Planned unit” section was never marked done after shipping; corrected 2026-07-15.
WindowsTempFolder() (system-wide C:\Windows\Temp, distinct from user %TEMP%) — Done verified live 2026-07-15 via console scan + Clean Selected. Result: 121/123 entries cleared without elevation, 2 correctly skipped as genuinely locked (an active session log, Office Click-to-Run’s live streaming log) — matches ActionExecutor.DeleteContents’s partial-delete format exactly (Cleared N entries, skipped M. Blocked by: …), no crash, no silent failure. Correction to the original caution: elevation was not required in practice — most of C:\Windows\Temp was writable under a normal user account on this machine.
Next up: Avalonia UI port. A real blocker was found 2026-07-15 before any port work started — see “Avalonia port — blocker found” below.
2026-08-07: the published v0.0.0 release does not run on a clean download — see “v0.0.0 release doesn’t run” in the Build Log tab. Self-contained single-file rebuild is built and verified locally; not yet uploaded to replace the broken release asset.
Location
C:\Users\ASUS ROG\Downloads\disk-clean-checklist\ — .NET solution, C#
DiskCleanup.Core\— scanners, action executor, selection logic (shared)DiskCleanup\— console app (Units 1–2)DiskCleanup.Wpf\— GUI widget (Unit 3): DataGrid checklist, risk filter, Clean SelectedDiskCleanup.Tests\— xUnit tests against Core
What it does
- Scans these categories and computes sizes:
- Recycle Bin
- User Temp (
%TEMP%), system-wideC:\Windows\Temp, andSoftwareDistribution\Download - VS Code
CachedExtensionVSIXs - WSL:
~/.cache,~/.npm,~/.local/share/pnpm/store, and anynode_modules/targetdirs found under$HOME(depth 6) — classified Safe (project marker present), Review (marker missing), or Info (inside a known remote-dev-server dir like.vscode-server, excluded rather than walked into) - Docker reclaimable space (
docker system df) - Top N largest folders in Downloads
AppData\Local\Packagesfolders untouched for 6+ months (bloatware candidates)- Top installed apps by size (registry) — informational only
- AI-related folders (
.claude/.codex), flagging unnecessary chat logs, temp files, bloated memory files
- Prints a numbered checklist, e.g.
[1] Recycle Bin — 8.2GB — SAFE,[2] Downloads\Foo — 5GB — REVIEW - Prompts the user to type numbers (e.g.
1,2,5orall-safe) to act on - Executes the chosen actions (delete/clear/prune) and reprints free space before/after
New: scheduled check (background automation)
- A separate run mode (
--check) does the scan silently, no prompts - If free disk space < 45GB, sends a Windows toast notification with a short summary (top 3–5 reclaimable items + total reclaimable size)
- Triggered by Windows Task Scheduler at user-configured times (app does not self-schedule — setup command is printed for the user to run once)
- Clicking/opening from the notification just launches the normal interactive checklist run — no auto-cleanup ever happens from
--check
What it does NOT do
--checkmode never deletes or modifies anything — notify only- Never touches Downloads project folders or installed apps without an explicit number pick
- Anything requiring admin (Docker vhdx compaction, MSI uninstalls) is printed as a command to copy into an elevated terminal, not executed directly
Done =
DiskCleanup.exe → see the checklist → type numbers → selected items get cleaned → see new free space before/after.
DiskCleanup.exe --check (run by Task Scheduler) → if space < 45GB, toast notification with checklist summary appears.
Build plan — incremental, one unit at a time, stop for evaluation between
- Scanner + checklist printer (no actions yet) Done
- Selection input + action executor Done
- WPF widget: checklist with checkboxes, risk filter, Clean Selected Done
--checkmode + toast + Task Scheduler setup Deferred — see “Cross-platform MVP scope” (toast mechanism is platform-specific; wait for the platform-abstraction seam)- StalePackages AppX cross-check (
Get-AppxPackage) — distinguishes “app uninstalled, orphaned data” from “app still installed, folder just looks stale” Done - DockerVhdxBloat scanner (flags Docker Desktop’s WSL2
.vhdxonce it never-shrinks past 20GB, suggests a diskpart compaction command) Done - AiFolders root-folder safety fix (allowlist of verified-safe subpaths only, never
.claude/.codexroot) + session-file staleness/active-session detection Done - Scan-side + delete-side warning collection (Review.md finding #3:
catch{}no longer silently swallows scan/delete errors; both CLI and WPF surface which specific file/folder blocked an action) Done — verified live 2026-07-02 against real locked Temp files and an access-denied SoftwareDistribution run
Unit 2 amendment
all-safeno longer auto-executes. It pre-selects all SAFE-risk items, prints them as a confirmation list, and requires a final y/n before any deletion happens — same as a manual numeric selection- No item, regardless of risk level, is deleted without an explicit confirm step (reason: a SAFE-categorized item like a WSL project’s
node_modulesmay still be one the user wants to keep)
Phase 2 (remaining, future)
- Filter by category/size (risk-level filter done in Unit 3)
- Background/scheduled run of the widget (ties into Unit 4’s
--checkmode, deferred above)
Known issues (open, safety-relevant)
None currently open. See “Resolved” for the WSL node_modules scope fix.
Resolved
ScanClaudeFolder (Scanners.cs:513-586) now matches each session’s .jsonl against its sibling <sessionId>/ folder (subagents/, tool-results/): when both exist, deleting the conversation deletes the folder in the same action (CheckItem.SecondaryPath, wired through ActionExecutor.MoveToRecycleBin); when only the folder is left (parent conversation already deleted by a prior cleanup), it gets its own SAFE row. memory/ stays excluded either way. Not WSL-specific — one fix in the shared scanner covers both native Windows and WSL’s .claude, since Wsl() calls the same function.
Verified live 2026-07-15 against the real .claude folder: found and cleanly deleted 19 pre-existing orphaned session folders across multiple projects, zero failures, all recoverable via Recycle Bin. .codex was checked too — the folder no longer exists on this machine (user-deleted), so ScanCodexFolder was left untouched.
Test coverage: ScannersTests.cs — ScanClaudeFolder_CascadesSessionFolderIntoMatchingJsonl, ScanClaudeFolder_OrphanedSessionFolder_FlaggedAsSafeRow, ScanClaudeFolder_ActiveSessionOrphanFolder_NotFlagged.
Wsl()’s node_modules/target discovery (Scanners.cs FindBuildDirs) walks the entire WSL home directory (depth 6), not just ~/projects. Confirmed live 2026-07-02: ~/.vscode-server contains 7 real node_modules dirs (VS Code Server’s own bundled extensions), which used to surface as ordinary SAFE/DeleteFolder rows — indistinguishable from a project’s own dependencies, the same failure class as a prior incident where WSL cleanup corrupted ~/.vscode-server.
Fix: kept the whole-home walk, but FindBuildDirs now classifies every hit three ways — known remote-dev-server dirs (.vscode-server, .vscode-server-insiders, .cursor-server, .windsurf-server) pulled out as Info/no-action rows instead of walked into; .cache/.npm skipped (already their own CheckItems); every other node_modules/target hit is Safe only if a project marker sits next to it (package.json, or Cargo.toml/pom.xml/build.sbt), otherwise Review with a reason explaining the marker couldn’t be confirmed.
Verified: VS Code’s own troubleshooting docs confirm deleting ~/.vscode-server is an official, auto-healing recovery step — so the real risk guarded against is a partial delete of one piece while the server may still be running, not “this folder is untouchable.” The exclusion list is a UX nicety; the marker-file check is the actual safety net for unlisted tools on other machines.
Test coverage: ScannersTests.cs — FindBuildDirs_*, HasProjectMarker_*. all-safe is safe to run on a WSL scan again.
Native node_modules/build-artifact scanning Done
Spec’d 2026-07-09, shipped since, correction recorded 2026-07-15.
NativeBuildDirs()/FindNativeBuildDirs() (Scanners.cs:156/:1019) exist, are wired into Program.cs and MainWindow.xaml.cs, and have test coverage in ScannersTests.cs. This section was left unmarked after shipping — kept below for the original design reasoning, not as an open item.
Before this, node_modules/target discovery only ran inside WSL (Wsl() → FindBuildDirs). Native Windows project folders (e.g. Downloads\bank-transaction, Downloads\AccountabilityApp) got zero build-artifact visibility — only ever a lump “this folder is large” REVIEW row via DownloadsTopFolders, never “here’s the regenerable part inside it.”
What it does
- Walks a bounded set of real project roots —
Downloads,Documents,Desktop— depth-limited, same shape as WSL’sWalk(root, depth, 6) - Reuses
HasProjectMarkerand the SAFE/REVIEW split as-is (already OS-agnostic — a plainFile.Existscheck): SAFE only whenpackage.jsonorCargo.toml/pom.xml/build.sbtsits next to it, REVIEW otherwise - Excludes
AppData,Program Files,Program Files (x86),Windows,ProgramDatafrom the walk entirely — never descended into, since installed Electron/VS Code-family apps ship their own bundlednode_modulesunder those paths (same failure class as the WSL.vscode-serverincident). A native equivalent ofWslAppServerDirNamesmay still be needed for portable/no-installer apps living elsewhere
What it does NOT do
- No drive-wide
C:\walk — bounded to the same known user-content roots as existing scanners - No auto-detection of arbitrary project roots outside those folders (e.g. a repo cloned to
C:\dev) — out of scope for v1
Planned: system-root clutter + user dotfolder scanners
Spec’d 2026-07-09, not yet built.
- SystemRootClutter — curated exact-name allowlist of
C:\root vendor/installer leftovers. Tier A (intelFPGA,HP eSupport,RyzenPPKG Driver,WCH.CN,DumpStack.log,vfcompat.dll,appverifUI.dll) → REVIEW, delete offered. Tier B (inetpub,flexlm) → INFO only, delete never offered (can be actively-serving IIS sites / license servers).Python3xx,Ruby34-x64,xamppexplicitly excluded — installed runtimes, uninstall properly, not a folder-delete target. - UserDotfolders —
.cargo/.cisco/.cursoretc., cross-checked against install/PATH state before ever flagging REVIEW (same honesty as StalePackages — a failed check says “couldn’t confirm,” never silently SAFE)..configexcluded entirely (generic dumping ground, no single “still in use” signal). Confirmed via live check 2026-07-09:.cursoris NOT temp/cache despite the app being uninstalled —plans/andprojects/hold real authored planning docs and per-project session history — so “app uninstalled” alone is not sufficient signal for SAFE.
Known gaps vs. a full manual OS-level cleanup pass
Audited 2026-07-09 against Storage Sense, Disk Cleanup, restore points, optional features, language packs, dev package caches. Not committed to building all of it — recorded so future prioritization starts from a real audit.
Not covered, ranked by likely payoff
Windows.old/ previous Windows installation — often 10–20GB, single largest possible one-time win, not investigated whether it currently exists on this machineNative-Windows node_modules/build-artifact scanningDone- Native dev package caches: NuGet (
~/.nuget/packages), pip, Maven (~/.m2, confirmed on PATH), Gradle (~/.gradle), Android SDK. Only Docker and WSL’s~/.npmare currently handled. Next by this ranking C:\Windows\Temp (system-wide temp)Done — WindowsTempFolder()- AI model weight caches (Ollama/HuggingFace-style) — different from the
.claude/.codexsession-log scanning already done; not investigated - WinSxS component cleanup, old driver store packages, Delivery Optimization files, thumbnail cache, DirectX shader cache, System Restore points, optional Windows features (Hyper-V, Sandbox, IIS, SMB1), unused language packs — all untouched, lower estimated payoff/higher effort or risk than the above
Partial vs. the manual checklist’s version
DownloadsTopFolders/PersonalFoldersshow top-N largest items only, not a full drive-wide*.iso/*.zip/gigantic-size sweepInstalledAppsBySizeis visibility-only by design — the manual checklist’s “uninstall large programs” step still requires acting via Settings
Deferred (explicitly out of scope for now)
- Locked-file-owner detail (“who is holding this handle” for a delete failure) — feasible on all 3 target OSes (Windows: Restart Manager API
rstrtmgr.dll; Linux: walk/proc/[pid]/fd; macOS:lsof/libproc) but each is a fully different mechanism. Deferred rather than built Windows-only; revisit as a plugin behind the platform-abstraction seam. - Unit 4 (
--check+ toast notification) — the toast mechanism is also platform-specific (Windows Toast vs. Linuxlibnotifyvs. macOSUserNotifications). Deferred until the seam below exists.
Cross-platform MVP scope
Added 2026-07-02. Goal expanded this session: scale to Linux and Mac, not stay Windows-only. That changes what “MVP” means — full feature parity across 3 OSes is not the bar; a shared Core with clean platform seams is.
Per-scanner scope decision — finalized 2026-07-13
Every scanner in Scanners.cs, sorted by what it actually needs — verified against each scanner’s real mechanism, not a guess.
| Category | Scanners | Reasoning |
|---|---|---|
| A already portable |
VsCodeCache, NativeBuildDirs, Docker, DownloadsTopFolders, PersonalFolders, AiFolders |
Ports with zero changes — already use Environment.SpecialFolder/Path.GetTempPath()/plain Path.Combine, none Windows-specific. MyMusic/MyVideos worth a Linux smoke test (XDG mapping less consistent) but not a blocker. |
| B needs a seam |
RecycleBin, TempFolders |
Cross-platform concept, per-OS mechanism. RecycleBin’s move-to-trash already has ITrashProvider; empty-the-bin (SHEmptyRecycleBinW) still needs its own seam. Linux target: freedesktop.org trash spec. Mac: ~/.Trash. TempFolders splits into a portable %TEMP% half and a Windows-only WindowsUpdateCache(). |
| C Windows-only, dropped |
Wsl (+ FindBuildDirs) |
WSL doesn’t exist on Linux/Mac. Not “ported” — just doesn’t run. |
| D new scanner, not a port |
InstalledAppsBySize, StalePackages, DockerVhdxBloat |
Windows-only mechanism, concept has an analogue but that’s new scanner work. Linux: dpkg/rpm; Mac: Homebrew casks. StalePackages is UWP/AppX-specific, no Linux/Mac equivalent model. DockerVhdxBloat’s Mac analogue lives under ~/Library/Containers/…, a different shape entirely. |
Avalonia port — blocker found (2026-07-15, before any port work started)
Checked the actual .csproj files rather than assuming the seam work made the port ready. It didn’t:
DiskCleanup.Core.csproj: net10.0-windows
DiskCleanup.csproj: net10.0-windows
DiskCleanup.Wpf.csproj: net10.0-windows
DiskCleanup.Tests.csproj: net10.0-windows
All four projects — including Core — target net10.0-windows, a Windows-only TFM. An Avalonia project on Linux/Mac cannot reference Core at all as it stands, independent of whether ITrashProvider is fully seamed. Core also still has Windows-only code mixed directly into shared files:
WindowsIdentityinRecycleBin()(Scanners.cs)Microsoft.Win32registry calls (StalePackages/InstalledAppsBySize— expected per Category D, but confirms the mixing)WindowsTrashProvider.csitself lives in Core, not a Windows-specific project
Not one unit
There’s a prerequisite unit first: make Core build as portable net10.0 — either multi-target Core, or physically split the Windows-only scanners + WindowsTrashProvider into a separate Windows-specific project that both the WPF app and the future Avalonia build reference. Category A scanners don’t need to move; only the Category B/D Windows-only code does. Not yet decided: multi-target vs. physical split, and exactly which files move — the next thing to spec before writing any Avalonia code.
Recommended next steps, ranked
Fix the FindBuildDirs safety issueDone 2026-07-08Introduce the platform-abstraction seam in CoreDone 2026-07-13 — both Win32 calls that used to live inActionExecutor(SHFileOperation,SHEmptyRecycleBinW) now go throughITrashProvider, implemented byWindowsTrashProvider. ZeroDllImports left inActionExecutor.Decide per-OS scanner scopeDone 2026-07-13 — see table aboveSplit TempFoldersDone 2026-07-13 —TempFolders()is now just the portable%TEMP%line;WindowsUpdateCache()is the new Windows-only scanner, wired into both apps- Split/multi-target Core so it builds as portable
net10.0Not started — actual next unit - Only then: the Avalonia UI port and Unit 4/toast, since both depend on the seam existing first Depends on #5
Context / background
- Language: C# (.NET, self-contained console app)
- Folder is stateless — each run scans fresh, no saved history between runs
- 45GB free-space threshold is the trigger for the
--checknotification
Risk levels used in the earlier manual cleanup pass, for reference
Safe Recycle Bin, build caches (node_modules, target/, npm/VSIX caches) — fully regenerable · Review Downloads project folders, AppData\Local\Packages bloatware candidates, Docker prune/compaction · Info only installed apps list — no action, just visibility