windowsweep 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AI-INTEGRATION-GUIDE.md +15 -11
  2. package/CHANGELOG.md +56 -0
  3. package/LICENSE +21 -21
  4. package/README.md +70 -42
  5. package/VERSION +1 -1
  6. package/lib/actions.ps1 +212 -212
  7. package/lib/config.ps1 +108 -99
  8. package/lib/constants.ps1 +103 -93
  9. package/lib/fs.ps1 +189 -180
  10. package/lib/log.ps1 +140 -140
  11. package/lib/safety.ps1 +541 -493
  12. package/lib/scan.ps1 +109 -100
  13. package/lib/ui.ps1 +275 -275
  14. package/modules/android_avd.ps1 +36 -36
  15. package/modules/app_caches.ps1 +58 -58
  16. package/modules/browsers.ps1 +33 -33
  17. package/modules/build_tools.ps1 +35 -35
  18. package/modules/crash_trap.ps1 +89 -89
  19. package/modules/disk_usage.ps1 +49 -49
  20. package/modules/docker.ps1 +93 -93
  21. package/modules/editors.ps1 +150 -150
  22. package/modules/globals_audit.ps1 +236 -236
  23. package/modules/health.ps1 +101 -101
  24. package/modules/installed_programs.ps1 +118 -118
  25. package/modules/menu.ps1 +63 -63
  26. package/modules/notify.ps1 +60 -60
  27. package/modules/orphaned_appdata.ps1 +189 -189
  28. package/modules/personal.ps1 +126 -126
  29. package/modules/pkg_managers.ps1 +73 -73
  30. package/modules/projects.ps1 +160 -160
  31. package/modules/release_helpers.ps1 +438 -438
  32. package/modules/reports.ps1 +212 -212
  33. package/modules/runner.ps1 +242 -239
  34. package/modules/self_test_extra.ps1 +537 -360
  35. package/modules/startup_audit.ps1 +126 -126
  36. package/modules/system_admin.ps1 +220 -220
  37. package/modules/test_runners.ps1 +24 -24
  38. package/modules/walkthrough.ps1 +80 -79
  39. package/modules/windows_user.ps1 +118 -118
  40. package/package.json +2 -2
  41. package/windowsweep.ps1 +322 -316
