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.
- package/AI-INTEGRATION-GUIDE.md +15 -11
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -21
- package/README.md +70 -42
- package/VERSION +1 -1
- package/lib/actions.ps1 +212 -212
- package/lib/config.ps1 +108 -99
- package/lib/constants.ps1 +103 -93
- package/lib/fs.ps1 +189 -180
- package/lib/log.ps1 +140 -140
- package/lib/safety.ps1 +541 -493
- package/lib/scan.ps1 +109 -100
- package/lib/ui.ps1 +275 -275
- package/modules/android_avd.ps1 +36 -36
- package/modules/app_caches.ps1 +58 -58
- package/modules/browsers.ps1 +33 -33
- package/modules/build_tools.ps1 +35 -35
- package/modules/crash_trap.ps1 +89 -89
- package/modules/disk_usage.ps1 +49 -49
- package/modules/docker.ps1 +93 -93
- package/modules/editors.ps1 +150 -150
- package/modules/globals_audit.ps1 +236 -236
- package/modules/health.ps1 +101 -101
- package/modules/installed_programs.ps1 +118 -118
- package/modules/menu.ps1 +63 -63
- package/modules/notify.ps1 +60 -60
- package/modules/orphaned_appdata.ps1 +189 -189
- package/modules/personal.ps1 +126 -126
- package/modules/pkg_managers.ps1 +73 -73
- package/modules/projects.ps1 +160 -160
- package/modules/release_helpers.ps1 +438 -438
- package/modules/reports.ps1 +212 -212
- package/modules/runner.ps1 +242 -239
- package/modules/self_test_extra.ps1 +537 -360
- package/modules/startup_audit.ps1 +126 -126
- package/modules/system_admin.ps1 +220 -220
- package/modules/test_runners.ps1 +24 -24
- package/modules/walkthrough.ps1 +80 -79
- package/modules/windows_user.ps1 +118 -118
- package/package.json +2 -2
- package/windowsweep.ps1 +322 -316
package/lib/config.ps1
CHANGED
|
@@ -1,99 +1,108 @@
|
|
|
1
|
-
# config.ps1 - persistent settings (~\.windowsweep\config.json) and the developer question.
|
|
2
|
-
|
|
3
|
-
function Get-DefaultConfig {
|
|
4
|
-
return [ordered]@{
|
|
5
|
-
schema = 1
|
|
6
|
-
developer = $null
|
|
7
|
-
developerAskedAt = $null
|
|
8
|
-
days = 100
|
|
9
|
-
tempDays = 3
|
|
10
|
-
largeFileMb = 100
|
|
11
|
-
scanRoots = @()
|
|
12
|
-
excludePaths = @()
|
|
13
|
-
welcomed = $false
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function Import-Config {
|
|
18
|
-
<# .SYNOPSIS Load config.json over the defaults; a broken file is reported and ignored. #>
|
|
19
|
-
$ws = $Script:WS
|
|
20
|
-
$cfg = Get-DefaultConfig
|
|
21
|
-
$path = $ws.ConfigPath
|
|
22
|
-
if (Test-Path -LiteralPath $path) {
|
|
23
|
-
try {
|
|
24
|
-
$raw = Get-Content -LiteralPath $path -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
|
|
25
|
-
foreach ($k in @($cfg.Keys)) {
|
|
26
|
-
$prop = $raw.PSObject.Properties[$k]
|
|
27
|
-
if ($null -ne $prop) { $cfg[$k] = $prop.Value }
|
|
28
|
-
}
|
|
29
|
-
if ($null -eq $cfg.scanRoots) { $cfg.scanRoots = @() }
|
|
30
|
-
if ($null -eq $cfg.excludePaths) { $cfg.excludePaths = @() }
|
|
31
|
-
} catch {
|
|
32
|
-
Write-Warn "config file unreadable, using defaults: $path ($($_.Exception.Message))"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
$ws.Config = $cfg
|
|
36
|
-
return $cfg
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function Save-Config {
|
|
40
|
-
$ws = $Script:WS
|
|
41
|
-
try {
|
|
42
|
-
$dir = Split-Path -Parent $ws.ConfigPath
|
|
43
|
-
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
|
|
44
|
-
[IO.File]::WriteAllText($ws.ConfigPath, ($ws.Config | ConvertTo-Json -Depth 4), (New-Object System.Text.UTF8Encoding($false)))
|
|
45
|
-
} catch {
|
|
46
|
-
Write-Warn "could not save config: $($_.Exception.Message)"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function Get-ToolchainHints {
|
|
51
|
-
<# .SYNOPSIS Names of developer tools found on this machine (used to suggest the developer answer). #>
|
|
52
|
-
$found = @()
|
|
53
|
-
foreach ($c in @('node', 'npm', 'yarn', 'pnpm', 'bun', 'deno', 'git', 'docker', 'python', 'pip', 'uv', 'java', 'gradle', 'dotnet', 'cargo', 'go', 'flutter', 'composer', 'php', 'code', 'cursor')) {
|
|
54
|
-
if (Get-Command $c -ErrorAction SilentlyContinue) { $found += $c }
|
|
55
|
-
}
|
|
56
|
-
$dirs = @{
|
|
57
|
-
'Gradle caches' = "$env:USERPROFILE\.gradle"; 'Android SDK' = "$env:LOCALAPPDATA\Android\Sdk"; 'VS Code' = "$env:APPDATA\Code"
|
|
58
|
-
'Docker Desktop' = "$env:LOCALAPPDATA\Docker"; 'JetBrains IDEs' = "$env:LOCALAPPDATA\JetBrains"; 'Visual Studio' = "$env:LOCALAPPDATA\Microsoft\VisualStudio"
|
|
59
|
-
}
|
|
60
|
-
foreach ($k in $dirs.Keys) { if (Test-Path -LiteralPath $dirs[$k]) { $found += $k } }
|
|
61
|
-
return $found
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function Resolve-DeveloperMode {
|
|
65
|
-
<# .SYNOPSIS Decide developer mode: flag > saved answer > interactive question > conservative default (yes).
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
$
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
$ws.
|
|
98
|
-
Write-
|
|
99
|
-
}
|
|
1
|
+
# config.ps1 - persistent settings (~\.windowsweep\config.json) and the developer question.
|
|
2
|
+
|
|
3
|
+
function Get-DefaultConfig {
|
|
4
|
+
return [ordered]@{
|
|
5
|
+
schema = 1
|
|
6
|
+
developer = $null
|
|
7
|
+
developerAskedAt = $null
|
|
8
|
+
days = 100
|
|
9
|
+
tempDays = 3
|
|
10
|
+
largeFileMb = 100
|
|
11
|
+
scanRoots = @()
|
|
12
|
+
excludePaths = @()
|
|
13
|
+
welcomed = $false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Import-Config {
|
|
18
|
+
<# .SYNOPSIS Load config.json over the defaults; a broken file is reported and ignored. #>
|
|
19
|
+
$ws = $Script:WS
|
|
20
|
+
$cfg = Get-DefaultConfig
|
|
21
|
+
$path = $ws.ConfigPath
|
|
22
|
+
if (Test-Path -LiteralPath $path) {
|
|
23
|
+
try {
|
|
24
|
+
$raw = Get-Content -LiteralPath $path -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
|
|
25
|
+
foreach ($k in @($cfg.Keys)) {
|
|
26
|
+
$prop = $raw.PSObject.Properties[$k]
|
|
27
|
+
if ($null -ne $prop) { $cfg[$k] = $prop.Value }
|
|
28
|
+
}
|
|
29
|
+
if ($null -eq $cfg.scanRoots) { $cfg.scanRoots = @() }
|
|
30
|
+
if ($null -eq $cfg.excludePaths) { $cfg.excludePaths = @() }
|
|
31
|
+
} catch {
|
|
32
|
+
Write-Warn "config file unreadable, using defaults: $path ($($_.Exception.Message))"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
$ws.Config = $cfg
|
|
36
|
+
return $cfg
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function Save-Config {
|
|
40
|
+
$ws = $Script:WS
|
|
41
|
+
try {
|
|
42
|
+
$dir = Split-Path -Parent $ws.ConfigPath
|
|
43
|
+
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
|
|
44
|
+
[IO.File]::WriteAllText($ws.ConfigPath, ($ws.Config | ConvertTo-Json -Depth 4), (New-Object System.Text.UTF8Encoding($false)))
|
|
45
|
+
} catch {
|
|
46
|
+
Write-Warn "could not save config: $($_.Exception.Message)"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function Get-ToolchainHints {
|
|
51
|
+
<# .SYNOPSIS Names of developer tools found on this machine (used to suggest the developer answer). #>
|
|
52
|
+
$found = @()
|
|
53
|
+
foreach ($c in @('node', 'npm', 'yarn', 'pnpm', 'bun', 'deno', 'git', 'docker', 'python', 'pip', 'uv', 'java', 'gradle', 'dotnet', 'cargo', 'go', 'flutter', 'composer', 'php', 'code', 'cursor')) {
|
|
54
|
+
if (Get-Command $c -ErrorAction SilentlyContinue) { $found += $c }
|
|
55
|
+
}
|
|
56
|
+
$dirs = @{
|
|
57
|
+
'Gradle caches' = "$env:USERPROFILE\.gradle"; 'Android SDK' = "$env:LOCALAPPDATA\Android\Sdk"; 'VS Code' = "$env:APPDATA\Code"
|
|
58
|
+
'Docker Desktop' = "$env:LOCALAPPDATA\Docker"; 'JetBrains IDEs' = "$env:LOCALAPPDATA\JetBrains"; 'Visual Studio' = "$env:LOCALAPPDATA\Microsoft\VisualStudio"
|
|
59
|
+
}
|
|
60
|
+
foreach ($k in $dirs.Keys) { if (Test-Path -LiteralPath $dirs[$k]) { $found += $k } }
|
|
61
|
+
return $found
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function Resolve-DeveloperMode {
|
|
65
|
+
<# .SYNOPSIS Decide developer mode: flag > saved answer > interactive question > conservative default (yes).
|
|
66
|
+
-ReadOnly resolves from the flag or the saved answer ONLY. A read-only mode may not ask the question,
|
|
67
|
+
may not write config.json, and may not invent a decision the person never made - so when neither a flag
|
|
68
|
+
nor a saved answer exists it leaves Developer $null and the health report keeps saying "not decided yet",
|
|
69
|
+
which is the truth. Without this, --scan ignored --developer and --not-developer outright: the flag was
|
|
70
|
+
parsed, stored in DeveloperFlag, and never read, so section 0 printed "not decided yet" whatever you
|
|
71
|
+
passed and --json returned developer: null. The desktop window passes those flags to --scan
|
|
72
|
+
(desktop/src/lib/engine.ts) and could not read back the setting it had just sent. #>
|
|
73
|
+
param([switch] $ReadOnly)
|
|
74
|
+
$ws = $Script:WS
|
|
75
|
+
if ($null -ne $ws.DeveloperFlag) { $ws.Developer = [bool]$ws.DeveloperFlag; $ws.DeveloperSource = 'flag'; return }
|
|
76
|
+
if ($ws.ForgetDeveloper) { $ws.Config.developer = $null; $ws.Config.developerAskedAt = $null }
|
|
77
|
+
if ($null -ne $ws.Config.developer) { $ws.Developer = [bool]$ws.Config.developer; $ws.DeveloperSource = 'config'; return }
|
|
78
|
+
if ($ReadOnly) { return }
|
|
79
|
+
if ($ws.Interactive -and -not $ws.Yes) {
|
|
80
|
+
$hints = @(Get-ToolchainHints)
|
|
81
|
+
Write-Box 'One question before anything else' 'It decides how package and build caches are treated'
|
|
82
|
+
Write-Plain ' Are you a developer on this machine?'
|
|
83
|
+
Write-Note "yes = package/build/test-runner caches are only pruned when idle $($ws.Days)+ days (recent work stays fast);"
|
|
84
|
+
Write-Note ' the newest version of every versioned tool cache is kept, unless --purge-all.'
|
|
85
|
+
Write-Note 'no = those caches are cleared completely; project scans are skipped.'
|
|
86
|
+
if ($hints.Count -gt 0) { Write-Note ("detected developer tooling: " + ($hints -join ', ')) }
|
|
87
|
+
$ans = Confirm-Ui -Prompt 'Are you a developer on this machine?' -Default 'y' -NoAutoYes
|
|
88
|
+
$ws.Developer = $ans
|
|
89
|
+
$ws.DeveloperSource = 'answer'
|
|
90
|
+
$ws.Config.developer = $ans
|
|
91
|
+
$ws.Config.developerAskedAt = (Get-Date).ToString('yyyy-MM-dd')
|
|
92
|
+
Save-Config
|
|
93
|
+
if ($ans) { Write-Ok 'developer mode ON - saved to config.json (change with --forget-developer)' } else { Write-Ok 'developer mode OFF - saved to config.json (change with --forget-developer)' }
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
$ws.Developer = $true
|
|
97
|
+
$ws.DeveloperSource = 'default'
|
|
98
|
+
Write-Note 'developer question not asked (non-interactive run) - defaulting to developer mode ON, the conservative choice; pass --not-developer to override'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function Confirm-PurgeAllOnce {
|
|
102
|
+
<# .SYNOPSIS --purge-all from a console asks for a typed word once per run; --yes is the confirmation in batch runs. #>
|
|
103
|
+
$ws = $Script:WS
|
|
104
|
+
if (-not $ws.PurgeAll -or $ws.DryRun -or $ws.Yes) { return }
|
|
105
|
+
if (Confirm-Typed -Prompt 'FULL PURGE: --purge-all empties every cache target completely, including files you used yesterday.' -Word 'purge') { Write-Ok 'purge-all confirmed for this run'; return }
|
|
106
|
+
$ws.PurgeAll = $false
|
|
107
|
+
Write-Info 'purge-all declined - this run prunes by the idle window instead'
|
|
108
|
+
}
|
package/lib/constants.ps1
CHANGED
|
@@ -1,93 +1,103 @@
|
|
|
1
|
-
# constants.ps1 - project metadata, section catalogue, profiles and gates.
|
|
2
|
-
# Dot-sourced by windowsweep.ps1. ASCII only (PowerShell 5.1 reads BOM-less files as ANSI).
|
|
3
|
-
|
|
4
|
-
# Single source of truth for the version is package.json: the Node launcher exports it as
|
|
5
|
-
# WINDOWSWEEP_VERSION. A direct checkout falls back to this literal; the version:check script
|
|
6
|
-
# asserts the two never drift.
|
|
7
|
-
$Script:WS_VERSION_FALLBACK = '1.
|
|
8
|
-
|
|
9
|
-
$Script:WS_NAME = 'windowsweep'
|
|
10
|
-
$Script:WS_TAGLINE = '
|
|
11
|
-
$Script:WS_AUTHOR = 'Ahsan Mahmood'
|
|
12
|
-
$Script:WS_EMAIL = 'aoneahsan@gmail.com'
|
|
13
|
-
$Script:WS_WEB = 'https://aoneahsan.com'
|
|
14
|
-
$Script:WS_LINKEDIN = 'https://linkedin.com/in/aoneahsan'
|
|
15
|
-
$Script:WS_GITHUB = 'https://github.com/aoneahsan'
|
|
16
|
-
$Script:WS_REPO = 'https://github.com/aoneahsan/windowsweep'
|
|
17
|
-
$Script:WS_ISSUES = 'https://github.com/aoneahsan/windowsweep/issues'
|
|
18
|
-
$Script:WS_NPM = 'https://www.npmjs.com/package/windowsweep'
|
|
19
|
-
$Script:WS_DOCS = 'https://github.com/aoneahsan/windowsweep/blob/main/docs/README.md'
|
|
20
|
-
$Script:WS_SUPPORT = 'https://aoneahsan.com/payment?project-id=windowsweep&project-identifier=windowsweep'
|
|
21
|
-
$Script:WS_LICENSE = 'MIT License'
|
|
22
|
-
|
|
23
|
-
# Exit codes (documented in docs/cli-reference.md).
|
|
24
|
-
$Script:WS_EXIT_OK = 0
|
|
25
|
-
$Script:WS_EXIT_FAIL = 1
|
|
26
|
-
$Script:WS_EXIT_USAGE = 2
|
|
27
|
-
$Script:WS_EXIT_REFUSED = 3
|
|
28
|
-
$Script:WS_EXIT_INTERRUPT = 130
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@{ Id =
|
|
47
|
-
@{ Id =
|
|
48
|
-
@{ Id =
|
|
49
|
-
@{ Id =
|
|
50
|
-
@{ Id =
|
|
51
|
-
@{ Id =
|
|
52
|
-
@{ Id =
|
|
53
|
-
@{ Id =
|
|
54
|
-
@{ Id =
|
|
55
|
-
@{ Id =
|
|
56
|
-
@{ Id =
|
|
57
|
-
@{ Id =
|
|
58
|
-
@{ Id =
|
|
59
|
-
@{ Id =
|
|
60
|
-
@{ Id =
|
|
61
|
-
@{ Id =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
$
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
$Script:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
# constants.ps1 - project metadata, section catalogue, profiles and gates.
|
|
2
|
+
# Dot-sourced by windowsweep.ps1. ASCII only (PowerShell 5.1 reads BOM-less files as ANSI).
|
|
3
|
+
|
|
4
|
+
# Single source of truth for the version is package.json: the Node launcher exports it as
|
|
5
|
+
# WINDOWSWEEP_VERSION. A direct checkout falls back to this literal; the version:check script
|
|
6
|
+
# asserts the two never drift.
|
|
7
|
+
$Script:WS_VERSION_FALLBACK = '1.2.0'
|
|
8
|
+
|
|
9
|
+
$Script:WS_NAME = 'windowsweep'
|
|
10
|
+
$Script:WS_TAGLINE = 'Developer-aware Windows cleanup CLI: dry-run first, personal folders refused, zero install via npx.'
|
|
11
|
+
$Script:WS_AUTHOR = 'Ahsan Mahmood'
|
|
12
|
+
$Script:WS_EMAIL = 'aoneahsan@gmail.com'
|
|
13
|
+
$Script:WS_WEB = 'https://aoneahsan.com'
|
|
14
|
+
$Script:WS_LINKEDIN = 'https://linkedin.com/in/aoneahsan'
|
|
15
|
+
$Script:WS_GITHUB = 'https://github.com/aoneahsan'
|
|
16
|
+
$Script:WS_REPO = 'https://github.com/aoneahsan/windowsweep'
|
|
17
|
+
$Script:WS_ISSUES = 'https://github.com/aoneahsan/windowsweep/issues'
|
|
18
|
+
$Script:WS_NPM = 'https://www.npmjs.com/package/windowsweep'
|
|
19
|
+
$Script:WS_DOCS = 'https://github.com/aoneahsan/windowsweep/blob/main/docs/README.md'
|
|
20
|
+
$Script:WS_SUPPORT = 'https://aoneahsan.com/payment?project-id=windowsweep&project-identifier=windowsweep'
|
|
21
|
+
$Script:WS_LICENSE = 'MIT License'
|
|
22
|
+
|
|
23
|
+
# Exit codes (documented in docs/cli-reference.md).
|
|
24
|
+
$Script:WS_EXIT_OK = 0
|
|
25
|
+
$Script:WS_EXIT_FAIL = 1
|
|
26
|
+
$Script:WS_EXIT_USAGE = 2
|
|
27
|
+
$Script:WS_EXIT_REFUSED = 3
|
|
28
|
+
$Script:WS_EXIT_INTERRUPT = 130
|
|
29
|
+
|
|
30
|
+
# The protected-path CATEGORIES, in the wording the tool shows a person. Both --list-targets and
|
|
31
|
+
# --list --json read this one list: the JSON is the same sentences, so a front end cannot show a user
|
|
32
|
+
# a narrower promise than the console does.
|
|
33
|
+
$Script:WS_PROTECT_CATEGORIES = @(
|
|
34
|
+
'every drive root, Windows, System32, Program Files, ProgramData, the user profile root and AppData roots',
|
|
35
|
+
'browser profile data (Local Storage, IndexedDB, cookies, history, bookmarks, extensions, PWA CacheStorage)',
|
|
36
|
+
'editor user data (settings, globalStorage, local History); UWP LocalState; toolchains (nvm, npm globals, corepack, ...)',
|
|
37
|
+
'NTUSER.DAT, UsrClass.dat, hiberfil/pagefile/swapfile (only powercfg touches hiberfil), Prefetch, Windows\Installer, WinSxS (DISM only)'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Section catalogue. Numbering is a public contract frozen at 1.0.0: a section may be retired
|
|
41
|
+
# (kept as a no-op that says so) but a number is never reused for something else.
|
|
42
|
+
# Tier: report | rebuilds | slow | recycle | permanent | config
|
|
43
|
+
# Batch: safe (in --all) | optin (--only/--profile with --yes) | deep (needs --i-understand-deep)
|
|
44
|
+
# | interactive (never batch; dry-run/report allowed)
|
|
45
|
+
$Script:WS_SECTIONS = @(
|
|
46
|
+
@{ Id = 0; Key = 'health'; Title = 'System health report'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section00' }
|
|
47
|
+
@{ Id = 1; Key = 'pkg'; Title = 'Package-manager caches (npm, yarn, pnpm, bun, deno, pip, uv, Composer, NuGet, Cargo, Go, pub)'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $true; Fn = 'Invoke-Section01' }
|
|
48
|
+
@{ Id = 2; Key = 'build'; Title = 'Build-tool caches (Gradle, Maven, Android, Unity, JetBrains)'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $true; Fn = 'Invoke-Section02' }
|
|
49
|
+
@{ Id = 3; Key = 'runners'; Title = 'Test-runner browsers (Cypress, Playwright, Puppeteer) - keep newest'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $true; Fn = 'Invoke-Section03' }
|
|
50
|
+
@{ Id = 4; Key = 'avd'; Title = 'Android emulators (AVDs) idle N+ days'; Tier = 'slow'; Admin = $false; Batch = 'optin'; Dev = $true; Fn = 'Invoke-Section04' }
|
|
51
|
+
@{ Id = 5; Key = 'docker'; Title = 'Docker: dangling images, build cache, unused images older than N days'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $true; Fn = 'Invoke-Section05' }
|
|
52
|
+
@{ Id = 6; Key = 'editors'; Title = 'Editor caches (VS Code, Cursor, Windsurf, Visual Studio) + superseded extensions'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section06' }
|
|
53
|
+
@{ Id = 7; Key = 'browsers'; Title = 'Browser caches (Chrome, Edge, Brave, Vivaldi, Opera, Chromium, Firefox)'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section07' }
|
|
54
|
+
@{ Id = 8; Key = 'apps'; Title = 'Desktop app caches (Discord, Slack, Teams, Zoom, Spotify, Postman, Figma, ...)'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section08' }
|
|
55
|
+
@{ Id = 9; Key = 'wincaches'; Title = 'Windows user caches (INetCache, WER, crash dumps, shader caches, UWP temp)'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section09' }
|
|
56
|
+
@{ Id = 10; Key = 'temp'; Title = 'User temp files older than N days'; Tier = 'rebuilds'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section10' }
|
|
57
|
+
@{ Id = 11; Key = 'recycle'; Title = 'Empty the Recycle Bin - PERMANENT'; Tier = 'permanent'; Admin = $false; Batch = 'deep'; Dev = $false; Fn = 'Invoke-Section11' }
|
|
58
|
+
@{ Id = 12; Key = 'wu'; Title = 'Windows Update + system temp (SoftwareDistribution, Delivery Optimization, Windows\Temp, CBS logs)'; Tier = 'rebuilds'; Admin = $true; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section12' }
|
|
59
|
+
@{ Id = 13; Key = 'cleanmgr'; Title = 'Windows Disk Cleanup engine (cleanmgr, curated handlers)'; Tier = 'rebuilds'; Admin = $true; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section13' }
|
|
60
|
+
@{ Id = 14; Key = 'dism'; Title = 'Component store cleanup (DISM StartComponentCleanup) - slow'; Tier = 'rebuilds'; Admin = $true; Batch = 'optin'; Dev = $false; Fn = 'Invoke-Section14' }
|
|
61
|
+
@{ Id = 15; Key = 'hiberfil'; Title = 'Hibernation file (off / reduced)'; Tier = 'config'; Admin = $true; Batch = 'deep'; Dev = $false; Fn = 'Invoke-Section15' }
|
|
62
|
+
@{ Id = 16; Key = 'eventlogs'; Title = 'Clear Windows Event Logs - PERMANENT'; Tier = 'permanent'; Admin = $true; Batch = 'deep'; Dev = $false; Fn = 'Invoke-Section16' }
|
|
63
|
+
@{ Id = 17; Key = 'projects'; Title = 'Stale project build artefacts (node_modules, dist, .next, target, ...)'; Tier = 'rebuilds'; Admin = $false; Batch = 'interactive'; Dev = $true; Fn = 'Invoke-Section17' }
|
|
64
|
+
@{ Id = 18; Key = 'partials'; Title = 'Partial / orphan downloads -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section18' }
|
|
65
|
+
@{ Id = 19; Key = 'large'; Title = 'Large stale personal files (Downloads) -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section19' }
|
|
66
|
+
@{ Id = 20; Key = 'vhdx'; Title = 'Docker Desktop / WSL disk image compaction (stops Docker + WSL)'; Tier = 'config'; Admin = $true; Batch = 'deep'; Dev = $true; Fn = 'Invoke-Section20' }
|
|
67
|
+
@{ Id = 21; Key = 'diskusage'; Title = 'Disk usage report (largest entries, drives, disk images)'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section21' }
|
|
68
|
+
@{ Id = 22; Key = 'globals'; Title = 'Globally installed packages audit (npm, pnpm, yarn, bun, deno) - report only'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section22' }
|
|
69
|
+
@{ Id = 23; Key = 'orphaned'; Title = 'Orphaned application data under AppData -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section23' }
|
|
70
|
+
@{ Id = 24; Key = 'programs'; Title = 'Installed programs not modified for N+ days - report only'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section24' }
|
|
71
|
+
@{ Id = 25; Key = 'startup'; Title = 'Startup items audit (Run keys, Startup folders, logon tasks) - report only'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section25' }
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# The safe batch run by --all. Admin sections 12 and 13 join it only when already elevated.
|
|
75
|
+
$Script:WS_SAFE_BATCH = @(0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 21)
|
|
76
|
+
$Script:WS_SAFE_BATCH_ADMIN = @(12, 13)
|
|
77
|
+
|
|
78
|
+
$Script:WS_PROFILES = @{
|
|
79
|
+
'dev' = @(1, 2, 3, 4, 5, 6, 17)
|
|
80
|
+
'minimal' = @(7, 8, 9, 10)
|
|
81
|
+
'cache-only' = @(1, 2, 3, 6, 7, 8, 9)
|
|
82
|
+
'system' = @(12, 13, 14)
|
|
83
|
+
'deep' = @(0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 21)
|
|
84
|
+
'audit' = @(0, 21, 22, 24, 25)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Walkthrough order (interactive default mode). Admin sections are inserted when elevated.
|
|
88
|
+
$Script:WS_WALKTHROUGH = @(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 18, 19, 23)
|
|
89
|
+
$Script:WS_WALKTHROUGH_ADMIN = @(12, 13, 14)
|
|
90
|
+
|
|
91
|
+
function Get-Section {
|
|
92
|
+
<# .SYNOPSIS Returns the catalogue entry for a section id, or $null. #>
|
|
93
|
+
param([int] $Id)
|
|
94
|
+
foreach ($s in $Script:WS_SECTIONS) { if ($s.Id -eq $Id) { return $s } }
|
|
95
|
+
return $null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function Get-ToolVersion {
|
|
99
|
+
<# .SYNOPSIS The running version: launcher-provided env wins over the checkout fallback. #>
|
|
100
|
+
$v = $env:WINDOWSWEEP_VERSION
|
|
101
|
+
if ([string]::IsNullOrWhiteSpace($v)) { return $Script:WS_VERSION_FALLBACK }
|
|
102
|
+
return $v.Trim()
|
|
103
|
+
}
|