windowsweep 1.0.0 → 1.1.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 +199 -0
- package/CHANGELOG.md +97 -0
- package/README.md +30 -17
- package/VERSION +1 -1
- package/lib/actions.ps1 +9 -6
- package/lib/config.ps1 +9 -0
- package/lib/constants.ps1 +10 -6
- package/lib/log.ps1 +1 -1
- package/lib/safety.ps1 +10 -10
- package/lib/scan.ps1 +10 -4
- package/lib/ui.ps1 +52 -9
- package/modules/crash_trap.ps1 +2 -2
- package/modules/docker.ps1 +1 -1
- package/modules/editors.ps1 +2 -1
- package/modules/globals_audit.ps1 +236 -0
- package/modules/health.ps1 +1 -1
- package/modules/installed_programs.ps1 +118 -0
- package/modules/menu.ps1 +3 -1
- package/modules/notify.ps1 +60 -0
- package/modules/orphaned_appdata.ps1 +189 -0
- package/modules/personal.ps1 +9 -7
- package/modules/pkg_managers.ps1 +4 -0
- package/modules/projects.ps1 +28 -8
- package/modules/release_helpers.ps1 +26 -2
- package/modules/runner.ps1 +56 -7
- package/modules/self_test_extra.ps1 +360 -0
- package/modules/startup_audit.ps1 +126 -0
- package/modules/windows_user.ps1 +6 -0
- package/package.json +3 -3
- package/windowsweep.ps1 +26 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# AI integration guide
|
|
2
|
+
|
|
3
|
+
This is the contract an automated caller can rely on: what windowsweep promises, what it refuses, what it
|
|
4
|
+
prints, and what it returns. It is written for an agent or a script rather than a person. For what the tool
|
|
5
|
+
*is*, read the [README](https://github.com/aoneahsan/windowsweep#readme); for what each section touches, read
|
|
6
|
+
[Sections 0-25](https://github.com/aoneahsan/windowsweep/blob/main/docs/sections.md).
|
|
7
|
+
|
|
8
|
+
**windowsweep deletes files.** Every command below that is not marked read-only can remove data. There is no
|
|
9
|
+
undo for caches. Treat a real run as an irreversible operation and preview it first.
|
|
10
|
+
|
|
11
|
+
## The safe sequence
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
npx windowsweep --self-test --no-color --no-report # read-only: proves the guards on this machine
|
|
15
|
+
npx windowsweep --scan --no-color --no-report # read-only: every target and its size
|
|
16
|
+
npx windowsweep --dry-run --all --yes --no-color # writes nothing; reports what a real run would remove
|
|
17
|
+
npx windowsweep --all --yes --no-color # the real safe batch
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Add `--json` when a program reads the result. In that mode **stdout carries exactly one line, the JSON
|
|
21
|
+
summary**, and every human-readable line goes to stderr. `--json` also implies `--quiet`.
|
|
22
|
+
|
|
23
|
+
## What `--yes` covers, and what it never covers
|
|
24
|
+
|
|
25
|
+
`--yes` auto-confirms **regenerable caches only**: sections 0-3, 5-10, 21, and 12-14 when the console is
|
|
26
|
+
already elevated.
|
|
27
|
+
|
|
28
|
+
| Never covered by `--yes` | Behaviour |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Sections 17, 18, 19, 23 | The selection prompt appears even with `--yes` and defaults to none. With no console attached nothing is selected, and each asks a final question `--yes` does not answer. Only `--select` / `--select-file` supply a choice (see below) |
|
|
31
|
+
| Sections 11, 15, 16, 20 | Deep. Refused in batch mode unless `--i-understand-deep` is also passed |
|
|
32
|
+
| `--uninstall-data` | Always asks; `--yes` does not answer it |
|
|
33
|
+
| `--purge-all` | From a console it asks for a typed `purge` once per run. In a batch run `--yes` is the confirmation |
|
|
34
|
+
|
|
35
|
+
In batch mode (`--all`, `--only`, `--profile`) sections 17, 18, 19 and 23 are refused outright and reported as
|
|
36
|
+
`refused`, unless a `--select` or `--select-file` choice was supplied. They need a person choosing items -
|
|
37
|
+
in advance is fine, absent is not.
|
|
38
|
+
|
|
39
|
+
## Exit codes
|
|
40
|
+
|
|
41
|
+
| Code | Meaning |
|
|
42
|
+
|---|---|
|
|
43
|
+
| 0 | Success |
|
|
44
|
+
| 1 | A section failed, or the self-test found a failure |
|
|
45
|
+
| 2 | Usage error, or an interactive mode was started without a console |
|
|
46
|
+
| 3 | A section named in `--only` was refused, or an installer was started from `npx` |
|
|
47
|
+
| 130 | Interrupted before the run finished (Ctrl-C) |
|
|
48
|
+
|
|
49
|
+
## Flags that matter to automation
|
|
50
|
+
|
|
51
|
+
| Flag | Effect |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `--json` | One JSON line on stdout; everything else on stderr. Implies `--quiet` |
|
|
54
|
+
| `--no-color`, `--ascii` | Disable colour and Unicode glyphs. Both are automatic when output is redirected |
|
|
55
|
+
| `--no-report` | Skip the JSON report file; the session log is still written |
|
|
56
|
+
| `--cleanup-logs` | Delete this run's log at exit; reports are kept |
|
|
57
|
+
| `--quiet` | Fewer informational lines |
|
|
58
|
+
| `--only L`, `--profile NAME`, `--exclude L` | Choose sections: `--only 1,3,5-7`; profiles `dev`, `minimal`, `cache-only`, `system`, `deep`, `audit` |
|
|
59
|
+
| `--days N`, `--temp-days N` | Idle windows, default 100 and 3 |
|
|
60
|
+
| `--developer`, `--not-developer` | Override the saved developer answer for this run |
|
|
61
|
+
| `--scan-roots "P1;P2"`, `--exclude-path P` | Section 17 roots and exclusions |
|
|
62
|
+
| `--logs-dir P`, `--reports-dir P` | Redirect this run's output |
|
|
63
|
+
| `--select L`, `--select-file P` | Supply an interactive section's selection in advance, by index or by full path. The only way sections 17/18/19/23 run unattended |
|
|
64
|
+
| `--notify` | A Windows notification when the run ends. Never changes the exit code, never writes to stdout |
|
|
65
|
+
| `--list --json` | Print the section catalogue as one JSON line instead of the human table |
|
|
66
|
+
| `--pwsh` | Launcher only: run the engine on PowerShell 7 |
|
|
67
|
+
|
|
68
|
+
A non-interactive run with no saved developer answer defaults to developer mode **on**, the conservative
|
|
69
|
+
choice, and says so.
|
|
70
|
+
|
|
71
|
+
## The `--json` line
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{"tool":"windowsweep","version":"1.1.0","mode":"all","dry_run":false,"elevated":false,"developer":true,
|
|
75
|
+
"freed_bytes":0,"estimated_bytes":0,
|
|
76
|
+
"sections":[{"section":1,"status":"ran","freed_bytes":0}],
|
|
77
|
+
"candidates":[],"targets":[],
|
|
78
|
+
"refusals":[],"log_file":"...","report_file":"..."}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Key | Meaning |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `tool`, `version` | Always `windowsweep` and the running version |
|
|
84
|
+
| `mode` | `all`, `only`, `scan`, `walkthrough`, `menu`, ... |
|
|
85
|
+
| `dry_run`, `elevated` | The two booleans that change what a run does |
|
|
86
|
+
| `developer` | `true`, `false`, or `null` in a mode that never resolves the question (`--scan`, `--list`) |
|
|
87
|
+
| `freed_bytes` | Real bytes removed. `0` in a dry-run |
|
|
88
|
+
| `estimated_bytes` | What a dry-run says a real run would remove. `0` in a real run |
|
|
89
|
+
| `sections[]` | One entry per section attempted: `section`, `status`, `freed_bytes` |
|
|
90
|
+
| `candidates[]` | What an interactive section offered: `section`, `index`, `path`, `bytes`, `idle_days`, `project`. Always present, empty when none were collected |
|
|
91
|
+
| `targets[]` | Scan mode only: `section`, `label`, `path`, `bytes`. Always present, empty otherwise |
|
|
92
|
+
| `refusals[]` | Human-readable reasons a section was refused in batch mode |
|
|
93
|
+
| `log_file`, `report_file` | Absolute paths, or `null` under `--no-report` |
|
|
94
|
+
|
|
95
|
+
`status` is one of `ran`, `dry-run`, `skipped`, `refused`, `failed`.
|
|
96
|
+
|
|
97
|
+
## Where output lands
|
|
98
|
+
|
|
99
|
+
| Item | Path |
|
|
100
|
+
|---|---|
|
|
101
|
+
| Session logs | `%USERPROFILE%\.windowsweep\logs\` |
|
|
102
|
+
| JSON reports and exports | `%USERPROFILE%\.windowsweep\reports\` |
|
|
103
|
+
| Debug and crash bundles | `%USERPROFILE%\.windowsweep\feedback\` |
|
|
104
|
+
| Settings | `%USERPROFILE%\.windowsweep\config.json` |
|
|
105
|
+
|
|
106
|
+
Override the root with `WINDOWSWEEP_HOME`, or the two folders with `--logs-dir` / `--reports-dir`
|
|
107
|
+
(`WINDOWSWEEP_LOG_DIR` / `WINDOWSWEEP_REPORTS_DIR`). `WINDOWSWEEP_NO_COLOR`, `NO_COLOR` and
|
|
108
|
+
`WINDOWSWEEP_ASCII` are honoured; `WINDOWSWEEP_SHELL=pwsh` selects PowerShell 7 through the Node launcher.
|
|
109
|
+
|
|
110
|
+
The report file is schema-versioned JSON (`schema_version: 1`) with five top-level blocks: `credits`, `meta`
|
|
111
|
+
(times, host, OS, PowerShell version, mode, the three flags, the idle windows, the launcher), `disk`
|
|
112
|
+
(`before` and `after` snapshots per fixed drive), `steps` (one row per section with `status` and
|
|
113
|
+
`freed_bytes`), and `totals`. `--export md|html|both [N|latest|all]` renders any report to Markdown or a
|
|
114
|
+
self-contained HTML page without extra tools.
|
|
115
|
+
|
|
116
|
+
## Elevation
|
|
117
|
+
|
|
118
|
+
Sections 12-16 and 20 need Administrator rights. Without them the runner skips each with the exact command
|
|
119
|
+
to run. `--elevate` relaunches the whole run through a UAC prompt: **a new elevated window opens with its own
|
|
120
|
+
log and report**, and the original process waits and returns the child's exit code. That prompt cannot be
|
|
121
|
+
answered by a script, so `--elevate` does not belong in an unattended context.
|
|
122
|
+
|
|
123
|
+
## Guarantees
|
|
124
|
+
|
|
125
|
+
- **No network calls of its own.** The self-test greps the source for HTTP and socket calls and fails the run
|
|
126
|
+
if it finds one. There is no telemetry and no update check.
|
|
127
|
+
- **Every deletion passes one chokepoint** with a declared root, and is refused if it falls outside it.
|
|
128
|
+
- **Protected paths are refused regardless of flags**: drive roots, Windows, Program Files, the profile root,
|
|
129
|
+
personal folders, credentials, toolchains, browser and editor state. No flag bypasses this.
|
|
130
|
+
- **Junctions and symlinks are removed as links, never followed.**
|
|
131
|
+
- **`--dry-run` and `--scan` write nothing** but the log and the report.
|
|
132
|
+
- **Section numbers are a public contract.** 0-25 today; a section may be retired as a no-op, and a number is
|
|
133
|
+
never reused. Read `--list --json` rather than hard-coding the set.
|
|
134
|
+
- **Zero runtime dependencies.** The package is PowerShell plus a Node launcher that uses only built-in
|
|
135
|
+
modules.
|
|
136
|
+
|
|
137
|
+
## Do not
|
|
138
|
+
|
|
139
|
+
- Do not run `npx windowsweep` from a directory that contains a checkout of this repository: npx resolves the
|
|
140
|
+
local package and the command fails. Run it from anywhere else.
|
|
141
|
+
- Do not schedule `npx windowsweep` as a task action. The npx cache is evicted and the task breaks; install
|
|
142
|
+
globally and use `--install-task`, which refuses under npx for this reason.
|
|
143
|
+
- Do not pipe `y` into the tool to force a personal section. Sections 17, 18, 19 and 23 are interactive by
|
|
144
|
+
design and a piped answer selects nothing - pass `--select` or `--select-file` instead, which is the
|
|
145
|
+
supported way and names exactly what goes.
|
|
146
|
+
- Do not treat a green `--dry-run` as proof that a real run frees the same amount: an app that is open at run
|
|
147
|
+
time keeps its caches.
|
|
148
|
+
|
|
149
|
+
## Driving the interactive sections (1.1.0+)
|
|
150
|
+
|
|
151
|
+
Sections 17, 18, 19 and 23 refuse to run unattended, because they delete things a person should choose. Two
|
|
152
|
+
flags supply that choice in advance, and either one lifts the refusal:
|
|
153
|
+
|
|
154
|
+
```powershell
|
|
155
|
+
# 1. list what the section would offer - deletes nothing
|
|
156
|
+
npx windowsweep --only 17 --dry-run --json --no-color
|
|
157
|
+
# -> read .candidates[] from the single stdout line:
|
|
158
|
+
# { "section": 17, "index": 1, "path": "...", "bytes": 0, "idle_days": 400, "project": "..." }
|
|
159
|
+
|
|
160
|
+
# 2. act on exactly the ones you chose
|
|
161
|
+
npx windowsweep --only 17 --select 1,3 # by index, one list per prompt, in prompt order
|
|
162
|
+
npx windowsweep --only 17 --select-file picks.txt # by full path, one per line, UTF-8, # comments allowed
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Rules a caller can rely on:
|
|
166
|
+
|
|
167
|
+
- **`--yes` alone still selects nothing.** It never has. Only `--select` / `--select-file` do.
|
|
168
|
+
- The scripted selection also answers that section's final confirmation, so the run does not stall.
|
|
169
|
+
- `--select` is repeatable and consumed one list per prompt; `--select-file` is offered to every prompt, so
|
|
170
|
+
a line that matches nothing in a given section is reported once and skipped.
|
|
171
|
+
- Neither flag reaches a path the deletion chokepoint would otherwise refuse.
|
|
172
|
+
|
|
173
|
+
## Reading the catalogue and following progress
|
|
174
|
+
|
|
175
|
+
```powershell
|
|
176
|
+
npx windowsweep --list --json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
One stdout line: `sections[]` (`id`, `key`, `title`, `tier`, `admin`, `batch`, `dev`), `safe_batch`,
|
|
180
|
+
`safe_batch_admin`, `profiles`, `walkthrough`, `walkthrough_admin`. Read it instead of hard-coding section
|
|
181
|
+
numbers - they are frozen, but the set grows.
|
|
182
|
+
|
|
183
|
+
In `--json` mode every section brackets itself on **stderr**:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
##windowsweep section=7 event=start
|
|
187
|
+
##windowsweep section=7 event=end status=ran freed_bytes=4096
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`status` is one of `ran`, `dry-run`, `skipped`, `refused`, `failed`. Scan mode also fills `targets[]`
|
|
191
|
+
(`section`, `label`, `path`, `bytes`) in the final JSON line. `candidates` and `targets` are always present,
|
|
192
|
+
empty when nothing was collected.
|
|
193
|
+
|
|
194
|
+
## The read-only audits
|
|
195
|
+
|
|
196
|
+
`--profile audit` runs sections 0, 21, 22, 24 and 25 and deletes nothing in any mode: the health report, disk
|
|
197
|
+
usage, globally installed packages, installed programs not modified for `--days`+ days, and startup items.
|
|
198
|
+
Sections 22, 24 and 25 print removal commands but never execute one, and section 25 never changes a startup
|
|
199
|
+
entry. Section 24 reports "not modified", not "not used" - Windows keeps no reliable last-launched record.
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,103 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [1.1.0] - 2026-09-04
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Section 22 - global packages audit.** Lists what npm, pnpm, yarn, bun and deno installed globally with
|
|
17
|
+
size and idle days, flags the ones no recent project references, and prints the exact uninstall command.
|
|
18
|
+
It never uninstalls anything and declares no deletable target: several of those roots are protected paths.
|
|
19
|
+
- **Section 23 - orphaned application data.** Top-level folders under `%APPDATA%` and `%LOCALAPPDATA%` that
|
|
20
|
+
no installed program, Store package or running process claims and that nothing has touched for `--days`+
|
|
21
|
+
days. Interactive, Recycle Bin, and it **fails closed** - an unreadable uninstall registry produces zero
|
|
22
|
+
candidates rather than treating everything as orphaned. Every vendor folder another section already cleans
|
|
23
|
+
is excluded automatically, derived from the declared target list so the exclusions cannot drift.
|
|
24
|
+
- **Section 24 - installed programs not modified for N+ days.** Report only, largest first, with
|
|
25
|
+
`winget uninstall --id` when one `winget list` call resolves the program and its own `UninstallString`
|
|
26
|
+
otherwise; Store apps listed separately. It never runs an uninstaller.
|
|
27
|
+
- **Section 25 - startup items audit.** Run and RunOnce keys, both Startup folders, logon-triggered scheduled
|
|
28
|
+
tasks and `Win32_StartupCommand` in one table with each item's enabled state. It changes nothing.
|
|
29
|
+
- **`--select L` and `--select-file P`.** Answer an interactive section's selection in advance, by index or
|
|
30
|
+
by full path. Either flag lets sections 17, 18, 19 and 23 run unattended - a person did choose - and the
|
|
31
|
+
selection answers that section's final confirmation. `--yes` alone still selects nothing.
|
|
32
|
+
- **`--notify`.** A Windows notification when a run ends: a real toast on Windows PowerShell 5.1, a tray
|
|
33
|
+
balloon on PowerShell 7. It never changes the exit code and never writes to stdout. `--install-task` adds
|
|
34
|
+
it to the weekly task, so the Sunday run reports itself.
|
|
35
|
+
- **`--json` additions.** `candidates[]` (what an interactive section offered) and `targets[]` (what scan
|
|
36
|
+
mode measured), both always present so a caller can rely on the shape, plus per-section progress lines on
|
|
37
|
+
stderr: `##windowsweep section=NN event=start|end status=<status> freed_bytes=<n>`.
|
|
38
|
+
- **`--list --json`** prints the section catalogue - sections, tiers, batch policy, the safe batch, the
|
|
39
|
+
profiles and the walkthrough order - so a front end reads it instead of hard-coding it.
|
|
40
|
+
- Section 1 gained the Hugging Face model cache (`~\.cache\huggingface\hub`, pruned by the idle gate,
|
|
41
|
+
developer-gated). Section 9 lists `wsreset.exe` as the Microsoft Store cache lever and offers it as a next
|
|
42
|
+
step rather than running it: `wsreset` has no silent mode and always opens the Store.
|
|
43
|
+
- Section 17 recognises `.nx`, `.mypy_cache`, `.ruff_cache`, `.tox`, `.eggs`, `.output` and `.serverless`,
|
|
44
|
+
and treats `.cache` as an artefact only when a Gatsby or Parcel config sits beside it. `.venv`, `venv` and
|
|
45
|
+
`.terraform` are deliberately still excluded.
|
|
46
|
+
- `docs/sections.md` records the candidate targets that are researched but **not** shipped because the
|
|
47
|
+
software is not installed on the build machine, and why `C:\Intel` was inspected and rejected.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `Get-OrphanExclusions` returned its exclusion set as an unrolled array, whose `.Contains` is
|
|
52
|
+
case-sensitive, so a folder named `slack` would have slipped past the `Slack` entry. Found by a new check
|
|
53
|
+
before the section shipped.
|
|
54
|
+
- Section 24's idle measurement used the newest of write, access and creation time. Last-access is live on
|
|
55
|
+
many systems, so every install folder read as touched today and the section could never report anything;
|
|
56
|
+
it now reads last-write only, which is what "not modified" means.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- The target table is collected from the section catalogue instead of a literal `0..21` range, so a new
|
|
61
|
+
section is reachable by `--scan`, `--list-targets` and the safety checks the moment it is declared. The
|
|
62
|
+
menu's prompt range is derived the same way.
|
|
63
|
+
- Profile `audit` is now `0, 21, 22, 24, 25`. The three new report sections are read-only and safe, but stay
|
|
64
|
+
out of `--all` so a cleanup run remains a cleanup run.
|
|
65
|
+
- Self-test: 27 more checks (151 total) cover the catalogue, section 23's fail-closed gate and derived
|
|
66
|
+
exclusions, section 22 declaring nothing deletable, the scripted-selection flags, the `--json` and
|
|
67
|
+
catalogue shapes, the progress-line format, the global-package verdict, the startup-state rule and the
|
|
68
|
+
artefact list. Each was proved red against a planted defect before it was kept.
|
|
69
|
+
- Self-test: ten more checks cover the argument parser, section lists, docker size text, the layout-guard leaf
|
|
70
|
+
test, the `--json` shape, superseded versions, the Chromium layout, workspace storage, stale artefacts and
|
|
71
|
+
the report exporters. `Test-KnownCacheLeaf` and `Get-JsonSummary` were split out of `Invoke-TargetList` and
|
|
72
|
+
`Write-JsonSummary` so the logic can be exercised directly; no behaviour changed.
|
|
73
|
+
- `AI-INTEGRATION-GUIDE.md` ships in the npm package and is mirrored on the documentation site: the contract
|
|
74
|
+
an agent or a script relies on - the safe command sequence, what `--yes` never covers, exit codes, the
|
|
75
|
+
`--json` line, output paths and the guarantees.
|
|
76
|
+
- A documentation site at `windowsweep-docs.aoneahsan.com` (Docusaurus on GitHub Pages), mirroring `docs/`.
|
|
77
|
+
|
|
78
|
+
## [1.0.1] - 2026-09-03
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- **Sections 17, 18 and 19 no longer pre-select every item under `--yes`.** In the walkthrough and the menu
|
|
83
|
+
with auto-yes on, section 17 could remove every listed build artefact without a person choosing one, and
|
|
84
|
+
18/19 pre-selected everything before their final question. The selection prompt now appears even with
|
|
85
|
+
`--yes`, defaults to none, and section 17's final confirmation is never auto-answered. Batch mode already
|
|
86
|
+
refused these sections and is unchanged. (RW-002)
|
|
87
|
+
- Section 19's title no longer names Desktop; only Downloads is scanned. (RW-003)
|
|
88
|
+
- Sections 18 and 19 report the tier `recycle` instead of `permanent` in `--list`. (RW-004)
|
|
89
|
+
- `--purge-all` from a console asks you to type `purge` once per run, as documented; `--yes` remains the
|
|
90
|
+
confirmation in batch runs. (RW-005)
|
|
91
|
+
- A running editor's VSIX download cache is cleared as documented: it is its own target without the
|
|
92
|
+
running-editor guard. (RW-006)
|
|
93
|
+
- `--install-task` and `--install-alias` refuse to run under `npx` (exit 3) and print the global-install
|
|
94
|
+
steps; the npx cache is evicted and the task or alias would break later. (RW-007)
|
|
95
|
+
- The engine exits 130 when a run is interrupted before it finished; previously only the Node launcher
|
|
96
|
+
did. (RW-008)
|
|
97
|
+
- `--uninstall-data` always asks; `--yes` no longer removes your history unattended. (RW-010)
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
|
|
101
|
+
- Internal rename `Write-Log` -> `Write-LogLine` (18 call sites) so PSScriptAnalyzer's
|
|
102
|
+
`PSAvoidOverwritingBuiltInCmdlets` passes under PowerShell 7 in CI. No behaviour change.
|
|
103
|
+
- Self-test: four new checks prove the `--yes` asymmetry at the helper and at the section 17/18/19 call sites,
|
|
104
|
+
and that every picker call in `modules/` carries `-NoAutoYes` (114 checks).
|
|
105
|
+
- Keywords trimmed to twelve (`temp-files` dropped). (RW-011)
|
|
106
|
+
|
|
10
107
|
## [1.0.0] - 2026-09-03
|
|
11
108
|
|
|
12
109
|
First release. The Windows member of the cleanup family beside
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://nodejs.org)
|
|
14
14
|
[](https://github.com/aoneahsan/windowsweep#platform-support)
|
|
15
15
|
|
|
16
|
-
[Docs](https://github.com/aoneahsan/windowsweep/blob/main/docs/README.md) · [npm](https://www.npmjs.com/package/windowsweep) · [GitHub](https://github.com/aoneahsan/windowsweep) · [Changelog](https://github.com/aoneahsan/windowsweep/blob/main/CHANGELOG.md) · [Contributing](https://github.com/aoneahsan/windowsweep/blob/main/CONTRIBUTING.md) · [Support](https://github.com/aoneahsan/windowsweep/issues)
|
|
16
|
+
[Docs](https://github.com/aoneahsan/windowsweep/blob/main/docs/README.md) · [AI Guide](https://github.com/aoneahsan/windowsweep/blob/main/AI-INTEGRATION-GUIDE.md) · [npm](https://www.npmjs.com/package/windowsweep) · [GitHub](https://github.com/aoneahsan/windowsweep) · [Changelog](https://github.com/aoneahsan/windowsweep/blob/main/CHANGELOG.md) · [Contributing](https://github.com/aoneahsan/windowsweep/blob/main/CONTRIBUTING.md) · [Support](https://github.com/aoneahsan/windowsweep/issues)
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -34,12 +34,12 @@ It is the Windows member of a family with [linux-cleanup](https://github.com/aon
|
|
|
34
34
|
|
|
35
35
|
| | |
|
|
36
36
|
|---|---|
|
|
37
|
-
| **Version** | `1.
|
|
37
|
+
| **Version** | `1.1.0` |
|
|
38
38
|
| **License** | MIT |
|
|
39
39
|
| **Node** | `>=14` (launcher only) |
|
|
40
40
|
| **Runtime** | Windows PowerShell 5.1 (built in) or PowerShell 7 |
|
|
41
41
|
| **Platforms** | Windows 10 (1809+) and Windows 11 |
|
|
42
|
-
| **Install size** | ~
|
|
42
|
+
| **Install size** | ~109 kB packed · ~366 kB unpacked · 44 files · no dependencies |
|
|
43
43
|
| **Undo** | Recycle Bin for personal files; none for caches (they regenerate) |
|
|
44
44
|
| **Status** | Stable · actively maintained |
|
|
45
45
|
|
|
@@ -102,8 +102,9 @@ security scanner or a registry cleaner. It reclaims disk space, nothing else.
|
|
|
102
102
|
|
|
103
103
|
- **Developer mode** - one question on the first run. Yes keeps package, build and test-runner caches used in
|
|
104
104
|
the last 100 days and the newest version of every versioned tool; no clears them completely.
|
|
105
|
-
- **
|
|
106
|
-
|
|
105
|
+
- **26 numbered sections** - from package-manager caches to Windows Update leftovers, plus four read-only
|
|
106
|
+
audits (global packages, orphaned app data, idle programs, startup items), each naming its paths before it
|
|
107
|
+
acts. Numbers are a public contract.
|
|
107
108
|
- **One deletion chokepoint** - refuses drive roots, Windows, Program Files, your profile root, personal
|
|
108
109
|
folders, credentials, toolchains and browser or editor state; asserts every deletion sits inside its declared
|
|
109
110
|
target; never follows a junction or symlink; handles paths beyond 260 characters; skips files another program
|
|
@@ -119,8 +120,8 @@ security scanner or a registry cleaner. It reclaims disk space, nothing else.
|
|
|
119
120
|
- **Running-app guard** - an open browser, editor or app keeps its caches; the tool tells you which to close.
|
|
120
121
|
- **Session reports** - schema-versioned JSON, exportable to Markdown or a self-contained HTML page, plus
|
|
121
122
|
`--json` for scripts.
|
|
122
|
-
- **Self-test** -
|
|
123
|
-
fixture before you trust it.
|
|
123
|
+
- **Self-test** - 151 checks prove the guards on your machine with a real junction, a 400-character path and
|
|
124
|
+
a dry-run fixture before you trust it.
|
|
124
125
|
- **Offline by design** - zero network calls, no telemetry, no update check. Crash bundles stay on disk.
|
|
125
126
|
|
|
126
127
|
<a id="platform-support"></a>
|
|
@@ -128,9 +129,9 @@ security scanner or a registry cleaner. It reclaims disk space, nothing else.
|
|
|
128
129
|
|
|
129
130
|
| Platform | Supported | Notes |
|
|
130
131
|
|---|---|---|
|
|
131
|
-
| Windows 11 | ✅ |
|
|
132
|
-
| Windows 10 (1809 and later) | ✅ | The primary development target |
|
|
133
|
-
| Windows Server 2019+ | ⚠️ | Uses nothing newer than 1809, but
|
|
132
|
+
| Windows 11 | ✅ | Same engine and PowerShell hosts; a real run on Windows 11 is on the verification list ([roadmap](https://github.com/aoneahsan/windowsweep/blob/main/remaining-work-summary.md)) |
|
|
133
|
+
| Windows 10 (1809 and later) | ✅ | The primary development target; every real run so far |
|
|
134
|
+
| Windows Server 2019+ | ⚠️ | Uses nothing newer than 1809; CI runs the self-test and a dry-run on Windows Server (`windows-latest`) on every push, but no real cleanup has been run on Server |
|
|
134
135
|
| Linux | ❌ | `os: ["win32"]` makes npm refuse to install; use [linux-cleanup](https://github.com/aoneahsan/linux-cleanup) |
|
|
135
136
|
| macOS | ❌ | Use [macleanup](https://github.com/aoneahsan/macleanup) |
|
|
136
137
|
|
|
@@ -195,8 +196,8 @@ section with `a` run / `s` skip / `q` quit and a running total.
|
|
|
195
196
|
### The sections
|
|
196
197
|
|
|
197
198
|
**Tier** says what happens to the data: *report* deletes nothing, *rebuilds* comes back on its own, *slow*
|
|
198
|
-
comes back but costs minutes, *Recycle Bin* is recoverable until you empty it
|
|
199
|
-
changes a setting.
|
|
199
|
+
comes back but costs minutes, *Recycle Bin* is recoverable until you empty it (`--list` shows this tier as
|
|
200
|
+
`recycle`), *permanent* is not, *config* changes a setting.
|
|
200
201
|
|
|
201
202
|
| # | Section | Tier | Admin | Batch |
|
|
202
203
|
|---|---|---|:-:|---|
|
|
@@ -222,10 +223,16 @@ changes a setting.
|
|
|
222
223
|
| 19 | Large stale personal files in Downloads | Recycle Bin | - | interactive |
|
|
223
224
|
| 20 | Docker Desktop / WSL disk-image compaction | config | yes | deep |
|
|
224
225
|
| 21 | Disk usage report | report | - | safe |
|
|
226
|
+
| 22 | Global packages audit (npm, pnpm, yarn, bun, deno) - never uninstalls | report | - | safe · audit only |
|
|
227
|
+
| 23 | Orphaned application data under AppData | Recycle Bin | - | interactive |
|
|
228
|
+
| 24 | Installed programs not modified for N+ days - never uninstalls | report | - | safe · audit only |
|
|
229
|
+
| 25 | Startup items audit - never changes them | report | - | safe · audit only |
|
|
225
230
|
|
|
226
231
|
*Safe* sections run in `--all`; *opt-in* ones run when named in `--only` or a profile; *deep* ones also need
|
|
227
|
-
`--i-understand-deep`; *interactive* ones never run unattended
|
|
228
|
-
|
|
232
|
+
`--i-understand-deep`; *interactive* ones never run unattended unless you pass a selection. The three
|
|
233
|
+
*audit only* sections are read-only and safe, but stay out of `--all` so a cleanup run is a cleanup run -
|
|
234
|
+
`--profile audit` is where they live. Every section is documented in
|
|
235
|
+
[Sections 0-25](https://github.com/aoneahsan/windowsweep/blob/main/docs/sections.md).
|
|
229
236
|
|
|
230
237
|
### Clean interactively
|
|
231
238
|
|
|
@@ -303,6 +310,8 @@ windowsweep [mode] [options]
|
|
|
303
310
|
| `--scan-roots "P1;P2"`, `--exclude-path P` | auto | Section 17 roots and exclusions |
|
|
304
311
|
| `--hiberfil off\|reduced\|keep` | ask | What section 15 does |
|
|
305
312
|
| `--permanent` | off | Sections 18/19 delete instead of using the Recycle Bin |
|
|
313
|
+
| `--select L`, `--select-file P` | off | Answer an interactive section's selection in advance, by index or by path - the one way sections 17/18/19/23 run unattended |
|
|
314
|
+
| `--notify` | off | A Windows notification when the run ends |
|
|
306
315
|
| `--json`, `--quiet`, `--no-color`, `--ascii`, `--no-report`, `--cleanup-logs` | off | Output and record controls |
|
|
307
316
|
|
|
308
317
|
Every flag, exit code and environment variable:
|
|
@@ -403,16 +412,20 @@ More: [FAQ](https://github.com/aoneahsan/windowsweep/blob/main/docs/faq.md).
|
|
|
403
412
|
| [Quick start](https://github.com/aoneahsan/windowsweep/blob/main/docs/quick-start.md) | running your first cleanup |
|
|
404
413
|
| [Safety model](https://github.com/aoneahsan/windowsweep/blob/main/docs/safety-model.md) | you want every guarantee spelled out before deleting anything |
|
|
405
414
|
| [Developer mode](https://github.com/aoneahsan/windowsweep/blob/main/docs/developer-mode.md) | you want to know what the first question changes |
|
|
406
|
-
| [Sections 0-
|
|
415
|
+
| [Sections 0-25](https://github.com/aoneahsan/windowsweep/blob/main/docs/sections.md) | you want to know precisely what one section touches |
|
|
407
416
|
| [CLI reference](https://github.com/aoneahsan/windowsweep/blob/main/docs/cli-reference.md) | you need an exact flag, exit code or variable |
|
|
408
417
|
| [Admin sections and elevation](https://github.com/aoneahsan/windowsweep/blob/main/docs/admin-and-elevation.md) | before running the system profile or touching the hibernation file |
|
|
409
418
|
| [Reports and logs](https://github.com/aoneahsan/windowsweep/blob/main/docs/reports-and-logs.md) | parsing the JSON or finding a log |
|
|
419
|
+
| [AI integration guide](https://github.com/aoneahsan/windowsweep/blob/main/AI-INTEGRATION-GUIDE.md) | an agent or a script runs it for you |
|
|
410
420
|
| [Troubleshooting](https://github.com/aoneahsan/windowsweep/blob/main/docs/troubleshooting.md) | something failed |
|
|
421
|
+
| [Roadmap and remaining work](https://github.com/aoneahsan/windowsweep/blob/main/remaining-work-summary.md) | you want to know what is verified, what is planned (1.1, the docs site) and what is still open |
|
|
411
422
|
|
|
412
423
|
<a id="changelog"></a>
|
|
413
424
|
## 🔄 Changelog [#](#changelog)
|
|
414
425
|
|
|
415
|
-
Latest release: **`1.
|
|
426
|
+
Latest release: **`1.1.0`** - four new sections (global packages, orphaned app data, idle programs, startup
|
|
427
|
+
items), `--select` / `--select-file` so a script or a GUI can drive the interactive sections, `--notify`, and
|
|
428
|
+
`candidates[]`, `targets[]`, progress lines and `--list --json` for machine callers.
|
|
416
429
|
Full history: [CHANGELOG.md](https://github.com/aoneahsan/windowsweep/blob/main/CHANGELOG.md).
|
|
417
430
|
|
|
418
431
|
<a id="contributing"></a>
|
|
@@ -478,5 +491,5 @@ this tool deletes files, so review what it proposes before confirming.
|
|
|
478
491
|
<a id="keywords"></a>
|
|
479
492
|
## 🏷️ Keywords [#](#keywords)
|
|
480
493
|
|
|
481
|
-
*windows · cleanup · disk-cleanup · cache-cleanup · disk-space ·
|
|
494
|
+
*windows · cleanup · disk-cleanup · cache-cleanup · disk-space · node-modules · npm-cache ·
|
|
482
495
|
yarn-cache · developer-tools · powershell · cli · dry-run*
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.1.0
|
package/lib/actions.ps1
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
$Script:WS_CHROMIUM_PROFILE_CACHES = @('Cache', 'Code Cache', 'GPUCache', 'DawnCache', 'DawnGraphiteCache', 'DawnWebGPUCache', 'GrShaderCache', 'ShaderCache', 'Service Worker\ScriptCache')
|
|
5
5
|
$Script:WS_CHROMIUM_ROOT_CACHES = @('ShaderCache', 'GrShaderCache', 'GraphiteDawnCache', 'Crashpad\completed', 'Crashpad\reports', 'SwReporter')
|
|
6
6
|
$Script:WS_ELECTRON_CACHES = @('Cache', 'Code Cache', 'GPUCache', 'DawnCache', 'DawnGraphiteCache', 'DawnWebGPUCache', 'GrShaderCache', 'ShaderCache', 'Service Worker\CacheStorage', 'Service Worker\ScriptCache', 'Crashpad\completed', 'Crashpad\reports')
|
|
7
|
-
$Script:WS_EDITOR_CACHES = @('Cache', 'CachedData', 'CachedProfilesData', '
|
|
7
|
+
$Script:WS_EDITOR_CACHES = @('Cache', 'CachedData', 'CachedProfilesData', 'Code Cache', 'GPUCache', 'DawnCache', 'DawnGraphiteCache', 'DawnWebGPUCache', 'Service Worker\CacheStorage', 'Service Worker\ScriptCache', 'Crashpad\completed', 'Crashpad\reports')
|
|
8
8
|
$Script:WS_FIREFOX_CACHES = @('cache2', 'startupCache', 'thumbnails', 'jumpListCache', 'OfflineCache', 'shader-cache')
|
|
9
9
|
|
|
10
10
|
function Get-ChromiumProfileDirs {
|
|
@@ -99,6 +99,13 @@ function Confirm-Section {
|
|
|
99
99
|
return (Confirm-Ui -Prompt $Prompt -Default $Default)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function Test-KnownCacheLeaf {
|
|
103
|
+
<# .SYNOPSIS Second guard for the layout kinds: only a folder whose leaf name is a declared cache name may be cleared. #>
|
|
104
|
+
param([string] $Path)
|
|
105
|
+
$known = @($Script:WS_CHROMIUM_PROFILE_CACHES + $Script:WS_CHROMIUM_ROOT_CACHES + $Script:WS_ELECTRON_CACHES + $Script:WS_EDITOR_CACHES + $Script:WS_FIREFOX_CACHES | ForEach-Object { [IO.Path]::GetFileName($_) })
|
|
106
|
+
return ($known -contains [IO.Path]::GetFileName($Path))
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
function Invoke-TargetList {
|
|
103
110
|
<# .SYNOPSIS Run every target with its mode under the dry-run, developer, purge and running-app policies. Returns bytes freed/estimated. #>
|
|
104
111
|
param([pscustomobject[]] $Targets)
|
|
@@ -117,11 +124,7 @@ function Invoke-TargetList {
|
|
|
117
124
|
if ($t.Kind -in 'chromium', 'firefox', 'electron', 'editor') {
|
|
118
125
|
$mode = 'clear'
|
|
119
126
|
# Second guard: a layout target may only ever clear a known cache folder, never a profile or app root.
|
|
120
|
-
$
|
|
121
|
-
$paths = @($paths | Where-Object {
|
|
122
|
-
$leaf = [IO.Path]::GetFileName($_)
|
|
123
|
-
if ($known -contains $leaf) { $true } else { Write-Err "REFUSE (not a known cache folder for a $($t.Kind) layout): $_"; $false }
|
|
124
|
-
})
|
|
127
|
+
$paths = @($paths | Where-Object { if (Test-KnownCacheLeaf $_) { $true } else { Write-Err "REFUSE (not a known cache folder for a $($t.Kind) layout): $_"; $false } })
|
|
125
128
|
}
|
|
126
129
|
if ($ws.PurgeAll -and $mode -in 'prune', 'units') { $mode = 'clear' }
|
|
127
130
|
if ($t.Dev -and $ws.Developer -eq $false -and $mode -in 'prune', 'units') { $mode = 'clear' }
|
package/lib/config.ps1
CHANGED
|
@@ -88,3 +88,12 @@ function Resolve-DeveloperMode {
|
|
|
88
88
|
$ws.DeveloperSource = 'default'
|
|
89
89
|
Write-Note 'developer question not asked (non-interactive run) - defaulting to developer mode ON, the conservative choice; pass --not-developer to override'
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
function Confirm-PurgeAllOnce {
|
|
93
|
+
<# .SYNOPSIS --purge-all from a console asks for a typed word once per run; --yes is the confirmation in batch runs. #>
|
|
94
|
+
$ws = $Script:WS
|
|
95
|
+
if (-not $ws.PurgeAll -or $ws.DryRun -or $ws.Yes) { return }
|
|
96
|
+
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 }
|
|
97
|
+
$ws.PurgeAll = $false
|
|
98
|
+
Write-Info 'purge-all declined - this run prunes by the idle window instead'
|
|
99
|
+
}
|
package/lib/constants.ps1
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Single source of truth for the version is package.json: the Node launcher exports it as
|
|
5
5
|
# WINDOWSWEEP_VERSION. A direct checkout falls back to this literal; the version:check script
|
|
6
6
|
# asserts the two never drift.
|
|
7
|
-
$Script:WS_VERSION_FALLBACK = '1.
|
|
7
|
+
$Script:WS_VERSION_FALLBACK = '1.1.0'
|
|
8
8
|
|
|
9
9
|
$Script:WS_NAME = 'windowsweep'
|
|
10
10
|
$Script:WS_TAGLINE = 'Safe-by-default Windows cleanup CLI - developer-aware, dry-run first, zero install via npx.'
|
|
@@ -29,7 +29,7 @@ $Script:WS_EXIT_INTERRUPT = 130
|
|
|
29
29
|
|
|
30
30
|
# Section catalogue. Numbering is a public contract frozen at 1.0.0: a section may be retired
|
|
31
31
|
# (kept as a no-op that says so) but a number is never reused for something else.
|
|
32
|
-
# Tier: report | rebuilds | slow | permanent | config
|
|
32
|
+
# Tier: report | rebuilds | slow | recycle | permanent | config
|
|
33
33
|
# Batch: safe (in --all) | optin (--only/--profile with --yes) | deep (needs --i-understand-deep)
|
|
34
34
|
# | interactive (never batch; dry-run/report allowed)
|
|
35
35
|
$Script:WS_SECTIONS = @(
|
|
@@ -51,10 +51,14 @@ $Script:WS_SECTIONS = @(
|
|
|
51
51
|
@{ Id = 15; Key = 'hiberfil'; Title = 'Hibernation file (off / reduced)'; Tier = 'config'; Admin = $true; Batch = 'deep'; Dev = $false; Fn = 'Invoke-Section15' }
|
|
52
52
|
@{ Id = 16; Key = 'eventlogs'; Title = 'Clear Windows Event Logs - PERMANENT'; Tier = 'permanent'; Admin = $true; Batch = 'deep'; Dev = $false; Fn = 'Invoke-Section16' }
|
|
53
53
|
@{ 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' }
|
|
54
|
-
@{ Id = 18; Key = 'partials'; Title = 'Partial / orphan downloads -> Recycle Bin'; Tier = '
|
|
55
|
-
@{ Id = 19; Key = 'large'; Title = 'Large stale personal files (Downloads
|
|
54
|
+
@{ Id = 18; Key = 'partials'; Title = 'Partial / orphan downloads -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section18' }
|
|
55
|
+
@{ Id = 19; Key = 'large'; Title = 'Large stale personal files (Downloads) -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section19' }
|
|
56
56
|
@{ 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' }
|
|
57
57
|
@{ Id = 21; Key = 'diskusage'; Title = 'Disk usage report (largest entries, drives, disk images)'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $false; Fn = 'Invoke-Section21' }
|
|
58
|
+
@{ Id = 22; Key = 'globals'; Title = 'Globally installed packages audit (npm, pnpm, yarn, bun, deno) - report only'; Tier = 'report'; Admin = $false; Batch = 'safe'; Dev = $true; Fn = 'Invoke-Section22' }
|
|
59
|
+
@{ Id = 23; Key = 'orphaned'; Title = 'Orphaned application data under AppData -> Recycle Bin'; Tier = 'recycle'; Admin = $false; Batch = 'interactive'; Dev = $false; Fn = 'Invoke-Section23' }
|
|
60
|
+
@{ 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' }
|
|
61
|
+
@{ 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' }
|
|
58
62
|
)
|
|
59
63
|
|
|
60
64
|
# The safe batch run by --all. Admin sections 12 and 13 join it only when already elevated.
|
|
@@ -67,11 +71,11 @@ $Script:WS_PROFILES = @{
|
|
|
67
71
|
'cache-only' = @(1, 2, 3, 6, 7, 8, 9)
|
|
68
72
|
'system' = @(12, 13, 14)
|
|
69
73
|
'deep' = @(0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 13, 14, 21)
|
|
70
|
-
'audit' = @(0, 21)
|
|
74
|
+
'audit' = @(0, 21, 22, 24, 25)
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
# Walkthrough order (interactive default mode). Admin sections are inserted when elevated.
|
|
74
|
-
$Script:WS_WALKTHROUGH = @(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 18, 19)
|
|
78
|
+
$Script:WS_WALKTHROUGH = @(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 18, 19, 23)
|
|
75
79
|
$Script:WS_WALKTHROUGH_ADMIN = @(12, 13, 14)
|
|
76
80
|
|
|
77
81
|
function Get-Section {
|
package/lib/log.ps1
CHANGED
|
@@ -29,7 +29,7 @@ function Initialize-Log {
|
|
|
29
29
|
try { [IO.File]::WriteAllLines($ws.LogFile, $header) } catch { $ws.LogFile = $null }
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function Write-
|
|
32
|
+
function Write-LogLine {
|
|
33
33
|
<# .SYNOPSIS Append one timestamped line to the run log; never throws. #>
|
|
34
34
|
param([string] $Text)
|
|
35
35
|
$f = $Script:WS.LogFile
|
package/lib/safety.ps1
CHANGED
|
@@ -155,7 +155,7 @@ function Remove-TreeInternal {
|
|
|
155
155
|
try {
|
|
156
156
|
if ($isLink) {
|
|
157
157
|
if ($isDir) { [IO.Directory]::Delete($e.FullName, $false) } else { [IO.File]::Delete($e.FullName) }
|
|
158
|
-
Write-
|
|
158
|
+
Write-LogLine ("removed link (not followed): " + (Remove-LongPrefix $e.FullName))
|
|
159
159
|
} elseif ($isDir) {
|
|
160
160
|
if (-not (Remove-TreeInternal -Path $e.FullName -Result $Result)) { $allClear = $false }
|
|
161
161
|
} else {
|
|
@@ -206,24 +206,24 @@ function Remove-PathSafe {
|
|
|
206
206
|
if ($isLink) {
|
|
207
207
|
if ($isDir) { [IO.Directory]::Delete($lp, $false) } else { [IO.File]::Delete($lp) }
|
|
208
208
|
$r.Removed = $true
|
|
209
|
-
Write-
|
|
209
|
+
Write-LogLine "removed link (not followed): $p$tag"
|
|
210
210
|
} elseif ($isDir) {
|
|
211
211
|
$r.Removed = Remove-TreeInternal -Path $p -Result $r
|
|
212
212
|
$r.Bytes = [math]::Max([long]0, $bytes - $r.SkippedBytes)
|
|
213
213
|
if ($r.Skipped -gt 0) { Write-Note "$($r.Skipped) item(s) in use or inaccessible, left in place under $p" }
|
|
214
|
-
Write-
|
|
214
|
+
Write-LogLine ("removed dir: $p ($($r.Bytes) bytes, skipped $($r.Skipped))$tag")
|
|
215
215
|
} else {
|
|
216
216
|
Remove-ReadOnlyAttribute $lp
|
|
217
217
|
[IO.File]::Delete($lp)
|
|
218
218
|
$r.Removed = $true; $r.Bytes = $bytes; $r.Files = 1
|
|
219
|
-
Write-
|
|
219
|
+
Write-LogLine "removed file: $p ($bytes bytes)$tag"
|
|
220
220
|
}
|
|
221
221
|
Add-Freed $r.Bytes
|
|
222
222
|
} catch {
|
|
223
223
|
$r.Skipped++
|
|
224
224
|
$r.Reason = 'in use or access denied'
|
|
225
225
|
Write-Note "in use or inaccessible, skipped: $p"
|
|
226
|
-
Write-
|
|
226
|
+
Write-LogLine "skip (locked): $p - $($_.Exception.Message)"
|
|
227
227
|
}
|
|
228
228
|
return $r
|
|
229
229
|
}
|
|
@@ -272,7 +272,7 @@ function Remove-StaleFiles {
|
|
|
272
272
|
} catch { $null = $_ }
|
|
273
273
|
}
|
|
274
274
|
Add-Freed $out.Freed
|
|
275
|
-
Write-
|
|
275
|
+
Write-LogLine ("pruned $($out.Files) files ($($out.Freed) bytes) idle $Days+ days from $root; skipped $($out.Skipped)")
|
|
276
276
|
Write-Ok ("$Label - pruned {0} in {1} files idle {2}+ days ({3} skipped in use)" -f (Format-Bytes $out.Freed), $out.Files, $Days, $out.Skipped)
|
|
277
277
|
return $out
|
|
278
278
|
}
|
|
@@ -405,7 +405,7 @@ function Send-ToRecycleBin {
|
|
|
405
405
|
}
|
|
406
406
|
$r.Removed = $true; $r.Bytes = $bytes
|
|
407
407
|
Add-Freed $bytes
|
|
408
|
-
Write-
|
|
408
|
+
Write-LogLine "recycled: $p ($bytes bytes)"
|
|
409
409
|
Write-Ok ("moved to Recycle Bin: {0} ({1})" -f $p, (Format-Bytes $bytes))
|
|
410
410
|
} catch {
|
|
411
411
|
$r.Skipped++; $r.Reason = $_.Exception.Message
|
|
@@ -424,14 +424,14 @@ function Invoke-External {
|
|
|
424
424
|
Write-DryRun "would run: $display"
|
|
425
425
|
return [pscustomobject]@{ Ran = $false; ExitCode = 0; Output = @() }
|
|
426
426
|
}
|
|
427
|
-
Write-
|
|
427
|
+
Write-LogLine "run${tag}: $display"
|
|
428
428
|
$lines = @()
|
|
429
429
|
try {
|
|
430
430
|
$raw = & $FilePath @ArgumentList 2>&1
|
|
431
431
|
foreach ($l in $raw) {
|
|
432
432
|
$s = [string]$l
|
|
433
433
|
$lines += $s
|
|
434
|
-
if (-not $Quiet) { Write-Note $s } else { Write-
|
|
434
|
+
if (-not $Quiet) { Write-Note $s } else { Write-LogLine $s }
|
|
435
435
|
}
|
|
436
436
|
$code = $LASTEXITCODE
|
|
437
437
|
if ($null -eq $code) { $code = 0 }
|
|
@@ -439,7 +439,7 @@ function Invoke-External {
|
|
|
439
439
|
$lines += $_.Exception.Message
|
|
440
440
|
$code = 1
|
|
441
441
|
}
|
|
442
|
-
Write-
|
|
442
|
+
Write-LogLine "exit $code$tag : $display"
|
|
443
443
|
return [pscustomobject]@{ Ran = $true; ExitCode = $code; Output = $lines }
|
|
444
444
|
}
|
|
445
445
|
|