@@ -1,220 +1,220 @@
1
- # system_admin.ps1 - sections 12-16: Windows Update cache, Disk Cleanup engine, DISM, hibernation file, event logs.
2
- # Every section here needs Administrator rights; the runner skips them (with the --elevate hint) when not elevated.
3
-
4
- function Get-Targets12 {
5
- $SR = $Script:P.SR; $PD = $Script:P.PD
6
- return @(
7
- (New-Target 12 'Windows Update download cache' "$SR\SoftwareDistribution\Download" -Mode clear -Note 'wuauserv and bits are stopped for the wipe and restarted')
8
- (New-Target 12 'Delivery Optimization cache' 'Delete-DeliveryOptimizationCache -Force' -Kind cmd)
9
- (New-Target 12 'Windows temp' "$SR\Temp" -Mode prune -Days $Script:WS.TempDays)
10
- (New-Target 12 'LocalService temp' "$SR\ServiceProfiles\LocalService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
11
- (New-Target 12 'NetworkService temp' "$SR\ServiceProfiles\NetworkService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
12
- (New-Target 12 'CBS servicing logs' "$SR\Logs\CBS" -Mode prune -Days 30 -Note 'the live CBS.log is open and skipped')
13
- (New-Target 12 'DISM logs' "$SR\Logs\DISM" -Mode prune -Days 30)
14
- (New-Target 12 'Windows Update logs' "$SR\Logs\WindowsUpdate" -Mode prune -Days 30)
15
- (New-Target 12 'Live kernel reports' "$SR\LiveKernelReports" -Mode clear-old -Days 7)
16
- (New-Target 12 'Windows Error Reporting queue (system)' "$PD\Microsoft\Windows\WER\ReportQueue" -Mode clear)
17
- (New-Target 12 'Windows Error Reporting archive (system)' "$PD\Microsoft\Windows\WER\ReportArchive" -Mode clear)
18
- (New-Target 12 'Windows Error Reporting temp (system)' "$PD\Microsoft\Windows\WER\Temp" -Mode clear)
19
- )
20
- }
21
-
22
- function Set-ServiceState {
23
- <# .SYNOPSIS Stop or start a service through the dry-run-aware wrapper. Returns $true when it was running before a stop. #>
24
- param([string] $Name, [string] $Action)
25
- $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
26
- if (-not $svc) { return $false }
27
- if ($Action -eq 'stop') {
28
- if ($svc.Status -ne 'Running') { return $false }
29
- if ($Script:WS.DryRun) { Write-DryRun "would stop service $Name"; return $true }
30
- try { Stop-Service -Name $Name -Force -ErrorAction Stop; Write-Note "stopped $Name" } catch { Write-Warn "could not stop $Name : $($_.Exception.Message)" }
31
- return $true
32
- }
33
- if ($Script:WS.DryRun) { Write-DryRun "would start service $Name"; return $true }
34
- try { Start-Service -Name $Name -ErrorAction Stop; Write-Note "started $Name" } catch { Write-Warn "could not start $Name : $($_.Exception.Message)" }
35
- return $true
36
- }
37
-
38
- function Invoke-Section12 {
39
- $ws = $Script:WS
40
- Write-SectionIntro @(
41
- 'Windows Update keeps every downloaded package after installing it; Delivery Optimization keeps peer-share',
42
- 'copies; C:\Windows\Temp and the servicing logs grow forever. All of it is rebuilt on demand.'
43
- )
44
- if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
45
- $targets = Get-Targets12
46
- $null = Show-TargetSizes $targets
47
- if (-not (Confirm-Section 'Clear Windows Update and system temp caches now?')) { Write-Info 'skipped'; return }
48
- $wuWasRunning = Set-ServiceState -Name 'wuauserv' -Action 'stop'
49
- $bitsWasRunning = Set-ServiceState -Name 'bits' -Action 'stop'
50
- try {
51
- $null = Invoke-TargetList $targets
52
- } finally {
53
- if ($wuWasRunning) { $null = Set-ServiceState -Name 'wuauserv' -Action 'start' }
54
- if ($bitsWasRunning) { $null = Set-ServiceState -Name 'bits' -Action 'start' }
55
- }
56
- if (Get-Command Delete-DeliveryOptimizationCache -ErrorAction SilentlyContinue) {
57
- if ($ws.DryRun) { Write-DryRun 'would run Delete-DeliveryOptimizationCache -Force' }
58
- else {
59
- try { Delete-DeliveryOptimizationCache -Force -ErrorAction Stop; Write-Ok 'Delivery Optimization cache cleared' } catch { Write-Warn "Delivery Optimization: $($_.Exception.Message)" }
60
- }
61
- }
62
- }
63
-
64
- # ---------------------------------------------------------------------------------------------
65
- # Section 13 - Disk Cleanup engine (cleanmgr)
66
- # ---------------------------------------------------------------------------------------------
67
-
68
- # Handlers this tool enables. Deliberately absent: DownloadsFolder (your files), Recycle Bin (section 11 asks
69
- # separately), User file versions (File History), Windows ESD installation files (needed for Reset this PC),
70
- # Language Pack (removes installed languages).
71
- $Script:WS_CLEANMGR_HANDLERS = @(
72
- 'Active Setup Temp Folders', 'BranchCache', 'Content Indexer Cleaner', 'D3D Shader Cache', 'Delivery Optimization Files',
73
- 'Device Driver Packages', 'Diagnostic Data Viewer database files', 'Downloaded Program Files', 'Internet Cache Files',
74
- 'Offline Pages Files', 'Old ChkDsk Files', 'Previous Installations', 'RetailDemo Offline Content', 'Setup Log Files',
75
- 'System error memory dump files', 'System error minidump files', 'Temporary Files', 'Temporary Setup Files',
76
- 'Temporary Sync Files', 'Thumbnail Cache', 'Update Cleanup', 'Upgrade Discarded Files', 'Windows Defender',
77
- 'Windows Error Reporting Files', 'Windows Upgrade Log Files'
78
- )
79
- $Script:WS_CLEANMGR_KEY = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
80
- $Script:WS_CLEANMGR_FLAG = 'StateFlags0077'
81
-
82
- function Get-Targets13 {
83
- return @((New-Target 13 'cleanmgr /sagerun:77 with curated handlers' ($Script:WS_CLEANMGR_HANDLERS -join ', ') -Kind cmd -Note 'never Downloads, Recycle Bin, File History, ESD files or language packs'))
84
- }
85
-
86
- function Invoke-Section13 {
87
- $ws = $Script:WS
88
- Write-SectionIntro @(
89
- "Windows' own Disk Cleanup engine, driven with a curated handler list: temporary files, Update Cleanup,",
90
- 'thumbnail cache, memory dumps, setup and upgrade logs, previous Windows installations. It never touches',
91
- 'your Downloads folder, the Recycle Bin, File History or the files Reset-this-PC needs.'
92
- )
93
- if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
94
- if (-not (Test-CommandPresent 'cleanmgr.exe')) { Write-Warn 'cleanmgr.exe not found on this edition of Windows - skipped'; return }
95
- $present = @()
96
- foreach ($h in $Script:WS_CLEANMGR_HANDLERS) { if (Test-Path -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h)) { $present += $h } }
97
- Write-Info "handlers available on this machine: $($present.Count) of $($Script:WS_CLEANMGR_HANDLERS.Count)"
98
- foreach ($h in $present) { Write-Note $h }
99
- if ($ws.DryRun) { Write-DryRun 'would run cleanmgr.exe /sagerun:77 with those handlers (cleanmgr cannot preview sizes)'; return }
100
- if (-not (Confirm-Section 'Run Disk Cleanup with these handlers now? (a progress window may appear; it can take several minutes)')) { Write-Info 'skipped'; return }
101
- $before = Get-SystemDriveFree
102
- try {
103
- foreach ($h in $present) { Set-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -Value 2 -Type DWord -ErrorAction Stop }
104
- $r = Invoke-External -FilePath 'cleanmgr.exe' -ArgumentList @('/sagerun:77') -Destructive -Quiet -Label 'cleanmgr /sagerun:77'
105
- if ($r.ExitCode -ne 0) { Write-Warn "cleanmgr returned $($r.ExitCode)" }
106
- } catch {
107
- Write-Err "could not configure Disk Cleanup: $($_.Exception.Message)"
108
- } finally {
109
- foreach ($h in $present) { Remove-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -ErrorAction SilentlyContinue }
110
- }
111
- $gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
112
- Add-Freed $gain
113
- Write-Ok ("Disk Cleanup finished - system drive gained " + (Format-Bytes $gain))
114
- }
115
-
116
- # ---------------------------------------------------------------------------------------------
117
- # Section 14 - DISM component store
118
- # ---------------------------------------------------------------------------------------------
119
-
120
- function Get-Targets14 {
121
- $note = 'superseded component versions in WinSxS'
122
- if ($Script:WS.ResetBase) { $note += '; --reset-base also removes the ability to uninstall installed updates' }
123
- return @((New-Target 14 'Dism /Online /Cleanup-Image /StartComponentCleanup' "$($Script:P.SR)\WinSxS" -Kind cmd -Note $note))
124
- }
125
-
126
- function Invoke-Section14 {
127
- $ws = $Script:WS
128
- Write-SectionIntro @(
129
- 'The component store (WinSxS) keeps superseded versions of Windows components. DISM removes the ones no',
130
- 'installed update needs. It is slow (5-20 minutes) and safe; /ResetBase (opt-in) also drops the ability to',
131
- 'uninstall currently installed updates.'
132
- )
133
- if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
134
- if (-not (Test-CommandPresent 'Dism.exe')) { Write-Warn 'Dism.exe not found - skipped'; return }
135
- Write-Info 'analyzing the component store (read-only, about a minute)...'
136
- $a = Invoke-External -FilePath 'Dism.exe' -ArgumentList @('/Online', '/Cleanup-Image', '/AnalyzeComponentStore') -Quiet -Label 'dism analyze'
137
- foreach ($l in $a.Output) { if ($l -match 'Size|Cleanup|Reclaimable|Date' ) { Write-Note ($l.Trim()) } }
138
- $dismArgs = @('/Online', '/Cleanup-Image', '/StartComponentCleanup')
139
- if ($ws.ResetBase) { $dismArgs += '/ResetBase'; Write-Warn '/ResetBase requested: installed updates can no longer be uninstalled afterwards' }
140
- if (-not (Confirm-Section 'Run the component store cleanup now?')) { Write-Info 'skipped'; return }
141
- $before = Get-SystemDriveFree
142
- $r = Invoke-External -FilePath 'Dism.exe' -ArgumentList $dismArgs -Destructive -Quiet -Label 'dism StartComponentCleanup'
143
- if ($r.Ran) {
144
- $gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
145
- Add-Freed $gain
146
- if ($r.ExitCode -eq 0) { Write-Ok ("component store cleanup finished - system drive gained " + (Format-Bytes $gain)) } else { Write-Warn "DISM returned $($r.ExitCode) - see the log" }
147
- }
148
- }
149
-
150
- # ---------------------------------------------------------------------------------------------
151
- # Section 15 - hibernation file
152
- # ---------------------------------------------------------------------------------------------
153
-
154
- function Get-Targets15 {
155
- return @((New-Target 15 'hiberfil.sys (powercfg /hibernate off | /type reduced)' (Join-Path $Script:P.SD '\hiberfil.sys') -Kind cmd -Note 'off frees the whole file; reduced keeps Fast Startup at roughly 40% of RAM'))
156
- }
157
-
158
- function Invoke-Section15 {
159
- $ws = $Script:WS
160
- $h = Get-HiberfilInfo
161
- Write-SectionIntro @(
162
- 'hiberfil.sys holds a copy of RAM for Hibernate and Fast Startup, so it is about 40% of your RAM - permanently.',
163
- ' off removes it. Sleep still works; Hibernate, Fast Startup and hibernate-on-critical-battery do not.',
164
- ' reduced keeps Fast Startup only, at a smaller file (Windows decides the size, usually about half).',
165
- 'Running speed is unaffected either way; this is purely disk space.'
166
- )
167
- if ($h.Bytes -gt 0) { Write-Kv 'hiberfil.sys now:' ("{0} (hibernation {1})" -f (Format-Bytes $h.Bytes), $h.State) } else { Write-Info 'hiberfil.sys is absent - hibernation is already off'; return }
168
- if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
169
- $choice = $ws.Hiberfil
170
- if (-not $choice) {
171
- if (-not $ws.Interactive) { Write-Warn 'pass --hiberfil off|reduced|keep to run this section unattended'; return }
172
- $choice = Read-Choice -Prompt ' [o] turn hibernation off [r] reduced (keep Fast Startup) [k] keep as is > ' -Default 'k'
173
- switch ($choice) { 'o' { $choice = 'off' } 'r' { $choice = 'reduced' } default { $choice = 'keep' } }
174
- }
175
- if ($choice -eq 'keep') { Write-Info 'left unchanged'; return }
176
- $pcArgs = @('/hibernate', 'off')
177
- if ($choice -eq 'reduced') { $pcArgs = @('/hibernate', '/type', 'reduced') }
178
- if ($ws.DryRun) { Write-DryRun ("would run powercfg $($pcArgs -join ' ') (frees up to " + (Format-Bytes $h.Bytes) + ')'); Add-Freed $h.Bytes; return }
179
- if (-not (Confirm-Ui -Prompt "Run powercfg $($pcArgs -join ' ') now?" -Default 'n')) { Write-Info 'skipped'; return }
180
- $r = Invoke-External -FilePath 'powercfg.exe' -ArgumentList $pcArgs -Destructive -Quiet -Label "powercfg $($pcArgs -join ' ')"
181
- Start-Sleep -Seconds 2
182
- $after = Get-HiberfilInfo
183
- $gain = [math]::Max([long]0, $h.Bytes - $after.Bytes)
184
- Add-Freed $gain
185
- if ($r.ExitCode -eq 0) { Write-Ok ("hibernation set to '$choice' - hiberfil.sys " + (Format-Bytes $h.Bytes) + ' -> ' + (Format-Bytes $after.Bytes) + ' (freed ' + (Format-Bytes $gain) + ')') } else { Write-Warn "powercfg returned $($r.ExitCode)" }
186
- }
187
-
188
- # ---------------------------------------------------------------------------------------------
189
- # Section 16 - event logs
190
- # ---------------------------------------------------------------------------------------------
191
-
192
- function Get-Targets16 {
193
- return @((New-Target 16 'Windows Event Logs (wevtutil cl for every log)' "$($Script:P.SR)\System32\winevt\Logs" -Kind cmd -Note 'PERMANENT - troubleshooting history is lost'))
194
- }
195
-
196
- function Invoke-Section16 {
197
- $ws = $Script:WS
198
- $logDir = Join-Path $Script:P.SR 'System32\winevt\Logs'
199
- Write-SectionIntro @(
200
- 'Clears every Windows Event Log (Application, System, Security and the hundreds of per-component logs).',
201
- 'This is permanent and removes the history a technician would use to diagnose a problem. Only do this on',
202
- 'a machine that is behaving normally.'
203
- )
204
- if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
205
- $before = Get-DirectoryBytes $logDir
206
- Write-Kv 'event log store:' (Format-Bytes $before)
207
- if ($ws.DryRun) { Write-DryRun ("would clear every event log (about " + (Format-Bytes $before) + ')'); Add-Freed $before; return }
208
- if (-not (Confirm-Ui -Prompt 'Permanently clear ALL Windows Event Logs?' -Default 'n')) { Write-Info 'skipped'; return }
209
- $list = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('el') -Quiet -Label 'wevtutil el'
210
- $ok = 0; $failed = 0
211
- foreach ($name in $list.Output) {
212
- $n = ([string]$name).Trim()
213
- if (-not $n) { continue }
214
- $r = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('cl', $n) -Destructive -Quiet -Label "wevtutil cl $n"
215
- if ($r.ExitCode -eq 0) { $ok++ } else { $failed++ }
216
- }
217
- $gain = [math]::Max([long]0, $before - (Get-DirectoryBytes $logDir))
218
- Add-Freed $gain
219
- Write-Ok ("cleared $ok logs ($failed could not be cleared - analytic/debug channels), freed " + (Format-Bytes $gain))
220
- }
1
+ # system_admin.ps1 - sections 12-16: Windows Update cache, Disk Cleanup engine, DISM, hibernation file, event logs.
2
+ # Every section here needs Administrator rights; the runner skips them (with the --elevate hint) when not elevated.
3
+
4
+ function Get-Targets12 {
5
+ $SR = $Script:P.SR; $PD = $Script:P.PD
6
+ return @(
7
+ (New-Target 12 'Windows Update download cache' "$SR\SoftwareDistribution\Download" -Mode clear -Note 'wuauserv and bits are stopped for the wipe and restarted')
8
+ (New-Target 12 'Delivery Optimization cache' 'Delete-DeliveryOptimizationCache -Force' -Kind cmd)
9
+ (New-Target 12 'Windows temp' "$SR\Temp" -Mode prune -Days $Script:WS.TempDays)
10
+ (New-Target 12 'LocalService temp' "$SR\ServiceProfiles\LocalService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
11
+ (New-Target 12 'NetworkService temp' "$SR\ServiceProfiles\NetworkService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
12
+ (New-Target 12 'CBS servicing logs' "$SR\Logs\CBS" -Mode prune -Days 30 -Note 'the live CBS.log is open and skipped')
13
+ (New-Target 12 'DISM logs' "$SR\Logs\DISM" -Mode prune -Days 30)
14
+ (New-Target 12 'Windows Update logs' "$SR\Logs\WindowsUpdate" -Mode prune -Days 30)
15
+ (New-Target 12 'Live kernel reports' "$SR\LiveKernelReports" -Mode clear-old -Days 7)
16
+ (New-Target 12 'Windows Error Reporting queue (system)' "$PD\Microsoft\Windows\WER\ReportQueue" -Mode clear)
17
+ (New-Target 12 'Windows Error Reporting archive (system)' "$PD\Microsoft\Windows\WER\ReportArchive" -Mode clear)
18
+ (New-Target 12 'Windows Error Reporting temp (system)' "$PD\Microsoft\Windows\WER\Temp" -Mode clear)
19
+ )
20
+ }
21
+
22
+ function Set-ServiceState {
23
+ <# .SYNOPSIS Stop or start a service through the dry-run-aware wrapper. Returns $true when it was running before a stop. #>
24
+ param([string] $Name, [string] $Action)
25
+ $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
26
+ if (-not $svc) { return $false }
27
+ if ($Action -eq 'stop') {
28
+ if ($svc.Status -ne 'Running') { return $false }
29
+ if ($Script:WS.DryRun) { Write-DryRun "would stop service $Name"; return $true }
30
+ try { Stop-Service -Name $Name -Force -ErrorAction Stop; Write-Note "stopped $Name" } catch { Write-Warn "could not stop $Name : $($_.Exception.Message)" }
31
+ return $true
32
+ }
33
+ if ($Script:WS.DryRun) { Write-DryRun "would start service $Name"; return $true }
34
+ try { Start-Service -Name $Name -ErrorAction Stop; Write-Note "started $Name" } catch { Write-Warn "could not start $Name : $($_.Exception.Message)" }
35
+ return $true
36
+ }
37
+
38
+ function Invoke-Section12 {
39
+ $ws = $Script:WS
40
+ Write-SectionIntro @(
41
+ 'Windows Update keeps every downloaded package after installing it; Delivery Optimization keeps peer-share',
42
+ 'copies; C:\Windows\Temp and the servicing logs grow forever. All of it is rebuilt on demand.'
43
+ )
44
+ if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
45
+ $targets = Get-Targets12
46
+ $null = Show-TargetSizes $targets
47
+ if (-not (Confirm-Section 'Clear Windows Update and system temp caches now?')) { Write-Info 'skipped'; return }
48
+ $wuWasRunning = Set-ServiceState -Name 'wuauserv' -Action 'stop'
49
+ $bitsWasRunning = Set-ServiceState -Name 'bits' -Action 'stop'
50
+ try {
51
+ $null = Invoke-TargetList $targets
52
+ } finally {
53
+ if ($wuWasRunning) { $null = Set-ServiceState -Name 'wuauserv' -Action 'start' }
54
+ if ($bitsWasRunning) { $null = Set-ServiceState -Name 'bits' -Action 'start' }
55
+ }
56
+ if (Get-Command Delete-DeliveryOptimizationCache -ErrorAction SilentlyContinue) {
57
+ if ($ws.DryRun) { Write-DryRun 'would run Delete-DeliveryOptimizationCache -Force' }
58
+ else {
59
+ try { Delete-DeliveryOptimizationCache -Force -ErrorAction Stop; Write-Ok 'Delivery Optimization cache cleared' } catch { Write-Warn "Delivery Optimization: $($_.Exception.Message)" }
60
+ }
61
+ }
62
+ }
63
+
64
+ # ---------------------------------------------------------------------------------------------
65
+ # Section 13 - Disk Cleanup engine (cleanmgr)
66
+ # ---------------------------------------------------------------------------------------------
67
+
68
+ # Handlers this tool enables. Deliberately absent: DownloadsFolder (your files), Recycle Bin (section 11 asks
69
+ # separately), User file versions (File History), Windows ESD installation files (needed for Reset this PC),
70
+ # Language Pack (removes installed languages).
71
+ $Script:WS_CLEANMGR_HANDLERS = @(
72
+ 'Active Setup Temp Folders', 'BranchCache', 'Content Indexer Cleaner', 'D3D Shader Cache', 'Delivery Optimization Files',
73
+ 'Device Driver Packages', 'Diagnostic Data Viewer database files', 'Downloaded Program Files', 'Internet Cache Files',
74
+ 'Offline Pages Files', 'Old ChkDsk Files', 'Previous Installations', 'RetailDemo Offline Content', 'Setup Log Files',
75
+ 'System error memory dump files', 'System error minidump files', 'Temporary Files', 'Temporary Setup Files',
76
+ 'Temporary Sync Files', 'Thumbnail Cache', 'Update Cleanup', 'Upgrade Discarded Files', 'Windows Defender',
77
+ 'Windows Error Reporting Files', 'Windows Upgrade Log Files'
78
+ )
79
+ $Script:WS_CLEANMGR_KEY = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
80
+ $Script:WS_CLEANMGR_FLAG = 'StateFlags0077'
81
+
82
+ function Get-Targets13 {
83
+ return @((New-Target 13 'cleanmgr /sagerun:77 with curated handlers' ($Script:WS_CLEANMGR_HANDLERS -join ', ') -Kind cmd -Note 'never Downloads, Recycle Bin, File History, ESD files or language packs'))
84
+ }
85
+
86
+ function Invoke-Section13 {
87
+ $ws = $Script:WS
88
+ Write-SectionIntro @(
89
+ "Windows' own Disk Cleanup engine, driven with a curated handler list: temporary files, Update Cleanup,",
90
+ 'thumbnail cache, memory dumps, setup and upgrade logs, previous Windows installations. It never touches',
91
+ 'your Downloads folder, the Recycle Bin, File History or the files Reset-this-PC needs.'
92
+ )
93
+ if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
94
+ if (-not (Test-CommandPresent 'cleanmgr.exe')) { Write-Warn 'cleanmgr.exe not found on this edition of Windows - skipped'; return }
95
+ $present = @()
96
+ foreach ($h in $Script:WS_CLEANMGR_HANDLERS) { if (Test-Path -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h)) { $present += $h } }
97
+ Write-Info "handlers available on this machine: $($present.Count) of $($Script:WS_CLEANMGR_HANDLERS.Count)"
98
+ foreach ($h in $present) { Write-Note $h }
99
+ if ($ws.DryRun) { Write-DryRun 'would run cleanmgr.exe /sagerun:77 with those handlers (cleanmgr cannot preview sizes)'; return }
100
+ if (-not (Confirm-Section 'Run Disk Cleanup with these handlers now? (a progress window may appear; it can take several minutes)')) { Write-Info 'skipped'; return }
101
+ $before = Get-SystemDriveFree
102
+ try {
103
+ foreach ($h in $present) { Set-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -Value 2 -Type DWord -ErrorAction Stop }
104
+ $r = Invoke-External -FilePath 'cleanmgr.exe' -ArgumentList @('/sagerun:77') -Destructive -Quiet -Label 'cleanmgr /sagerun:77'
105
+ if ($r.ExitCode -ne 0) { Write-Warn "cleanmgr returned $($r.ExitCode)" }
106
+ } catch {
107
+ Write-Err "could not configure Disk Cleanup: $($_.Exception.Message)"
108
+ } finally {
109
+ foreach ($h in $present) { Remove-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -ErrorAction SilentlyContinue }
110
+ }
111
+ $gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
112
+ Add-Freed $gain
113
+ Write-Ok ("Disk Cleanup finished - system drive gained " + (Format-Bytes $gain))
114
+ }
115
+
116
+ # ---------------------------------------------------------------------------------------------
117
+ # Section 14 - DISM component store
118
+ # ---------------------------------------------------------------------------------------------
119
+
120
+ function Get-Targets14 {
121
+ $note = 'superseded component versions in WinSxS'
122
+ if ($Script:WS.ResetBase) { $note += '; --reset-base also removes the ability to uninstall installed updates' }
123
+ return @((New-Target 14 'Dism /Online /Cleanup-Image /StartComponentCleanup' "$($Script:P.SR)\WinSxS" -Kind cmd -Note $note))
124
+ }
125
+
126
+ function Invoke-Section14 {
127
+ $ws = $Script:WS
128
+ Write-SectionIntro @(
129
+ 'The component store (WinSxS) keeps superseded versions of Windows components. DISM removes the ones no',
130
+ 'installed update needs. It is slow (5-20 minutes) and safe; /ResetBase (opt-in) also drops the ability to',
131
+ 'uninstall currently installed updates.'
132
+ )
133
+ if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
134
+ if (-not (Test-CommandPresent 'Dism.exe')) { Write-Warn 'Dism.exe not found - skipped'; return }
135
+ Write-Info 'analyzing the component store (read-only, about a minute)...'
136
+ $a = Invoke-External -FilePath 'Dism.exe' -ArgumentList @('/Online', '/Cleanup-Image', '/AnalyzeComponentStore') -Quiet -Label 'dism analyze'
137
+ foreach ($l in $a.Output) { if ($l -match 'Size|Cleanup|Reclaimable|Date' ) { Write-Note ($l.Trim()) } }
138
+ $dismArgs = @('/Online', '/Cleanup-Image', '/StartComponentCleanup')
139
+ if ($ws.ResetBase) { $dismArgs += '/ResetBase'; Write-Warn '/ResetBase requested: installed updates can no longer be uninstalled afterwards' }
140
+ if (-not (Confirm-Section 'Run the component store cleanup now?')) { Write-Info 'skipped'; return }
141
+ $before = Get-SystemDriveFree
142
+ $r = Invoke-External -FilePath 'Dism.exe' -ArgumentList $dismArgs -Destructive -Quiet -Label 'dism StartComponentCleanup'
143
+ if ($r.Ran) {
144
+ $gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
145
+ Add-Freed $gain
146
+ if ($r.ExitCode -eq 0) { Write-Ok ("component store cleanup finished - system drive gained " + (Format-Bytes $gain)) } else { Write-Warn "DISM returned $($r.ExitCode) - see the log" }
147
+ }
148
+ }
149
+
150
+ # ---------------------------------------------------------------------------------------------
151
+ # Section 15 - hibernation file
152
+ # ---------------------------------------------------------------------------------------------
153
+
154
+ function Get-Targets15 {
155
+ return @((New-Target 15 'hiberfil.sys (powercfg /hibernate off | /type reduced)' (Join-Path $Script:P.SD '\hiberfil.sys') -Kind cmd -Note 'off frees the whole file, about 40% of RAM; reduced keeps Fast Startup at roughly half that'))
156
+ }
157
+
158
+ function Invoke-Section15 {
159
+ $ws = $Script:WS
160
+ $h = Get-HiberfilInfo
161
+ Write-SectionIntro @(
162
+ 'hiberfil.sys holds a copy of RAM for Hibernate and Fast Startup, so it is about 40% of your RAM - permanently.',
163
+ ' off removes it. Sleep still works; Hibernate, Fast Startup and hibernate-on-critical-battery do not.',
164
+ ' reduced keeps Fast Startup only, at a smaller file (Windows decides the size, usually about half).',
165
+ 'Running speed is unaffected either way; this is purely disk space.'
166
+ )
167
+ if ($h.Bytes -gt 0) { Write-Kv 'hiberfil.sys now:' ("{0} (hibernation {1})" -f (Format-Bytes $h.Bytes), $h.State) } else { Write-Info 'hiberfil.sys is absent - hibernation is already off'; return }
168
+ if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
169
+ $choice = $ws.Hiberfil
170
+ if (-not $choice) {
171
+ if (-not $ws.Interactive) { Write-Warn 'pass --hiberfil off|reduced|keep to run this section unattended'; return }
172
+ $choice = Read-Choice -Prompt ' [o] turn hibernation off [r] reduced (keep Fast Startup) [k] keep as is > ' -Default 'k'
173
+ switch ($choice) { 'o' { $choice = 'off' } 'r' { $choice = 'reduced' } default { $choice = 'keep' } }
174
+ }
175
+ if ($choice -eq 'keep') { Write-Info 'left unchanged'; return }
176
+ $pcArgs = @('/hibernate', 'off')
177
+ if ($choice -eq 'reduced') { $pcArgs = @('/hibernate', '/type', 'reduced') }
178
+ if ($ws.DryRun) { Write-DryRun ("would run powercfg $($pcArgs -join ' ') (frees up to " + (Format-Bytes $h.Bytes) + ')'); Add-Freed $h.Bytes; return }
179
+ if (-not (Confirm-Ui -Prompt "Run powercfg $($pcArgs -join ' ') now?" -Default 'n')) { Write-Info 'skipped'; return }
180
+ $r = Invoke-External -FilePath 'powercfg.exe' -ArgumentList $pcArgs -Destructive -Quiet -Label "powercfg $($pcArgs -join ' ')"
181
+ Start-Sleep -Seconds 2
182
+ $after = Get-HiberfilInfo
183
+ $gain = [math]::Max([long]0, $h.Bytes - $after.Bytes)
184
+ Add-Freed $gain
185
+ if ($r.ExitCode -eq 0) { Write-Ok ("hibernation set to '$choice' - hiberfil.sys " + (Format-Bytes $h.Bytes) + ' -> ' + (Format-Bytes $after.Bytes) + ' (freed ' + (Format-Bytes $gain) + ')') } else { Write-Warn "powercfg returned $($r.ExitCode)" }
186
+ }
187
+
188
+ # ---------------------------------------------------------------------------------------------
189
+ # Section 16 - event logs
190
+ # ---------------------------------------------------------------------------------------------
191
+
192
+ function Get-Targets16 {
193
+ return @((New-Target 16 'Windows Event Logs (wevtutil cl for every log)' "$($Script:P.SR)\System32\winevt\Logs" -Kind cmd -Note 'PERMANENT - troubleshooting history is lost'))
194
+ }
195
+
196
+ function Invoke-Section16 {
197
+ $ws = $Script:WS
198
+ $logDir = Join-Path $Script:P.SR 'System32\winevt\Logs'
199
+ Write-SectionIntro @(
200
+ 'Clears every Windows Event Log (Application, System, Security and the hundreds of per-component logs).',
201
+ 'This is permanent and removes the history a technician would use to diagnose a problem. Only do this on',
202
+ 'a machine that is behaving normally.'
203
+ )
204
+ if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
205
+ $before = Get-DirectoryBytes $logDir
206
+ Write-Kv 'event log store:' (Format-Bytes $before)
207
+ if ($ws.DryRun) { Write-DryRun ("would clear every event log (about " + (Format-Bytes $before) + ')'); Add-Freed $before; return }
208
+ if (-not (Confirm-Ui -Prompt 'Permanently clear ALL Windows Event Logs?' -Default 'n')) { Write-Info 'skipped'; return }
209
+ $list = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('el') -Quiet -Label 'wevtutil el'
210
+ $ok = 0; $failed = 0
211
+ foreach ($name in $list.Output) {
212
+ $n = ([string]$name).Trim()
213
+ if (-not $n) { continue }
214
+ $r = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('cl', $n) -Destructive -Quiet -Label "wevtutil cl $n"
215
+ if ($r.ExitCode -eq 0) { $ok++ } else { $failed++ }
216
+ }
217
+ $gain = [math]::Max([long]0, $before - (Get-DirectoryBytes $logDir))
218
+ Add-Freed $gain
219
+ Write-Ok ("cleared $ok logs ($failed could not be cleared - analytic/debug channels), freed " + (Format-Bytes $gain))
220
+ }
@@ -1,24 +1,24 @@
1
- # test_runners.ps1 - section 3: downloaded test browsers (Cypress, Playwright, Puppeteer). Keep the newest of each.
2
-
3
- $Script:WS_GROUP_BY_FAMILY = { param($name) ($name -replace '[-_]?v?\d[\d.]*$', '') }
4
-
5
- function Get-Targets03 {
6
- $P = $Script:P
7
- return @(
8
- (New-Target 3 'Cypress binary versions' "$($P.L)\Cypress\Cache" -Mode units -Dev $true -KeepNewest $true -Note 'one folder per Cypress version; newest kept')
9
- (New-Target 3 'Playwright browsers' "$($P.L)\ms-playwright" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY -ExcludeNames @('.links', 'daemon') -Note 'newest build of each browser kept')
10
- (New-Target 3 'Playwright-Go browsers' "$($P.L)\ms-playwright-go" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY)
11
- (New-Target 3 'Puppeteer browsers' "$($P.U)\.cache\puppeteer\*" -Kind glob -Mode units -Dev $true -KeepNewest $true -Note 'per browser family: newest build kept')
12
- )
13
- }
14
-
15
- function Invoke-Section03 {
16
- Write-SectionIntro @(
17
- 'Test runners download whole browsers per version. The newest build of each browser is always kept; older',
18
- 'builds go once idle for the window. A missing build is re-downloaded by the next install or "playwright install".'
19
- ) -Dev $true
20
- $targets = Get-Targets03
21
- $null = Show-TargetSizes $targets
22
- if (-not (Confirm-Section 'Prune superseded test-runner browsers now?')) { Write-Info 'skipped'; return }
23
- $null = Invoke-TargetList $targets
24
- }
1
+ # test_runners.ps1 - section 3: downloaded test browsers (Cypress, Playwright, Puppeteer). Keep the newest of each.
2
+
3
+ $Script:WS_GROUP_BY_FAMILY = { param($name) ($name -replace '[-_]?v?\d[\d.]*$', '') }
4
+
5
+ function Get-Targets03 {
6
+ $P = $Script:P
7
+ return @(
8
+ (New-Target 3 'Cypress binary versions' "$($P.L)\Cypress\Cache" -Mode units -Dev $true -KeepNewest $true -Note 'one folder per Cypress version; newest kept')
9
+ (New-Target 3 'Playwright browsers' "$($P.L)\ms-playwright" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY -ExcludeNames @('.links', 'daemon') -Note 'newest build of each browser kept')
10
+ (New-Target 3 'Playwright-Go browsers' "$($P.L)\ms-playwright-go" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY)
11
+ (New-Target 3 'Puppeteer browsers' "$($P.U)\.cache\puppeteer\*" -Kind glob -Mode units -Dev $true -KeepNewest $true -Note 'per browser family: newest build kept')
12
+ )
13
+ }
14
+
15
+ function Invoke-Section03 {
16
+ Write-SectionIntro @(
17
+ 'Test runners download whole browsers per version. The newest build of each browser is always kept; older',
18
+ 'builds go once idle for the window. A missing build is re-downloaded by the next install or "playwright install".'
19
+ ) -Dev $true
20
+ $targets = Get-Targets03
21
+ $null = Show-TargetSizes $targets
22
+ if (-not (Confirm-Section 'Prune superseded test-runner browsers now?')) { Write-Info 'skipped'; return }
23
+ $null = Invoke-TargetList $targets
24
+ }