windowsweep 1.1.0 → 1.3.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 (43) hide show
  1. package/AI-INTEGRATION-GUIDE.md +16 -12
  2. package/CHANGELOG.md +82 -0
  3. package/LICENSE +21 -21
  4. package/README.md +104 -69
  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/external.ps1 +87 -0
  10. package/lib/fs.ps1 +189 -180
  11. package/lib/log.ps1 +140 -140
  12. package/lib/safety.ps1 +467 -493
  13. package/lib/scan.ps1 +109 -100
  14. package/lib/ui.ps1 +275 -275
  15. package/modules/android_avd.ps1 +36 -36
  16. package/modules/app_caches.ps1 +58 -58
  17. package/modules/browsers.ps1 +33 -33
  18. package/modules/build_tools.ps1 +35 -35
  19. package/modules/crash_trap.ps1 +89 -89
  20. package/modules/disk_usage.ps1 +49 -49
  21. package/modules/docker.ps1 +93 -93
  22. package/modules/editors.ps1 +150 -150
  23. package/modules/globals_audit.ps1 +236 -236
  24. package/modules/health.ps1 +101 -101
  25. package/modules/installed_programs.ps1 +118 -118
  26. package/modules/menu.ps1 +63 -63
  27. package/modules/notify.ps1 +60 -60
  28. package/modules/orphaned_appdata.ps1 +189 -189
  29. package/modules/personal.ps1 +126 -126
  30. package/modules/pkg_managers.ps1 +73 -73
  31. package/modules/projects.ps1 +160 -160
  32. package/modules/release_helpers.ps1 +438 -438
  33. package/modules/reports.ps1 +212 -212
  34. package/modules/runner.ps1 +246 -239
  35. package/modules/self_test_contract.ps1 +195 -0
  36. package/modules/self_test_extra.ps1 +366 -360
  37. package/modules/startup_audit.ps1 +126 -126
  38. package/modules/system_admin.ps1 +220 -220
  39. package/modules/test_runners.ps1 +24 -24
  40. package/modules/walkthrough.ps1 +80 -79
  41. package/modules/windows_user.ps1 +118 -118
  42. package/package.json +3 -3
  43. package/windowsweep.ps1 +322 -316
