Systematic Windows disk cleanup for developer machines. Two mandatory phases: Discover first, then Execute only after user approval. All deletions must preserve parent folder timestamps.
Mandatory Rules
- NEVER delete anything without running Discover first. Always dry-run before execute.
- ALWAYS preserve timestamps on parent folders when deleting subfolders or their contents. See Timestamp Preservation.
- ALWAYS get explicit user approval between Discover and Execute phases.
Phase 1: Discover (Dry-Run)
This phase is read-only. No files are deleted. Run this first to understand what can be cleaned.
Steps
- Analyze -- If user provides a WizTree screenshot, read it and identify top space consumers using the Space Consumer Reference table below.
- Copy scripts -- Copy the bundled scripts from scripts/ into the user's working directory.
- Run dry-runs -- Execute the scripts without
-Executeto report what would be deleted:powershell -ExecutionPolicy Bypass -File ./cleanup.ps1 powershell -ExecutionPolicy Bypass -File ./clean_venvs.ps1 powershell -ExecutionPolicy Bypass -File ./clean_venvs.ps1 -Path D:\other\projects powershell -ExecutionPolicy Bypass -File ./downloads_report.ps1 - Present findings -- Show the user a summary table with per-category sizes and estimated total savings.
- Ask user -- Which categories to clean, which to skip. Do NOT proceed to Phase 2 without explicit approval.
What Discover Reports
| Script | Dry-run output |
|---|---|
cleanup.ps1 |
Per-category size (temp, Docker, npm, uv, Chrome, Windows junk, Android SDK) + Downloads analysis (top 50 files, top 20 file types, up to 15 duplicate groups). Shows Drive C: free space. Color-coded output with elapsed time. |
clean_venvs.ps1 |
Every .venv found, its size, whether it has pyvenv.cfg, total across all venvs. Color-coded by size (red >2 GB, yellow >1 GB). Per-venv elapsed time and running total. Terminal output only (no report file). |
downloads_report.ps1 |
Top 50 files, top 25 file types, up to 25 duplicate groups (>1 MB same size with waste calculation), .venv summary. Saved to timestamped .txt in script directory. Note: only searches for .venv folders (not venv or numbered variants). |
Phase 2: Execute (Deletions)
Only run this after the user has reviewed Phase 1 output and explicitly approved.
Steps
- Run with
-Execute:powershell -ExecutionPolicy Bypass -File ./cleanup.ps1 -Execute powershell -ExecutionPolicy Bypass -File ./cleanup.ps1 -Execute -SkipDocker -SkipAndroid powershell -ExecutionPolicy Bypass -File ./clean_venvs.ps1 -Execute powershell -ExecutionPolicy Bypass -File ./clean_venvs.ps1 -Execute -ToRecycleBin - Review report --
cleanup.ps1writescleanup_report_TIMESTAMP.txt(in script directory) logging every item with status (DELETED, LOCKED, WOULD DELETE, EXECUTED). The EXECUTED status is used for native CLI commands likenpm cache clean --forceanduv cache clean. - Check Drive C: -- Both
cleanup.ps1andclean_venvs.ps1show Drive C: free space before and after execution. - Verify -- User runs WizTree again to confirm space reclaimed.
- Handle failures -- If
-ToRecycleBinfails on deep paths (>260 chars), re-run those specific folders without-ToRecycleBinfor permanent delete.
Timestamp Preservation
MANDATORY for all deletion operations. When deleting a subfolder (venv, cache, temp), the parent folder's LastWriteTime, LastAccessTime, and CreationTime change. This must be prevented.
Pattern: Save and Restore
Every script that deletes subfolders MUST save parent timestamps before deletion and restore them after:
$parentDir = Split-Path $targetPath -Parent
$parentItem = Get-Item $parentDir -Force -ErrorAction SilentlyContinue
$origLastWrite = $parentItem.LastWriteTime
$origLastAccess = $parentItem.LastAccessTime
$origCreation = $parentItem.CreationTime
# --- perform deletion ---
Remove-Item $targetPath -Recurse -Force -ErrorAction Stop
# --- restore timestamps ---
$parentItem = Get-Item $parentDir -Force -ErrorAction SilentlyContinue
if ($parentItem) {
$parentItem.LastWriteTime = $origLastWrite
$parentItem.LastAccessTime = $origLastAccess
$parentItem.CreationTime = $origCreation
}
Where This Applies
clean_venvs.ps1-- Already implemented. Saves/restores parent timestamps around every venv deletion.cleanup.ps1-- Operates on system caches (temp, npm, Docker) where parent timestamp preservation is less critical, but the pattern should be applied if extending to user project directories.- Any new cleanup scripts you generate -- Always include this pattern.
Why This Matters
Developers rely on folder timestamps to know when a project was last modified. Deleting a .venv inside my-project/ should NOT change my-project/'s LastWriteTime from "2024-06-15" to "today". Without this, WizTree and file explorers show misleading modification dates.
Bundled Scripts
Three ready-to-use PowerShell scripts in scripts/. All use $env: variables for portability -- no hardcoded personal paths. Reports are written to $PSScriptRoot (the script's own directory).
| Script | Purpose | Key Parameters | Default Path |
|---|---|---|---|
| scripts/cleanup.ps1 | Main cleanup: temp, Docker, npm/uv caches, Chrome (multi-profile), Windows junk, Android SDK, Downloads report | -Execute, -SkipDocker, -SkipAndroid |
Uses $env: variables per category |
| scripts/clean_venvs.ps1 | Python .venv cleanup with pyvenv.cfg safety, timestamp preservation, Recycle Bin option | -Execute, -ToRecycleBin, -Path <dir> |
User's home directory |
| scripts/downloads_report.ps1 | Downloads analysis: top 50 files, file types, duplicates, .venv summary | -Path <dir> |
User's Downloads folder |
Script Features
- Native CLI fallback:
cleanup.ps1prefers native cache commands (npm cache clean --force,uv cache clean) when available, falls back toRemove-Itemif CLI not found. - npm cache path fallback: Checks
$env:LOCALAPPDATA\npm-cachefirst, then$env:APPDATA\npm-cache. - Chrome multi-profile: Discovers all Chrome profiles (Default + Profile *) and cleans 5 cache subdirectories per profile.
- Chrome running warning: Warns user if Chrome is running (cached files may be locked).
- Progress tracking: All scripts show elapsed time, per-item progress counters (
X/Y), and running totals during measurement and deletion. - Color-coded output: Categories color-coded by severity/size; venvs colored red (>2 GB), yellow (>1 GB), white (smaller).
Space Consumer Reference
| Category | Typical Location | Typical Size | Safe to Delete? |
|---|---|---|---|
| Temp files | $env:TEMP, C:\Windows\Temp |
5-30 GB | Yes (skip locked) |
| Docker | AppData\Local\Docker |
10-50 GB | Yes (if not using) |
| npm cache | AppData\Local\npm-cache |
2-10 GB | Yes (rebuilds on demand) |
| uv cache | AppData\Local\uv\cache |
2-10 GB | Yes (rebuilds on demand) |
| pip cache | AppData\Local\pip\cache |
1-5 GB | Yes (rebuilds on demand) |
| Chrome cache | AppData\Local\Google\Chrome\...\Cache |
2-10 GB | Yes (regenerates) |
| Recycle Bin | System | Variable | Yes |
| Windows Update | C:\Windows\SoftwareDistribution\Download |
1-5 GB | Yes |
| Thumbnail cache | AppData\Local\Microsoft\Windows\Explorer |
0.1-1 GB | Yes (regenerates) |
| Android SDK | AppData\Local\Android |
5-15 GB | Ask user |
| Python .venv | Inside project folders | 0.5-3 GB each | Yes (recreate with pip install) |
| Ollama models | ~/.ollama |
5-50 GB | Ask user |
| LM Studio | ~/.lmstudio |
5-30 GB | Ask user |
| node_modules | Inside project folders | 0.2-2 GB each | Yes (recreate with npm install) |
Leave Alone
AppData\Local\Programs-- installed applicationsAppData\Local\Microsoft-- OS/app datams-playwright-- if actively used for testing- WSL distributions -- contains Linux filesystems
- CapCut / other creative apps -- user content
Python .venv Safety Checks
- Verify with
pyvenv.cfg: Only delete folders containingpyvenv.cfgat root. Folders namedvenvwithout this file are NOT Python venvs. - Search patterns (
clean_venvs.ps1):.venv,venv,.venv310,.venv39, and regex\.?venv\d+. Deduplicated by FullName -Unique. - Search patterns (
downloads_report.ps1): Only searches for.venvfolders. Does NOT searchvenvor numbered variants. - Recycle Bin option:
-ToRecycleBinuses Shell API which fails on paths >260 chars. Always offer permanent delete as fallback.
Quick Reference
| Command | Phase | Purpose |
|---|---|---|
.\cleanup.ps1 |
Discover | See what would be cleaned |
.\cleanup.ps1 -Execute |
Execute | Delete files |
.\cleanup.ps1 -Execute -SkipDocker |
Execute | Delete files, skip Docker |
.\clean_venvs.ps1 |
Discover | Find all .venv folders (default path) |
.\clean_venvs.ps1 -Path D:\code |
Discover | Find .venvs in custom path |
.\clean_venvs.ps1 -Execute |
Execute | Permanently delete .venvs |
.\clean_venvs.ps1 -Execute -ToRecycleBin |
Execute | Send .venvs to Recycle Bin |
.\downloads_report.ps1 |
Discover | Analyze Downloads folder |
.\downloads_report.ps1 -Path D:\data |
Discover | Analyze custom directory |
All scripts run from Git Bash as: powershell -ExecutionPolicy Bypass -File ./script.ps1 [flags]
Critical Lessons Learned
- PowerShell 5.1 encoding: Without BOM defaults to ANSI. Never use Unicode chars (box-drawing, em-dashes) in generated scripts. ASCII only.
- NTFS small-file deletion is slow: Tens of thousands of small files (uv cache, venvs) take 10-30+ min. Warn users. This is not a hang.
- Git Bash + PowerShell interop:
$_gets mangled by bash. Always use-Fileflag, never-Commandwith complex expressions. - Recycle Bin Shell API limit:
Microsoft.VisualBasic.FileIOfails on paths >260 chars with "The system call level is not correct". Offer permanent delete as fallback. - Recycle Bin caveat for
-ToRecycleBin: Whenclean_venvs.ps1 -Execute -ToRecycleBinsends venvs to the Recycle Bin, space is only freed when the user empties it. Note:cleanup.ps1 -Executeempties the Recycle Bin automatically viaClear-RecycleBin -Force. - Docker cleanup order:
docker system prune -a --volumes -ffirst, stop Docker Desktop, then removeAppData\Local\Docker. - Cache deletion is safe: npm, uv, Chrome caches all regenerate on demand. Zero functional impact. pip cache is not cleaned by the bundled scripts but is listed in the Space Consumer Reference for manual cleanup if needed.
Common Mistakes
- Deleting without dry-run first -- always run Discover phase.
- Forgetting to preserve parent timestamps -- always save/restore around deletions.
- Writing Unicode in PowerShell scripts -- always ASCII for PS 5.1.
- Deleting non-venv folders named "venv" -- always check for
pyvenv.cfg. - Promising Recycle Bin for deep paths -- Shell API fails on long paths.
- Not showing progress -- users think the script is hung during slow NTFS deletions.