machine-health
Overview
This skill performs a weekly workstation health audit with a fail-safe posture: surface issues over silently fixing them. Findings always include reproduction commands so the human can rerun the check outside the skill. Remediations are narrow, logged, and only attempted when the OS-specific remediation-policy.md authorizes them. Severity is always trend-aware. A single reading is rarely load-bearing; history is consulted before finalizing severity.
The skill is stateless about scheduling; a separate routine (e.g., a Monday 08:00 scheduled task, or an ad-hoc /machine-health:audit invocation) calls into it.
Progressive disclosure by OS. The skill detects the host OS and loads only matching references and scripts. Windows is fully implemented. macOS and Linux are scaffolded as NOT_IMPLEMENTED stubs so they can be populated in future passes without restructuring.
Resolving output locations (do this first)
Two roots, resolved through the plugin's configuration seams:
| Root |
Holds |
Resolution |
Report root (-OutputBase) |
reports/, the human-facing dated reports |
${user_config.report_dir} when set to a non-empty path; if it is empty or still shows an unexpanded ${user_config.report_dir} token (option unset), default to $env:USERPROFILE\Documents\MachineHealth |
State root (-StateBase) |
state/ (history, latest snapshot, approvals), logs/, catalog overlay, custom checks, TODO.md proposals |
${CLAUDE_PLUGIN_DATA}, the per-plugin data directory that survives plugin updates, passed explicitly when the token expands. If it is unexpanded, do not fall through to the orchestrator's environment ladder: the harness sets CLAUDE_PLUGIN_DATA per plugin, and a subprocess inherits whichever plugin's value its parent process already carried, so a skill-invoked tool can inherit an unrelated installed plugin's value and write this plugin's state/, logs/, and catalog overlay into that plugin's directory. Pass -StateBase <report-root> explicitly instead. Colocating state with the reports is wrong-but-visible, where the inherited variable is wrong-and-silent. Report that the plugin-specific root could not be resolved. Never substitute a literal path: the directory under ~/.claude/plugins/data/ is named for the plugin's install identity, not the plugin, so a guessed path names a different directory and splits state from the overlay |
Pass both explicitly to the orchestrator (-OutputBase <report-root> -StateBase <state-root>). Never write generated state into the plugin's own install directory. A plugin update replaces it.
Other runtime parameters the caller passes (via slash-command arguments, scheduled task arguments, or environment variables; the orchestrator accepts them as parameters):
| Parameter |
Default |
Meaning |
RunMode |
weekly |
One of weekly, on-demand, first-run. first-run forces dry-run and seeds state. |
DryRun |
$false (on weekly/on-demand); $true (on first-run) |
Skip all remediations; still produce a full report. |
OS detection and routing
# Detect current OS (the skill requires PowerShell 7.4+; launch pwsh, not
# Windows PowerShell 5.1, which cannot run the checks)
if (PowerShell 7.4+) use $IsWindows / $IsMacOS / $IsLinux
else non-PowerShell shell: uname -s -> Darwin|Linux
# Load references (shared first, then OS-specific)
Read reference/shared/severity-rubric.md
Read reference/shared/output-schema.md
Read reference/shared/report-template.md
Read reference/shared/discovery-guide.md
Read reference/shared/remediation-philosophy.md
Read reference/shared/approvals.md
Read reference/shared/catalog-overlay.md
Read reference/<os>/*.md
# If the detected OS folder contains NOT_IMPLEMENTED.md, STOP.
# Produce an UNKNOWN-severity report explaining the gap, link to
# reference/shared/discovery-guide.md for porting guidance, and exit.
# Never attempt to execute Windows scripts on macOS/Linux.
Routing table:
| Detected OS |
Orchestrator |
Status |
| Windows |
scripts/windows/Invoke-MachineHealthCheck.ps1 |
Implemented |
| macOS |
scripts/macos/NOT_IMPLEMENTED.md |
Stub. Report UNKNOWN and stop |
| Linux |
scripts/linux/NOT_IMPLEMENTED.md |
Stub. Report UNKNOWN and stop |
High-level procedure
Verify preconditions. PowerShell 7.4+ (enforced by the orchestrator's #Requires; individual checks that need a still-newer cmdlet return UNKNOWN rather than aborting). The report and state roots are writable. Record elevation state via scripts/<os>/lib/Test-IsElevated.ps1. Never prompt for UAC.
Load and filter the catalog. The orchestrator reads the shipped catalog/checks.jsonc, merges the machine-local overlay at <StateBase>/catalog/checks.local.jsonc when present (see reference/shared/catalog-overlay.md), and keeps entries whose os list contains the current OS and where enabled: true and deprecated: false.
Load trend context. Read the tail of <StateBase>/state/history.jsonl (last 8 weeks) for each check. Pass the slice to each check script over stdin so checks can annotate deltas, but checks remain stateless themselves.
Invoke the OS orchestrator. Pass OutputBase, StateBase, and RunMode. The orchestrator dispatches checks under per-check 90s timeouts, collects JSON results, applies trend-aware severity adjustments, and, on non-dry runs, dispatches authorized remediations with before/after logging.
Receive the structured result. Run discovery per reference/shared/discovery-guide.md. Propose 1–3 OS-appropriate new checks. Straightforward read-only ones may be implemented as custom checks (script under <StateBase>/scripts/<os>/checks/, registered in the catalog overlay); anything needing new permissions or remediation lands in <StateBase>/TODO.md for human approval. Checks broadly useful to every consumer are best contributed to the plugin itself.
Render the markdown report from reference/shared/report-template.md into <OutputBase>/reports/health-<UTC-timestamp>.md (one file per run, so a same-day rerun does not overwrite the earlier report).
When the severity spread or trend deltas would read better visually, also generate a self-contained static HTML view of that report (color-coded CRIT/WARN/UNKNOWN, no remote fetch). The markdown .md report stays the durable record.
Update state. Write <StateBase>/state/latest.json. Append one compact line to <StateBase>/state/history.jsonl, the trend source of truth.
Verify and summarize. Confirm the report exists. Print CRIT/WARN counts + report path to session output. A clean run may still include UNKNOWN findings. Call those out too.
Guardrails
- Max total runtime: 15 minutes. Partial results mark missing checks
UNKNOWN with reason "timeout".
- Per-check timeout: 90 seconds.
- No interactive prompts. Ever. This runs unattended on a schedule; a stalled prompt would hang the weekly job.
- No retry loops on failure. One attempt per check, one attempt per remediation.
reference/shared/remediation-philosophy.md carries the one-attempt rationale for remediations; checks stay one-attempt for the same reason. A flaky check should read as flaky, not as eventually-fine.
- First-run dry mode. The first ever run (RunMode
first-run) forces DryRun = true. Seeds state, produces the first report, queues remediation approval in <StateBase>/TODO.md.
- Idempotency. Two runs back-to-back produce two valid reports and two history entries with no partial state.
- Egress allowlist. Microsoft Update endpoints, winget sources, and the CISA KEV feed are the only permitted outbound URLs. This keeps the skill's network footprint auditable and prevents an unvetted check or remediation from calling an arbitrary host. Every outbound URL is logged to
<StateBase>/logs/run-YYYY-MM-DD.log.
- No
Invoke-Expression on any data the skill did not author itself in this session. No "run whatever came back" patterns.
- Admin is not assumed. Any check requiring elevation self-checks and returns
UNKNOWN with needs_admin: true rather than prompting UAC.
- Defer remediations under user load. If the interactive session has had input in the last 60 seconds, log a note and run read-only checks only. Defer remediations to next run.
- Never write into the plugin install directory. All generated state routes to
<StateBase> / <OutputBase>; a plugin update replaces the install.
Self-improvement hooks
The skill grows itself within narrow, auditable bounds:
- Write to
<StateBase>/TODO.md when discovery proposes a check needing new permissions, network access, or remediation path. Human approval required before it becomes active.
- Mark catalog entries
deprecated: true (never delete silently) with a deprecation_reason when a check has become meaningless for this host, via the catalog overlay (reference/shared/catalog-overlay.md), never by editing the shipped catalog. Propose removal after 3 consecutive crashes (each increments crash_count).
- Demote chronically quiet checks. After 4 consecutive identical outputs, propose demotion to monthly cadence. Write to
<StateBase>/TODO.md; the approved demotion is an overlay cadence patch. Don't reshuffle cadence on your own.
- Refresh the CISA KEV cache weekly via
scripts/<os>/lib/Get-CisaKevCache.ps1 (the winget-upgrades check does this automatically; the live cache lives under $env:LOCALAPPDATA\machine-health\cache, seeded from the shipped catalog/cisa-kev.json stub). Skip if younger than 7 days.
- Never rewrite history.
state/history.jsonl is append-only. It is the trend-detection source of truth, and rewriting an old line corrupts every severity-trend comparison drawn from it. If a historical entry is wrong, add a correction entry; don't edit the old line.
Consumer configuration
- Report directory: the
report_dir plugin option (set at install or via /plugin configure machine-health@<marketplace>).
- Check catalog:
/machine-health:setup interviews and writes the machine-local overlay (disable/deprecate/demote shipped checks, register custom ones).
- Remediation approvals:
<StateBase>/state/approvals.json per reference/shared/approvals.md; nothing is approved by default. /machine-health:setup can seed it.
Not in scope for this skill
- Scheduling. A separate routine creates the scheduled task. This skill is stateless about cadence.
- Elevation prompts. If admin is needed, report
UNKNOWN and let the human decide whether to rerun elevated.
- Cross-machine aggregation. This skill is single-host. Multi-host dashboards are a separate concern.
1---2name: audit-93description: Audits local workstation health and emits a findings report: runs OS-specific checks (disk, OS updates, security posture, CISA KEV) from a versioned catalog with trend-aware severity; remediation only when pre-approved. Use when: 'machine health check', 'audit my machine', 'system health', 'workstation status report', or when a scheduled weekly routine fires. Outputs a dated markdown report; updates append-only history state. Windows fully implemented; macOS/Linux scaffolded (reports UNKNOWN and stops).4---56# machine-health78## Overview910This skill performs a **weekly workstation health audit** with a fail-safe posture: surface issues over silently fixing them. Findings always include reproduction commands so the human can rerun the check outside the skill. Remediations are narrow, logged, and only attempted when the OS-specific `remediation-policy.md` authorizes them. Severity is always trend-aware. A single reading is rarely load-bearing; history is consulted before finalizing severity.1112The skill is stateless about scheduling; a separate routine (e.g., a Monday 08:00 scheduled task, or an ad-hoc `/machine-health:audit` invocation) calls into it.1314**Progressive disclosure by OS.** The skill detects the host OS and loads only matching references and scripts. Windows is fully implemented. macOS and Linux are scaffolded as `NOT_IMPLEMENTED` stubs so they can be populated in future passes without restructuring.1516## Resolving output locations (do this first)1718Two roots, resolved through the plugin's configuration seams:1920| Root | Holds | Resolution |21|---|---|---|22| **Report root** (`-OutputBase`) | `reports/`, the human-facing dated reports | `${user_config.report_dir}` when set to a non-empty path; if it is empty or still shows an unexpanded `${user_config.report_dir}` token (option unset), default to `$env:USERPROFILE\Documents\MachineHealth` |23| **State root** (`-StateBase`) | `state/` (history, latest snapshot, approvals), `logs/`, catalog overlay, custom checks, `TODO.md` proposals | `${CLAUDE_PLUGIN_DATA}`, the per-plugin data directory that survives plugin updates, passed explicitly when the token expands. If it is unexpanded, do **not** fall through to the orchestrator's environment ladder: the harness sets `CLAUDE_PLUGIN_DATA` per plugin, and a subprocess inherits whichever plugin's value its parent process already carried, so a skill-invoked tool can inherit an unrelated installed plugin's value and write this plugin's `state/`, `logs/`, and catalog overlay into that plugin's directory. Pass `-StateBase <report-root>` explicitly instead. Colocating state with the reports is wrong-but-visible, where the inherited variable is wrong-and-silent. Report that the plugin-specific root could not be resolved. Never substitute a literal path: the directory under `~/.claude/plugins/data/` is named for the plugin's install identity, not the plugin, so a guessed path names a different directory and splits state from the overlay |2425Pass both explicitly to the orchestrator (`-OutputBase <report-root> -StateBase <state-root>`). Never write generated state into the plugin's own install directory. A plugin update replaces it.2627Other runtime parameters the caller passes (via slash-command arguments, scheduled task arguments, or environment variables; the orchestrator accepts them as parameters):2829| Parameter | Default | Meaning |30|---|---|---|31| `RunMode` | `weekly` | One of `weekly`, `on-demand`, `first-run`. `first-run` forces dry-run and seeds state. |32| `DryRun` | `$false` (on `weekly`/`on-demand`); `$true` (on `first-run`) | Skip all remediations; still produce a full report. |3334## OS detection and routing3536```text37# Detect current OS (the skill requires PowerShell 7.4+; launch pwsh, not38# Windows PowerShell 5.1, which cannot run the checks)39if (PowerShell 7.4+) use $IsWindows / $IsMacOS / $IsLinux40else non-PowerShell shell: uname -s -> Darwin|Linux4142# Load references (shared first, then OS-specific)43Read reference/shared/severity-rubric.md44Read reference/shared/output-schema.md45Read reference/shared/report-template.md46Read reference/shared/discovery-guide.md47Read reference/shared/remediation-philosophy.md48Read reference/shared/approvals.md49Read reference/shared/catalog-overlay.md50Read reference/<os>/*.md5152# If the detected OS folder contains NOT_IMPLEMENTED.md, STOP.53# Produce an UNKNOWN-severity report explaining the gap, link to54# reference/shared/discovery-guide.md for porting guidance, and exit.55# Never attempt to execute Windows scripts on macOS/Linux.56```5758Routing table:5960| Detected OS | Orchestrator | Status |61|---|---|---|62| Windows | `scripts/windows/Invoke-MachineHealthCheck.ps1` | Implemented |63| macOS | `scripts/macos/NOT_IMPLEMENTED.md` | Stub. Report UNKNOWN and stop |64| Linux | `scripts/linux/NOT_IMPLEMENTED.md` | Stub. Report UNKNOWN and stop |6566## High-level procedure67681. **Verify preconditions.** PowerShell 7.4+ (enforced by the orchestrator's `#Requires`; individual checks that need a still-newer cmdlet return UNKNOWN rather than aborting). The report and state roots are writable. Record elevation state via `scripts/<os>/lib/Test-IsElevated.ps1`. Never prompt for UAC.692. **Load and filter the catalog.** The orchestrator reads the shipped `catalog/checks.jsonc`, merges the machine-local overlay at `<StateBase>/catalog/checks.local.jsonc` when present (see `reference/shared/catalog-overlay.md`), and keeps entries whose `os` list contains the current OS and where `enabled: true` and `deprecated: false`.703. **Load trend context.** Read the tail of `<StateBase>/state/history.jsonl` (last 8 weeks) for each check. Pass the slice to each check script over stdin so checks can annotate deltas, but checks remain stateless themselves.714. **Invoke the OS orchestrator.** Pass `OutputBase`, `StateBase`, and `RunMode`. The orchestrator dispatches checks under per-check 90s timeouts, collects JSON results, applies trend-aware severity adjustments, and, on non-dry runs, dispatches authorized remediations with before/after logging.725. **Receive the structured result.** Run discovery per `reference/shared/discovery-guide.md`. Propose 1–3 OS-appropriate new checks. Straightforward read-only ones may be implemented as custom checks (script under `<StateBase>/scripts/<os>/checks/`, registered in the catalog overlay); anything needing new permissions or remediation lands in `<StateBase>/TODO.md` for human approval. Checks broadly useful to every consumer are best contributed to the plugin itself.736. **Render the markdown report** from `reference/shared/report-template.md` into `<OutputBase>/reports/health-<UTC-timestamp>.md` (one file per run, so a same-day rerun does not overwrite the earlier report).7475 When the severity spread or trend deltas would read better visually, also generate a self-contained static HTML view of that report (color-coded CRIT/WARN/UNKNOWN, no remote fetch). The markdown `.md` report stays the durable record.767. **Update state.** Write `<StateBase>/state/latest.json`. Append one compact line to `<StateBase>/state/history.jsonl`, the trend source of truth.778. **Verify and summarize.** Confirm the report exists. Print CRIT/WARN counts + report path to session output. A clean run may still include `UNKNOWN` findings. Call those out too.7879## Guardrails8081- **Max total runtime:** 15 minutes. Partial results mark missing checks `UNKNOWN` with reason `"timeout"`.82- **Per-check timeout:** 90 seconds.83- **No interactive prompts.** Ever. This runs unattended on a schedule; a stalled prompt would hang the weekly job.84- **No retry loops on failure.** One attempt per check, one attempt per remediation. `reference/shared/remediation-philosophy.md` carries the one-attempt rationale for remediations; checks stay one-attempt for the same reason. A flaky check should read as flaky, not as eventually-fine.85- **First-run dry mode.** The first ever run (RunMode `first-run`) forces `DryRun = true`. Seeds state, produces the first report, queues remediation approval in `<StateBase>/TODO.md`.86- **Idempotency.** Two runs back-to-back produce two valid reports and two history entries with no partial state.87- **Egress allowlist.** Microsoft Update endpoints, winget sources, and the CISA KEV feed are the only permitted outbound URLs. This keeps the skill's network footprint auditable and prevents an unvetted check or remediation from calling an arbitrary host. Every outbound URL is logged to `<StateBase>/logs/run-YYYY-MM-DD.log`.88- **No `Invoke-Expression`** on any data the skill did not author itself in this session. No "run whatever came back" patterns.89- **Admin is not assumed.** Any check requiring elevation self-checks and returns `UNKNOWN` with `needs_admin: true` rather than prompting UAC.90- **Defer remediations under user load.** If the interactive session has had input in the last 60 seconds, log a note and run read-only checks only. Defer remediations to next run.91- **Never write into the plugin install directory.** All generated state routes to `<StateBase>` / `<OutputBase>`; a plugin update replaces the install.9293## Self-improvement hooks9495The skill grows itself within narrow, auditable bounds:9697- **Write to `<StateBase>/TODO.md`** when discovery proposes a check needing new permissions, network access, or remediation path. Human approval required before it becomes active.98- **Mark catalog entries `deprecated: true`** (never delete silently) with a `deprecation_reason` when a check has become meaningless for this host, via the catalog overlay (`reference/shared/catalog-overlay.md`), never by editing the shipped catalog. Propose removal after 3 consecutive crashes (each increments `crash_count`).99- **Demote chronically quiet checks.** After 4 consecutive identical outputs, propose demotion to monthly cadence. Write to `<StateBase>/TODO.md`; the approved demotion is an overlay `cadence` patch. Don't reshuffle cadence on your own.100- **Refresh the CISA KEV cache** weekly via `scripts/<os>/lib/Get-CisaKevCache.ps1` (the winget-upgrades check does this automatically; the live cache lives under `$env:LOCALAPPDATA\machine-health\cache`, seeded from the shipped `catalog/cisa-kev.json` stub). Skip if younger than 7 days.101- **Never rewrite history.** `state/history.jsonl` is append-only. It is the trend-detection source of truth, and rewriting an old line corrupts every severity-trend comparison drawn from it. If a historical entry is wrong, add a correction entry; don't edit the old line.102103## Consumer configuration104105- **Report directory**: the `report_dir` plugin option (set at install or via `/plugin configure machine-health@<marketplace>`).106- **Check catalog**: `/machine-health:setup` interviews and writes the machine-local overlay (disable/deprecate/demote shipped checks, register custom ones).107- **Remediation approvals**: `<StateBase>/state/approvals.json` per `reference/shared/approvals.md`; nothing is approved by default. `/machine-health:setup` can seed it.108109## Not in scope for this skill110111- **Scheduling.** A separate routine creates the scheduled task. This skill is stateless about cadence.112- **Elevation prompts.** If admin is needed, report `UNKNOWN` and let the human decide whether to rerun elevated.113- **Cross-machine aggregation.** This skill is single-host. Multi-host dashboards are a separate concern.