package/lib/log.ps1 CHANGED
@@ -1,140 +1,140 @@
1
- # log.ps1 - session log, freed-bytes tally, drive snapshots and the JSON session report.
2
-
3
- function Initialize-Log {
4
- <# .SYNOPSIS Create the data directories and open this run's log with a credit header. #>
5
- $ws = $Script:WS
6
- foreach ($d in @($ws.LogsDir, $ws.ReportsDir)) {
7
- if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
8
- }
9
- $ws.LogFile = Join-Path $ws.LogsDir ("$Script:WS_NAME-" + $ws.Stamp + '.log')
10
- $elev = 'no'
11
- if ($ws.IsAdmin) { $elev = 'yes' }
12
- $dry = 'no'
13
- if ($ws.DryRun) { $dry = 'yes' }
14
- $header = @(
15
- '# ============================================================'
16
- "# $Script:WS_NAME v$(Get-ToolVersion) - session log"
17
- "# Author: $Script:WS_AUTHOR <$Script:WS_EMAIL>"
18
- "# Web: $Script:WS_WEB"
19
- "# License: $Script:WS_LICENSE"
20
- "# Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')"
21
- "# Mode: $($ws.Mode)"
22
- "# Host: $env:COMPUTERNAME"
23
- "# User: $env:USERNAME"
24
- "# Elevated: $elev"
25
- "# Dry-run: $dry"
26
- "# Launcher: $($ws.Launcher)"
27
- '# ============================================================'
28
- )
29
- try { [IO.File]::WriteAllLines($ws.LogFile, $header) } catch { $ws.LogFile = $null }
30
- }
31
-
32
- function Write-LogLine {
33
- <# .SYNOPSIS Append one timestamped line to the run log; never throws. #>
34
- param([string] $Text)
35
- $f = $Script:WS.LogFile
36
- if (-not $f) { return }
37
- try { [IO.File]::AppendAllText($f, ("[{0}] {1}`r`n" -f (Get-Date -Format 'HH:mm:ss'), $Text)) } catch { $null = $_ }
38
- }
39
-
40
- function Add-Freed {
41
- <# .SYNOPSIS Tally bytes freed (or, in dry-run, bytes that would be freed). #>
42
- param([long] $Bytes)
43
- if ($Bytes -le 0) { return }
44
- if ($Script:WS.DryRun) { $Script:WS.TotalEstimated += $Bytes } else { $Script:WS.TotalFreed += $Bytes }
45
- $Script:WS.SectionFreed += $Bytes
46
- }
47
-
48
- function Get-DriveSnapshot {
49
- <# .SYNOPSIS Size/free bytes of every ready fixed drive. #>
50
- $rows = @()
51
- foreach ($d in [IO.DriveInfo]::GetDrives()) {
52
- try {
53
- if (-not $d.IsReady) { continue }
54
- if ($d.DriveType -ne [IO.DriveType]::Fixed) { continue }
55
- $rows += [ordered]@{ drive = $d.Name.TrimEnd('\'); size_bytes = [long]$d.TotalSize; free_bytes = [long]$d.AvailableFreeSpace }
56
- } catch { $null = $_ }
57
- }
58
- return $rows
59
- }
60
-
61
- function Get-SystemDriveFree {
62
- <# .SYNOPSIS Free bytes on the system drive (used for before/after deltas). #>
63
- try { return [long](New-Object IO.DriveInfo($env:SystemDrive)).AvailableFreeSpace } catch { return [long]0 }
64
- }
65
-
66
- function Show-DriveTable {
67
- <# .SYNOPSIS Print size / used / free / percent for every fixed drive. #>
68
- Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,6}" -f 'DRIVE', 'SIZE', 'USED', 'FREE', 'USE%') 'White'
69
- foreach ($d in (Get-DriveSnapshot)) {
70
- $used = $d.size_bytes - $d.free_bytes
71
- $pct = 0
72
- if ($d.size_bytes -gt 0) { $pct = [math]::Round(100.0 * $used / $d.size_bytes) }
73
- $color = 'Gray'
74
- if ($pct -ge 90) { $color = 'Red' } elseif ($pct -ge 80) { $color = 'Yellow' }
75
- Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,5}%" -f $d.drive, (Format-Bytes $d.size_bytes), (Format-Bytes $used), (Format-Bytes $d.free_bytes), $pct) $color
76
- }
77
- }
78
-
79
- function Initialize-Report {
80
- $ws = $Script:WS
81
- $ws.Report = @{
82
- startedAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
83
- startedTicks = (Get-Date).Ticks
84
- drivesBefore = @(Get-DriveSnapshot)
85
- steps = New-Object System.Collections.ArrayList
86
- }
87
- }
88
-
89
- function Add-ReportStep {
90
- <# .SYNOPSIS Record a section outcome for the JSON report. Status: ran | skipped | refused | failed | dry-run #>
91
- param([int] $Section, [string] $Title, [string] $Status, [long] $Freed = 0, [string] $Note = '')
92
- $ws = $Script:WS
93
- if (-not $ws.Report) { return }
94
- $null = $ws.Report.steps.Add([ordered]@{ n = ($ws.Report.steps.Count + 1); section = $Section; title = $Title; status = $Status; freed_bytes = $Freed; note = $Note })
95
- }
96
-
97
- function Save-Report {
98
- <# .SYNOPSIS Write the canonical JSON report (schema 1). Returns the path or $null. #>
99
- $ws = $Script:WS
100
- if ($ws.NoReport -or -not $ws.Report) { return $null }
101
- $ran = 0; $skipped = 0
102
- foreach ($s in $ws.Report.steps) { if ($s.status -eq 'ran' -or $s.status -eq 'dry-run') { $ran++ } else { $skipped++ } }
103
- $dev = $null
104
- if ($null -ne $ws.Developer) { $dev = [bool]$ws.Developer }
105
- $doc = [ordered]@{
106
- schema_version = 1
107
- credits = [ordered]@{
108
- tool = $Script:WS_NAME; tool_version = (Get-ToolVersion); tool_homepage = $Script:WS_REPO; tool_license = $Script:WS_LICENSE
109
- author = [ordered]@{ name = $Script:WS_AUTHOR; email = $Script:WS_EMAIL; website = $Script:WS_WEB; linkedin = $Script:WS_LINKEDIN }
110
- }
111
- meta = [ordered]@{
112
- started_at = $ws.Report.startedAt
113
- finished_at = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
114
- duration_seconds = [int][math]::Round(((Get-Date).Ticks - $ws.Report.startedTicks) / 10000000)
115
- host = $env:COMPUTERNAME; user = $env:USERNAME
116
- os = $ws.OsCaption; powershell = $PSVersionTable.PSVersion.ToString()
117
- mode = $ws.Mode; dry_run = [bool]$ws.DryRun; elevated = [bool]$ws.IsAdmin; developer_mode = $dev
118
- idle_days = $ws.Days; temp_days = $ws.TempDays
119
- log_file = $ws.LogFile; launcher = $ws.Launcher; via_npx = [bool]$ws.Npx
120
- }
121
- disk = [ordered]@{ before = @($ws.Report.drivesBefore); after = @(Get-DriveSnapshot) }
122
- steps = @($ws.Report.steps)
123
- totals = [ordered]@{
124
- total_reclaimed_bytes = [long]$ws.TotalFreed
125
- total_reclaimed_human = (Format-Bytes $ws.TotalFreed)
126
- total_estimated_bytes = [long]$ws.TotalEstimated
127
- total_estimated_human = (Format-Bytes $ws.TotalEstimated)
128
- steps_run = $ran; steps_skipped = $skipped
129
- }
130
- }
131
- $path = Join-Path $ws.ReportsDir ('report-' + $ws.Stamp + '.json')
132
- try {
133
- [IO.File]::WriteAllText($path, ($doc | ConvertTo-Json -Depth 8), (New-Object System.Text.UTF8Encoding($false)))
134
- $ws.ReportFile = $path
135
- return $path
136
- } catch {
137
- Write-Warn "could not write report: $($_.Exception.Message)"
138
- return $null
139
- }
140
- }
1
+ # log.ps1 - session log, freed-bytes tally, drive snapshots and the JSON session report.
2
+
3
+ function Initialize-Log {
4
+ <# .SYNOPSIS Create the data directories and open this run's log with a credit header. #>
5
+ $ws = $Script:WS
6
+ foreach ($d in @($ws.LogsDir, $ws.ReportsDir)) {
7
+ if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
8
+ }
9
+ $ws.LogFile = Join-Path $ws.LogsDir ("$Script:WS_NAME-" + $ws.Stamp + '.log')
10
+ $elev = 'no'
11
+ if ($ws.IsAdmin) { $elev = 'yes' }
12
+ $dry = 'no'
13
+ if ($ws.DryRun) { $dry = 'yes' }
14
+ $header = @(
15
+ '# ============================================================'
16
+ "# $Script:WS_NAME v$(Get-ToolVersion) - session log"
17
+ "# Author: $Script:WS_AUTHOR <$Script:WS_EMAIL>"
18
+ "# Web: $Script:WS_WEB"
19
+ "# License: $Script:WS_LICENSE"
20
+ "# Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')"
21
+ "# Mode: $($ws.Mode)"
22
+ "# Host: $env:COMPUTERNAME"
23
+ "# User: $env:USERNAME"
24
+ "# Elevated: $elev"
25
+ "# Dry-run: $dry"
26
+ "# Launcher: $($ws.Launcher)"
27
+ '# ============================================================'
28
+ )
29
+ try { [IO.File]::WriteAllLines($ws.LogFile, $header) } catch { $ws.LogFile = $null }
30
+ }
31
+
32
+ function Write-LogLine {
33
+ <# .SYNOPSIS Append one timestamped line to the run log; never throws. #>
34
+ param([string] $Text)
35
+ $f = $Script:WS.LogFile
36
+ if (-not $f) { return }
37
+ try { [IO.File]::AppendAllText($f, ("[{0}] {1}`r`n" -f (Get-Date -Format 'HH:mm:ss'), $Text)) } catch { $null = $_ }
38
+ }
39
+
40
+ function Add-Freed {
41
+ <# .SYNOPSIS Tally bytes freed (or, in dry-run, bytes that would be freed). #>
42
+ param([long] $Bytes)
43
+ if ($Bytes -le 0) { return }
44
+ if ($Script:WS.DryRun) { $Script:WS.TotalEstimated += $Bytes } else { $Script:WS.TotalFreed += $Bytes }
45
+ $Script:WS.SectionFreed += $Bytes
46
+ }
47
+
48
+ function Get-DriveSnapshot {
49
+ <# .SYNOPSIS Size/free bytes of every ready fixed drive. #>
50
+ $rows = @()
51
+ foreach ($d in [IO.DriveInfo]::GetDrives()) {
52
+ try {
53
+ if (-not $d.IsReady) { continue }
54
+ if ($d.DriveType -ne [IO.DriveType]::Fixed) { continue }
55
+ $rows += [ordered]@{ drive = $d.Name.TrimEnd('\'); size_bytes = [long]$d.TotalSize; free_bytes = [long]$d.AvailableFreeSpace }
56
+ } catch { $null = $_ }
57
+ }
58
+ return $rows
59
+ }
60
+
61
+ function Get-SystemDriveFree {
62
+ <# .SYNOPSIS Free bytes on the system drive (used for before/after deltas). #>
63
+ try { return [long](New-Object IO.DriveInfo($env:SystemDrive)).AvailableFreeSpace } catch { return [long]0 }
64
+ }
65
+
66
+ function Show-DriveTable {
67
+ <# .SYNOPSIS Print size / used / free / percent for every fixed drive. #>
68
+ Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,6}" -f 'DRIVE', 'SIZE', 'USED', 'FREE', 'USE%') 'White'
69
+ foreach ($d in (Get-DriveSnapshot)) {
70
+ $used = $d.size_bytes - $d.free_bytes
71
+ $pct = 0
72
+ if ($d.size_bytes -gt 0) { $pct = [math]::Round(100.0 * $used / $d.size_bytes) }
73
+ $color = 'Gray'
74
+ if ($pct -ge 90) { $color = 'Red' } elseif ($pct -ge 80) { $color = 'Yellow' }
75
+ Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,5}%" -f $d.drive, (Format-Bytes $d.size_bytes), (Format-Bytes $used), (Format-Bytes $d.free_bytes), $pct) $color
76
+ }
77
+ }
78
+
79
+ function Initialize-Report {
80
+ $ws = $Script:WS
81
+ $ws.Report = @{
82
+ startedAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
83
+ startedTicks = (Get-Date).Ticks
84
+ drivesBefore = @(Get-DriveSnapshot)
85
+ steps = New-Object System.Collections.ArrayList
86
+ }
87
+ }
88
+
89
+ function Add-ReportStep {
90
+ <# .SYNOPSIS Record a section outcome for the JSON report. Status: ran | skipped | refused | failed | dry-run #>
91
+ param([int] $Section, [string] $Title, [string] $Status, [long] $Freed = 0, [string] $Note = '')
92
+ $ws = $Script:WS
93
+ if (-not $ws.Report) { return }
94
+ $null = $ws.Report.steps.Add([ordered]@{ n = ($ws.Report.steps.Count + 1); section = $Section; title = $Title; status = $Status; freed_bytes = $Freed; note = $Note })
95
+ }
96
+
97
+ function Save-Report {
98
+ <# .SYNOPSIS Write the canonical JSON report (schema 1). Returns the path or $null. #>
99
+ $ws = $Script:WS
100
+ if ($ws.NoReport -or -not $ws.Report) { return $null }
101
+ $ran = 0; $skipped = 0
102
+ foreach ($s in $ws.Report.steps) { if ($s.status -eq 'ran' -or $s.status -eq 'dry-run') { $ran++ } else { $skipped++ } }
103
+ $dev = $null
104
+ if ($null -ne $ws.Developer) { $dev = [bool]$ws.Developer }
105
+ $doc = [ordered]@{
106
+ schema_version = 1
107
+ credits = [ordered]@{
108
+ tool = $Script:WS_NAME; tool_version = (Get-ToolVersion); tool_homepage = $Script:WS_REPO; tool_license = $Script:WS_LICENSE
109
+ author = [ordered]@{ name = $Script:WS_AUTHOR; email = $Script:WS_EMAIL; website = $Script:WS_WEB; linkedin = $Script:WS_LINKEDIN }
110
+ }
111
+ meta = [ordered]@{
112
+ started_at = $ws.Report.startedAt
113
+ finished_at = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
114
+ duration_seconds = [int][math]::Round(((Get-Date).Ticks - $ws.Report.startedTicks) / 10000000)
115
+ host = $env:COMPUTERNAME; user = $env:USERNAME
116
+ os = $ws.OsCaption; powershell = $PSVersionTable.PSVersion.ToString()
117
+ mode = $ws.Mode; dry_run = [bool]$ws.DryRun; elevated = [bool]$ws.IsAdmin; developer_mode = $dev
118
+ idle_days = $ws.Days; temp_days = $ws.TempDays
119
+ log_file = $ws.LogFile; launcher = $ws.Launcher; via_npx = [bool]$ws.Npx
120
+ }
121
+ disk = [ordered]@{ before = @($ws.Report.drivesBefore); after = @(Get-DriveSnapshot) }
122
+ steps = @($ws.Report.steps)
123
+ totals = [ordered]@{
124
+ total_reclaimed_bytes = [long]$ws.TotalFreed
125
+ total_reclaimed_human = (Format-Bytes $ws.TotalFreed)
126
+ total_estimated_bytes = [long]$ws.TotalEstimated
127
+ total_estimated_human = (Format-Bytes $ws.TotalEstimated)
128
+ steps_run = $ran; steps_skipped = $skipped
129
+ }
130
+ }
131
+ $path = Join-Path $ws.ReportsDir ('report-' + $ws.Stamp + '.json')
132
+ try {
133
+ [IO.File]::WriteAllText($path, ($doc | ConvertTo-Json -Depth 8), (New-Object System.Text.UTF8Encoding($false)))
134
+ $ws.ReportFile = $path
135
+ return $path
136
+ } catch {
137
+ Write-Warn "could not write report: $($_.Exception.Message)"
138
+ return $null
139
+ }
140
+ }