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
@@ -1,126 +1,126 @@
1
- # startup_audit.ps1 - section 25: everything that starts with the user or the machine. Report only.
2
- #
3
- # The tool shows; the user decides in Task Manager. That non-goal is deliberate: disabling a startup item is
4
- # a system configuration change, not cleanup, and a wrong one is invisible until the next boot.
5
-
6
- function Get-StartupApprovedState {
7
- <# .SYNOPSIS enabled | disabled | unknown from an Explorer StartupApproved value.
8
- The value is 12 bytes: a DWORD of flags then a FILETIME. Explorer stamps that FILETIME when the item is
9
- DISABLED and zeroes it when the item is enabled, so a non-zero tail is the reliable signal.
10
- Measured on Windows 10 19045 (2026-09-04): five items carried a real FILETIME under a leading 0x01, and
11
- two enabled ones (0x01 and 0x04) carried an all-zero tail - the widely quoted "byte 0 is 02/03" table
12
- does not hold there, so the tail is read first and 03 / 07 are kept only as a fallback marker.
13
- A value shorter than 12 bytes is reported as unknown; it is never guessed. #>
14
- param([byte[]] $Value)
15
- if ($null -eq $Value -or $Value.Length -lt 12) { return 'unknown' }
16
- for ($i = 4; $i -lt 12; $i++) { if ($Value[$i] -ne 0) { return 'disabled' } }
17
- if ($Value[0] -eq 3 -or $Value[0] -eq 7) { return 'disabled' }
18
- return 'enabled'
19
- }
20
-
21
- function Get-StartupApprovedMap {
22
- <# .SYNOPSIS name -> enabled|disabled|unknown, from the StartupApproved keys Explorer maintains. #>
23
- $map = @{}
24
- $keys = @(
25
- 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run',
26
- 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32',
27
- 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder',
28
- 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run',
29
- 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder'
30
- )
31
- foreach ($k in $keys) {
32
- $props = $null
33
- try { $props = Get-ItemProperty -LiteralPath $k -ErrorAction Stop } catch { continue }
34
- foreach ($prop in $props.PSObject.Properties) {
35
- if ($prop.Name.StartsWith('PS')) { continue }
36
- if (-not ($prop.Value -is [byte[]])) { continue }
37
- $map[$prop.Name] = Get-StartupApprovedState -Value $prop.Value
38
- }
39
- }
40
- return $map
41
- }
42
-
43
- function Get-StartupItems {
44
- <# .SYNOPSIS Every startup entry the user can see, from all five places Windows keeps them. #>
45
- $P = $Script:P
46
- $approved = Get-StartupApprovedMap
47
- $items = @()
48
- $runKeys = @(
49
- @('HKCU Run', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'),
50
- @('HKCU RunOnce', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'),
51
- @('HKLM Run', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'),
52
- @('HKLM RunOnce', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'),
53
- @('HKLM Run (32)', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run'),
54
- @('HKLM RunOnce (32)', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce')
55
- )
56
- foreach ($rk in $runKeys) {
57
- # NOT $p: PowerShell variable names are case-insensitive, so $p would silently null the $P roots table.
58
- $props = $null
59
- try { $props = Get-ItemProperty -LiteralPath $rk[1] -ErrorAction Stop } catch { continue }
60
- foreach ($prop in $props.PSObject.Properties) {
61
- if ($prop.Name.StartsWith('PS')) { continue }
62
- $state = 'enabled'
63
- if ($approved.ContainsKey($prop.Name)) { $state = $approved[$prop.Name] }
64
- $items += [pscustomobject]@{ Source = $rk[0]; Name = $prop.Name; State = $state; Command = [string]$prop.Value }
65
- }
66
- }
67
- foreach ($folder in @(@('Startup (user)', (Join-Path $P.A 'Microsoft\Windows\Start Menu\Programs\Startup')), @('Startup (all users)', (Join-Path $P.PD 'Microsoft\Windows\Start Menu\Programs\Startup')))) {
68
- if (-not (Test-DirPresent $folder[1])) { continue }
69
- foreach ($e in (Get-ChildEntries $folder[1])) {
70
- if ($e -is [IO.DirectoryInfo] -or $e.Name -eq 'desktop.ini') { continue }
71
- $state = 'enabled'
72
- if ($approved.ContainsKey($e.Name)) { $state = $approved[$e.Name] }
73
- $items += [pscustomobject]@{ Source = $folder[0]; Name = $e.Name; State = $state; Command = (Remove-LongPrefix $e.FullName) }
74
- }
75
- }
76
- try {
77
- foreach ($t in (Get-ScheduledTask -ErrorAction Stop)) {
78
- if (-not (@($t.Triggers) | Where-Object { $_.CimClass.CimClassName -eq 'MSFT_TaskLogonTrigger' })) { continue }
79
- $state = 'enabled'
80
- if ($t.State -eq 'Disabled') { $state = 'disabled' }
81
- $cmd = ''
82
- foreach ($a in @($t.Actions)) { if ($a.Execute) { $cmd = [string]$a.Execute } }
83
- $items += [pscustomobject]@{ Source = 'Scheduled task (logon)'; Name = $t.TaskName; State = $state; Command = $cmd }
84
- }
85
- } catch { $null = $_ }
86
- try {
87
- foreach ($c in (Get-CimInstance Win32_StartupCommand -ErrorAction Stop)) {
88
- if (@($items | Where-Object { $_.Name -eq $c.Name }).Count -gt 0) { continue }
89
- $state = 'enabled'
90
- if ($approved.ContainsKey([string]$c.Name)) { $state = $approved[[string]$c.Name] }
91
- $items += [pscustomobject]@{ Source = "WMI ($($c.Location))"; Name = [string]$c.Name; State = $state; Command = [string]$c.Command }
92
- }
93
- } catch { $null = $_ }
94
- return @($items | Sort-Object Source, Name)
95
- }
96
-
97
- function Get-Targets25 {
98
- <# .SYNOPSIS Informational only. Section 25 changes nothing: it never disables or removes a startup item. #>
99
- return @((New-Target 25 'Startup items (registry, folders, tasks)' 'Run keys + Startup folders + logon tasks' -Kind cmd -Note 'report only; change them yourself in Task Manager > Startup'))
100
- }
101
-
102
- function Invoke-Section25 {
103
- $ws = $Script:WS
104
- Write-SectionIntro @(
105
- 'Everything that launches when you sign in or the machine boots: Run and RunOnce keys, both Startup',
106
- 'folders, scheduled tasks with a logon trigger, and what Explorer records as enabled or disabled.',
107
- 'This section only shows them - change them in Task Manager > Startup, where you can undo it.'
108
- )
109
- $items = @(Get-StartupItems)
110
- if ($items.Count -eq 0) { Write-Info 'no startup items found'; return }
111
- Write-UiLine (" {0,-22} {1,-30} {2,-9} {3}" -f 'SOURCE', 'NAME', 'STATE', 'COMMAND') 'White'
112
- $lines = @("startup items - $(Get-Date -Format 'yyyy-MM-dd HH:mm')", '')
113
- foreach ($it in $items) {
114
- $color = 'Gray'
115
- if ($it.State -eq 'disabled') { $color = 'DarkGray' }
116
- $line = " {0,-22} {1,-30} {2,-9} {3}" -f $it.Source, $it.Name, $it.State, $it.Command
117
- Write-UiLine $line $color
118
- $lines += $line.Trim()
119
- }
120
- $on = @($items | Where-Object { $_.State -eq 'enabled' }).Count
121
- Write-Info "$($items.Count) startup item(s), $on enabled"
122
- if (-not $ws.NoReport) {
123
- $out = Join-Path $ws.ReportsDir ('startup-items-' + $ws.Stamp + '.txt')
124
- try { [IO.File]::WriteAllLines($out, $lines); Write-Note "list saved: $out" } catch { $null = $_ }
125
- }
126
- }
1
+ # startup_audit.ps1 - section 25: everything that starts with the user or the machine. Report only.
2
+ #
3
+ # The tool shows; the user decides in Task Manager. That non-goal is deliberate: disabling a startup item is
4
+ # a system configuration change, not cleanup, and a wrong one is invisible until the next boot.
5
+
6
+ function Get-StartupApprovedState {
7
+ <# .SYNOPSIS enabled | disabled | unknown from an Explorer StartupApproved value.
8
+ The value is 12 bytes: a DWORD of flags then a FILETIME. Explorer stamps that FILETIME when the item is
9
+ DISABLED and zeroes it when the item is enabled, so a non-zero tail is the reliable signal.
10
+ Measured on Windows 10 19045 (2026-09-04): five items carried a real FILETIME under a leading 0x01, and
11
+ two enabled ones (0x01 and 0x04) carried an all-zero tail - the widely quoted "byte 0 is 02/03" table
12
+ does not hold there, so the tail is read first and 03 / 07 are kept only as a fallback marker.
13
+ A value shorter than 12 bytes is reported as unknown; it is never guessed. #>
14
+ param([byte[]] $Value)
15
+ if ($null -eq $Value -or $Value.Length -lt 12) { return 'unknown' }
16
+ for ($i = 4; $i -lt 12; $i++) { if ($Value[$i] -ne 0) { return 'disabled' } }
17
+ if ($Value[0] -eq 3 -or $Value[0] -eq 7) { return 'disabled' }
18
+ return 'enabled'
19
+ }
20
+
21
+ function Get-StartupApprovedMap {
22
+ <# .SYNOPSIS name -> enabled|disabled|unknown, from the StartupApproved keys Explorer maintains. #>
23
+ $map = @{}
24
+ $keys = @(
25
+ 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run',
26
+ 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32',
27
+ 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder',
28
+ 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run',
29
+ 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder'
30
+ )
31
+ foreach ($k in $keys) {
32
+ $props = $null
33
+ try { $props = Get-ItemProperty -LiteralPath $k -ErrorAction Stop } catch { continue }
34
+ foreach ($prop in $props.PSObject.Properties) {
35
+ if ($prop.Name.StartsWith('PS')) { continue }
36
+ if (-not ($prop.Value -is [byte[]])) { continue }
37
+ $map[$prop.Name] = Get-StartupApprovedState -Value $prop.Value
38
+ }
39
+ }
40
+ return $map
41
+ }
42
+
43
+ function Get-StartupItems {
44
+ <# .SYNOPSIS Every startup entry the user can see, from all five places Windows keeps them. #>
45
+ $P = $Script:P
46
+ $approved = Get-StartupApprovedMap
47
+ $items = @()
48
+ $runKeys = @(
49
+ @('HKCU Run', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'),
50
+ @('HKCU RunOnce', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'),
51
+ @('HKLM Run', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'),
52
+ @('HKLM RunOnce', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'),
53
+ @('HKLM Run (32)', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run'),
54
+ @('HKLM RunOnce (32)', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce')
55
+ )
56
+ foreach ($rk in $runKeys) {
57
+ # NOT $p: PowerShell variable names are case-insensitive, so $p would silently null the $P roots table.
58
+ $props = $null
59
+ try { $props = Get-ItemProperty -LiteralPath $rk[1] -ErrorAction Stop } catch { continue }
60
+ foreach ($prop in $props.PSObject.Properties) {
61
+ if ($prop.Name.StartsWith('PS')) { continue }
62
+ $state = 'enabled'
63
+ if ($approved.ContainsKey($prop.Name)) { $state = $approved[$prop.Name] }
64
+ $items += [pscustomobject]@{ Source = $rk[0]; Name = $prop.Name; State = $state; Command = [string]$prop.Value }
65
+ }
66
+ }
67
+ foreach ($folder in @(@('Startup (user)', (Join-Path $P.A 'Microsoft\Windows\Start Menu\Programs\Startup')), @('Startup (all users)', (Join-Path $P.PD 'Microsoft\Windows\Start Menu\Programs\Startup')))) {
68
+ if (-not (Test-DirPresent $folder[1])) { continue }
69
+ foreach ($e in (Get-ChildEntries $folder[1])) {
70
+ if ($e -is [IO.DirectoryInfo] -or $e.Name -eq 'desktop.ini') { continue }
71
+ $state = 'enabled'
72
+ if ($approved.ContainsKey($e.Name)) { $state = $approved[$e.Name] }
73
+ $items += [pscustomobject]@{ Source = $folder[0]; Name = $e.Name; State = $state; Command = (Remove-LongPrefix $e.FullName) }
74
+ }
75
+ }
76
+ try {
77
+ foreach ($t in (Get-ScheduledTask -ErrorAction Stop)) {
78
+ if (-not (@($t.Triggers) | Where-Object { $_.CimClass.CimClassName -eq 'MSFT_TaskLogonTrigger' })) { continue }
79
+ $state = 'enabled'
80
+ if ($t.State -eq 'Disabled') { $state = 'disabled' }
81
+ $cmd = ''
82
+ foreach ($a in @($t.Actions)) { if ($a.Execute) { $cmd = [string]$a.Execute } }
83
+ $items += [pscustomobject]@{ Source = 'Scheduled task (logon)'; Name = $t.TaskName; State = $state; Command = $cmd }
84
+ }
85
+ } catch { $null = $_ }
86
+ try {
87
+ foreach ($c in (Get-CimInstance Win32_StartupCommand -ErrorAction Stop)) {
88
+ if (@($items | Where-Object { $_.Name -eq $c.Name }).Count -gt 0) { continue }
89
+ $state = 'enabled'
90
+ if ($approved.ContainsKey([string]$c.Name)) { $state = $approved[[string]$c.Name] }
91
+ $items += [pscustomobject]@{ Source = "WMI ($($c.Location))"; Name = [string]$c.Name; State = $state; Command = [string]$c.Command }
92
+ }
93
+ } catch { $null = $_ }
94
+ return @($items | Sort-Object Source, Name)
95
+ }
96
+
97
+ function Get-Targets25 {
98
+ <# .SYNOPSIS Informational only. Section 25 changes nothing: it never disables or removes a startup item. #>
99
+ return @((New-Target 25 'Startup items (registry, folders, tasks)' 'Run keys + Startup folders + logon tasks' -Kind cmd -Note 'report only; change them yourself in Task Manager > Startup'))
100
+ }
101
+
102
+ function Invoke-Section25 {
103
+ $ws = $Script:WS
104
+ Write-SectionIntro @(
105
+ 'Everything that launches when you sign in or the machine boots: Run and RunOnce keys, both Startup',
106
+ 'folders, scheduled tasks with a logon trigger, and what Explorer records as enabled or disabled.',
107
+ 'This section only shows them - change them in Task Manager > Startup, where you can undo it.'
108
+ )
109
+ $items = @(Get-StartupItems)
110
+ if ($items.Count -eq 0) { Write-Info 'no startup items found'; return }
111
+ Write-UiLine (" {0,-22} {1,-30} {2,-9} {3}" -f 'SOURCE', 'NAME', 'STATE', 'COMMAND') 'White'
112
+ $lines = @("startup items - $(Get-Date -Format 'yyyy-MM-dd HH:mm')", '')
113
+ foreach ($it in $items) {
114
+ $color = 'Gray'
115
+ if ($it.State -eq 'disabled') { $color = 'DarkGray' }
116
+ $line = " {0,-22} {1,-30} {2,-9} {3}" -f $it.Source, $it.Name, $it.State, $it.Command
117
+ Write-UiLine $line $color
118
+ $lines += $line.Trim()
119
+ }
120
+ $on = @($items | Where-Object { $_.State -eq 'enabled' }).Count
121
+ Write-Info "$($items.Count) startup item(s), $on enabled"
122
+ if (-not $ws.NoReport) {
123
+ $out = Join-Path $ws.ReportsDir ('startup-items-' + $ws.Stamp + '.txt')
124
+ try { [IO.File]::WriteAllLines($out, $lines); Write-Note "list saved: $out" } catch { $null = $_ }
125
+ }
126
+ }