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