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
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
+ }