Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ jobs:
- name: Syntax parse check
shell: pwsh
run: |
$files = './Cleanup-Windows-Senior.ps1','./Optimize-Windows-Senior.ps1','./Repair-Windows-Senior.ps1','./WinSenior.ps1'
$files = (Get-ChildItem -Path . -Filter *.ps1 -File).FullName
Write-Host "Parsing: $($files -join ', ')"
$failed = $false
foreach ($f in $files) {
$errors = $null
Expand All @@ -39,7 +40,7 @@ jobs:
- name: PSScriptAnalyzer (fail on errors only)
shell: pwsh
run: |
$files = './Cleanup-Windows-Senior.ps1','./Optimize-Windows-Senior.ps1','./Repair-Windows-Senior.ps1','./WinSenior.ps1'
$files = (Get-ChildItem -Path . -Filter *.ps1 -File).FullName
$r = $files | ForEach-Object { Invoke-ScriptAnalyzer -Path $_ -Severity Error }
if ($r) { $r | Format-Table -AutoSize | Out-String | Write-Host; Write-Error 'ScriptAnalyzer found error-level issues'; exit 1 }
Write-Host 'No analyzer errors'
Expand Down
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ __pycache__/
Thumbs.db
desktop.ini
*.tmp

# codebase-index cache (machine-local; do not commit)
.claude/cache/codebase-index/

# AI assistant / editor tooling configs (installed per-tool, machine-local - not project files)
.codex/
.cursor/
.opencode/
.windsurf/
.zed/
AGENTS.md

# Release build output
dist/
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Changelog

All notable changes to this project are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [6.1.0] - 2026-06-24

### Added
- **Shared library `WinSenior.Common.ps1`** dot-sourced by every engine and the menu:
the admin check, WhatIf probe, byte formatter, canonical logger (`Write-WsLog`), and the
System Restore routine (`New-WinSeniorRestorePoint`) now live in one place.
- **Unified JSON report** across all three engines. `-ReportPath` writes one envelope
(`Tool/Version/Engine/Host/Timestamp/Mode/RestorePoint/DurationSec` + `Summary` + `Items`)
via `Write-WinSeniorReport`, so a single parser reads cleanup, optimize and repair output.
- **Scheduled-task installer** (`WinSenior.Schedule.ps1`). `WinSenior.ps1 -InstallSchedule`
registers a weekly unattended cleanup and a monthly read-only health scan under `\WinSenior\`
(reports to `%ProgramData%\WinSenior\reports`); `-RemoveSchedule` removes them.
- **Cleanup coverage 57 → 63:** per-user Windows caches, PowerShell module cache, Remote Desktop
bitmap cache, live-kernel dumps, the SRUM usage database, and the EventTranscript telemetry DB;
extended the shader-cache task (NVIDIA OptixCache/NV_Cache) and Windows.old (`$WinREAgent`).
- **Optimization coverage 29 → 49:** modern Windows 11 privacy/debloat — disable Recall &
Click-to-Do, Copilot, tailored-ad experiences, the Windows Spotlight policy, inking/typing and
online-speech telemetry, CEIP, the App-Compat appraiser, Windows Error Reporting upload,
Delivery-Optimization P2P upload, OneDrive pre-sign-in traffic, cloud clipboard (off by
default); combined taskbar/Start ad-surface debloat, the SCOOBE setup nag, show-file-extensions
(off), the classic Windows 10 context menu (off), Teredo (off) and Fast Startup (off).
- **Troubleshooting coverage 13 → 25:** System Restore protection, hosts-file integrity,
proxy/PAC hijack, firewall state, Defender signatures & active threats, SMBv1, critical
scheduled-task health, BITS queue, print spooler, Microsoft Store health, and report-only
SSD wear/temperature and crash history.
- `CHANGELOG.md` and `tools/Build-Release.ps1` (zip + SHA256SUMS) and `tools/Sign-Scripts.ps1`.

### Changed
- **Report schema is unified** (breaking for anyone parsing the old `clean.json`/`repair.json`):
engine-specific counters moved under `Summary`, and `Tasks`/`Results`/`Tweaks` are now `Items`.
- CI parses and analyzes **all** root `*.ps1` (was a hardcoded 4-file list), so the UI, common
and schedule libraries are linted too.
- Engine loggers and restore-point functions are now thin wrappers over the shared library
(~175 duplicated lines removed) with identical public signatures.

### Fixed
- `Write-WinSeniorReport` casts `Items` via `[object[]]`: `@()` throws
`System.ArgumentException` on the `Generic.List[object]` the engines pass.
- The Store-health check wraps `Get-AppxPackage` in try/catch — the Appx module raises a
terminating load error under PowerShell 7 that `-ErrorAction SilentlyContinue` does not catch.

## [6.0.0] - 2026-06-23

### Added
- Initial v6 release: registry-driven cleanup engine (57 tasks), optimization engine
(29 reversible tweaks), troubleshooting engine (13 checks), and the `WinSenior.ps1`
arrow-key TUI menu. Real `-WhatIf` via `SupportsShouldProcess`, a hard `Test-SafeToDelete`
guard, risk tiers (Safe/Moderate/Aggressive default; Dangerous behind `-IncludeDangerous`),
real System Restore points, and per-tweak undo.

[6.1.0]: https://github.com/denfry/WindowsCleaner/releases/tag/v6.1.0
[6.0.0]: https://github.com/denfry/WindowsCleaner/releases/tag/v6.0.0
113 changes: 39 additions & 74 deletions Cleanup-Windows-Senior.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

.NOTES
Author : denfry (https://github.com/denfry/WindowsCleaner)
Version : 6.0.0
Version : 6.1.0
Requires: PowerShell 5.1+ (Windows). Administrator rights for most tasks.

.EXAMPLE
Expand Down Expand Up @@ -120,6 +120,11 @@ $script:DenyList = @(
${env:ProgramFiles(x86)}
) | Where-Object { $_ } | ForEach-Object { $_.TrimEnd('\').ToLowerInvariant() }

# =====================================================================
# SHARED LIBRARY (admin / restore-point / logging / format helpers)
# =====================================================================
. (Join-Path $PSScriptRoot 'WinSenior.Common.ps1')

# =====================================================================
# LOGGING
# =====================================================================
Expand All @@ -129,39 +134,12 @@ function Write-CleanupLog {
[ValidateSet('Info','Success','Warning','Error','Debug','Step','WhatIf','Safety')]
[string]$Level = 'Info'
)
$tag = switch ($Level) {
'Success' { '[+]' } 'Warning' { '[!]' } 'Error' { '[x]' }
'Step' { '==>' } 'WhatIf' { '[~]' } 'Safety' { '[#]' }
'Debug' { ' ' } default { '[i]' }
}
$color = switch ($Level) {
'Success' { 'Green' } 'Warning' { 'Yellow' } 'Error' { 'Red' }
'Step' { 'Cyan' } 'WhatIf' { 'Cyan' } 'Safety' { 'Magenta' }
'Debug' { 'DarkGray' } default { 'Gray' }
}
$line = "$tag $Message"
if ($Level -ne 'Debug' -or $VerbosePreference -ne 'SilentlyContinue') {
Write-Host $line -ForegroundColor $color
}
$stamp = "[{0:yyyy-MM-dd HH:mm:ss}] [{1}] {2}" -f (Get-Date), $Level, $Message
# Logging is infrastructure, not a cleanup action: never let -WhatIf suppress it.
try { Add-Content -Path $LogPath -Value $stamp -ErrorAction SilentlyContinue -WhatIf:$false } catch { }
Write-WsLog -Message $Message -Level $Level -LogPath $LogPath
}

# =====================================================================
# UTILITIES
# =====================================================================
function Format-FileSize {
param([long]$Size)
if ($Size -ge 1TB) { '{0:N2} TB' -f ($Size / 1TB) }
elseif ($Size -ge 1GB) { '{0:N2} GB' -f ($Size / 1GB) }
elseif ($Size -ge 1MB) { '{0:N2} MB' -f ($Size / 1MB) }
elseif ($Size -ge 1KB) { '{0:N2} KB' -f ($Size / 1KB) }
else { "$Size B" }
}

function Test-WhatIfMode { [bool]$WhatIfPreference }

function Get-ItemSize {
param([System.IO.FileSystemInfo]$Item)
if ($Item.PSIsContainer) {
Expand Down Expand Up @@ -344,36 +322,12 @@ function Remove-ProtectedFolder {
# =====================================================================
# SAFETY / ENVIRONMENT
# =====================================================================
function Test-AdminPrivileges {
try {
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
(New-Object Security.Principal.WindowsPrincipal($id)).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)
} catch { $false }
}

function New-CleanupRestorePoint {
if (Test-WhatIfMode) {
Write-CleanupLog '[WhatIf] would create a System Restore point' 'WhatIf'
return $true
}
Write-CleanupLog 'Creating System Restore point...' 'Safety'
try {
$rk = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore'
New-ItemProperty -Path $rk -Name 'SystemRestorePointCreationFrequency' `
-Value 0 -PropertyType DWord -Force -ErrorAction SilentlyContinue | Out-Null
Enable-ComputerRestore -Drive "$env:SystemDrive\" -ErrorAction SilentlyContinue
Checkpoint-Computer -Description "Before Windows Cleanup $(Get-Date -Format 'yyyy-MM-dd HH:mm')" `
-RestorePointType 'MODIFY_SETTINGS' -ErrorAction Stop
Write-CleanupLog 'System Restore point created' 'Success'
$script:RestorePointMade = $true
return $true
}
catch {
Write-CleanupLog "Restore point not created: $($_.Exception.Message)" 'Warning'
Write-CleanupLog 'Continuing without a restore point (System Protection may be off).' 'Warning'
return $false
}
$st = New-WinSeniorRestorePoint `
-Description "Before Windows Cleanup $(Get-Date -Format 'yyyy-MM-dd HH:mm')" `
-LogAction { param($m, $l) Write-CleanupLog $m $l }
if ($st -eq 'Created') { $script:RestorePointMade = $true }
return ($st -ne 'Failed')
}

function Stop-BrowserProcesses {
Expand Down Expand Up @@ -478,6 +432,9 @@ function Get-CleanupTaskRegistry {
'<USER>\go\pkg\mod\cache\download\*',
'<USER>\AppData\Local\go-build\*',
'<USER>\AppData\Local\Pub\Cache\*')
New-CleanupTask ps-modulecache 'PowerShell module analysis cache' DevTools Safe -Paths @(
'<USER>\AppData\Local\Microsoft\Windows\PowerShell\ModuleAnalysisCache',
'<USER>\AppData\Local\Microsoft\Windows\PowerShell\StartupProfileData-*')

# ---------------- Apps / messengers (Safe) ----------------
New-CleanupTask appcache 'Windows app cache' Apps Safe -Paths @(
Expand Down Expand Up @@ -512,6 +469,8 @@ function Get-CleanupTaskRegistry {
'<USER>\AppData\Roaming\Adobe\Common\Media Cache\*',
'<USER>\AppData\Roaming\Adobe\Common\Media Cache Files\*',
'<USER>\AppData\Local\Adobe\CameraRaw\Cache\*')
New-CleanupTask rdp-cache 'Remote Desktop client bitmap cache' Apps Safe -Paths @(
'<USER>\AppData\Local\Microsoft\Terminal Server Client\Cache\*')

# ---------------- Games (launcher caches, Safe) ----------------
New-CleanupTask game-caches 'Game launcher caches (Steam/Epic/Battle.net/GOG)' Games Safe -Paths @(
Expand All @@ -538,7 +497,11 @@ function Get-CleanupTaskRegistry {
'<USER>\AppData\Local\D3DSCache\*',
'<USER>\AppData\Local\NVIDIA\DXCache\*',
'<USER>\AppData\Local\NVIDIA\GLCache\*',
'<USER>\AppData\Local\NVIDIA\OptixCache\*',
'<USER>\AppData\Local\NVIDIA Corporation\NV_Cache\*',
'<USER>\AppData\Local\AMD\DxCache\*')
New-CleanupTask win-caches 'Windows per-user app caches' System Safe -Paths @(
'<USER>\AppData\Local\Microsoft\Windows\Caches\*')
New-CleanupTask gpu-leftovers 'GPU driver installer leftovers (NVIDIA/AMD)' System Safe -Paths @(
'<DRIVE>NVIDIA\*',
'<DRIVE>AMD\*',
Expand Down Expand Up @@ -625,6 +588,13 @@ function Get-CleanupTaskRegistry {
'%WINDIR%\inf\setupapi.dev*.log',
'%WINDIR%\inf\setupapi.setup*.log',
'%ProgramData%\Microsoft\Windows Defender\Scans\History\Results\*')
New-CleanupTask livekernel 'Live kernel crash dumps (driver/GPU TDR)' Logs Safe -Paths @(
'%WINDIR%\LiveKernelReports\*.dmp')
New-CleanupTask srum-db 'Network/app usage telemetry DB (SRUM)' Logs Moderate `
-StopServices @('DPS') -Paths @('%WINDIR%\System32\sru\*')
New-CleanupTask eventtranscript 'Diagnostic telemetry database (EventTranscript)' Logs Moderate `
-StopServices @('DiagTrack') -Paths @(
'%ProgramData%\Microsoft\Diagnosis\EventTranscript\*')
New-CleanupTask crashdumps 'Crash & memory dumps' Logs Moderate -Paths @(
'%WINDIR%\Minidump\*',
'%WINDIR%\MEMORY.DMP',
Expand Down Expand Up @@ -672,6 +642,7 @@ function Get-CleanupTaskRegistry {
"$env:SystemDrive\Windows.old",
"$env:SystemDrive\`$Windows.~BT",
"$env:SystemDrive\`$Windows.~WS",
"$env:SystemDrive\`$WinREAgent",
"$env:WINDIR\Downloaded Program Files")) {
$r = Remove-ProtectedFolder -FullPath $folder -Description 'upgrade leftovers'
if ($r) { $total.Bytes += $r.Bytes; $total.Errors += $r.Errors }
Expand Down Expand Up @@ -806,22 +777,16 @@ function Show-CleanupSummary {
}

function Write-CleanupReport {
if (-not $ReportPath) { return }
$report = [pscustomobject]@{
Timestamp = (Get-Date).ToString('s')
Mode = if (Test-WhatIfMode) { 'DryRun' } else { 'Live' }
RestorePoint = $script:RestorePointMade
TotalBytes = $script:TotalBytes
TotalFreed = (Format-FileSize $script:TotalBytes)
TotalFiles = $script:TotalFiles
TotalErrors = $script:TotalErrors
DurationSec = [math]::Round(((Get-Date) - $script:StartTime).TotalSeconds, 1)
Tasks = $script:Stats
}
try {
$report | ConvertTo-Json -Depth 5 | Set-Content -Path $ReportPath -Encoding UTF8 -WhatIf:$false
Write-CleanupLog "JSON report written: $ReportPath" 'Info'
} catch { Write-CleanupLog "Could not write report: $($_.Exception.Message)" 'Warning' }
Write-WinSeniorReport -ReportPath $ReportPath -Engine 'Cleanup' `
-RestorePoint $script:RestorePointMade -StartTime $script:StartTime `
-Summary @{
TotalBytes = $script:TotalBytes
TotalFreed = (Format-FileSize $script:TotalBytes)
TotalFiles = $script:TotalFiles
TotalErrors = $script:TotalErrors
} `
-Items $script:Stats `
-LogAction { param($m, $l) Write-CleanupLog $m $l }
}

# =====================================================================
Expand Down
Loading
Loading