AI & Agentshigh risk

maintaining-macos-health

Hands-on playbook for macOS disk cleanup, dev-machine optimization, and proactive health alerting. Use when the Mac is full or slow, when a kernel panic / watchdog timeout / vm-compressor-space-shortage / Jetsam event happened, when the user asks to free disk space, audit storage, set up disk/memory alerts, or restore the same monitoring on a new Mac. Built around Mole (`mo` CLI) for safety guards plus a custom LaunchAgent-based alerter for active warnings. Covers Apple Silicon laptops with heavy AI/Docker workloads. Not for general macOS support, hardware diagnostics, networking issues, GUI / window-manager bugs, Time Machine recovery, broken app installs, or app-specific performance problems unrelated to disk or memory pressure.

CodeAlive-AI/ai-driven-development·skills/maintaining-macos-health/SKILL.md
38/ 100おすすめ度

この Skill を導入

coding agent を選び、プロジェクト用または個人用コマンドをコピーします。

収録 commit に固定
プロジェクトに導入.agents/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a codex -y
個人環境に導入~/.agents/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a codex -g -y
手動配置先.agents/skills/maintaining-macos-healthOfficial docs ↗
プロジェクトに導入.claude/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a claude-code -y
個人環境に導入~/.claude/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a claude-code -g -y
手動配置先.claude/skills/maintaining-macos-healthOfficial docs ↗
プロジェクトに導入.agents/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a github-copilot -y
個人環境に導入~/.copilot/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a github-copilot -g -y
手動配置先.agents/skills/maintaining-macos-healthOfficial docs ↗
プロジェクトに導入.agents/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a cursor -y
個人環境に導入~/.cursor/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a cursor -g -y
手動配置先.agents/skills/maintaining-macos-healthOfficial docs ↗
プロジェクトに導入.agents/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a gemini-cli -y
個人環境に導入~/.gemini/skills/maintaining-macos-health
npx skills add https://github.com/CodeAlive-AI/ai-driven-development/tree/8bf74245234ac9a1779788dfd40dbeafb8655e0b/skills/maintaining-macos-health -a gemini-cli -g -y
Native Gemini CLIgemini skills install https://github.com/CodeAlive-AI/ai-driven-development.git --scope workspace --path skills/maintaining-macos-health
手動配置先.agents/skills/maintaining-macos-healthOfficial docs ↗
⚠ インストールには open-source skills CLI を使用します。実行前にソースと権限を確認してください。
# Maintaining macOS Health

Recovery and prevention playbook for macOS disk and memory crises. Validated against a real watchdog-timeout kernel panic on Apple Silicon caused by `vm_compressor` segments saturated to 100 % with the disk over 90 % full. The same playbook works for routine cleanup or first-time setup on a new machine.

## Table of contents

- [When to use](#when-to-use)
- [Skill layout](#skill-layout)
- [Core mental model](#core-mental-model)
- [Standard workflows](#standard-workflows)
  - [A. "Free space NOW" (incident response)](#a-free-space-now-incident-response)
  - [B. "Set up alerting" (new machine or first time)](#b-set-up-alerting-new-machine-or-first-time)
  - [C. Alerter stopped working / making noise](#c-already-have-alerter-but-it-stopped-working--making-noise)
  - [D. "Uninstall an app cleanly"](#d-uninstall-an-app-cleanly)
- [Safety rules (non-negotiable)](#safety-rules-non-negotiable)
- [Domain quirks captured](#domain-quirks-captured)
- [Outcomes scale](#outcomes-scale)

## When to use

Trigger on any of:

- Disk free < 20 % or user complains about being out of space
- Watchdog-timeout / kernel panic / "no checkins from watchdogd"
- New `JetsamEvent-*.ips` with `vm-compressor-space-shortage`
- "Mac is slow", swap > 6 GB, sustained Critical memory pressure
- User wants to set up monitoring/alerting from scratch
- Migration to a new Mac → restore the same alerter
- General "clean my Mac" / "audit storage" / "free space" requests

## Skill layout

| File | Use for |
|---|---|
| `references/triage.md` | First 5 minutes — which signal fired, which tier of cleanup to start with |
| `references/cleanup-tiers.md` | Tiered cleanup playbook (10 tiers, zero-risk → discuss-first), copy-paste-safe shell blocks |
| `references/never-touch.md` | Categories that **must not** be deleted even under sudo (Mole-derived blacklist + incident-derived additions) |
| `references/mole-techniques.md` | What Mole does that we borrow: marker→target map for `mo purge`, safe-path validators, age thresholds |
| `references/alerting.md` | Full alerter design: 3 CRITICAL-only triggers, hysteresis, calibration, alerter vs terminal-notifier vs osascript, install/restore commands |
| `assets/mac-health-check` | Production-ready bash script (~250 lines, bash 3.2 compatible) |
| `assets/com.local.mac-health-check.plist` | LaunchAgent plist with `StartCalendarInterval` (StartInterval is broken on laptops) |
| `assets/config.sh` | Default config with safe thresholds |
| `assets/render-cleanup-plan.py` | Interactive HTML cleanup-plan UI. Renders categorised checkboxes from a JSON of scan findings, serves on `127.0.0.1:18347`, opens browser, waits for the user's selection, writes it to `/tmp/cleanup-selection-<ts>.json`. Used by Workflow A. |
| `assets/apply-cleanup-selection.py` | The **only sanctioned way** to apply a cleanup selection. Reads `selected_items` from a selection JSON and executes each item's `command` field. Enforces protected-override check + path validation + Mole-compatible operations log. Prevents drift between what the user picked and what gets deleted. Supports `--dry-run`. |

Read the relevant reference before acting. Do NOT operate from memory of these files — the details are calibrated to a real incident and small changes break safety.

## Core mental model

1. **Monitor passively** — Stats menubar (`brew install --cask stats`) — you see issues forming, not just when they explode.
2. **Alert actively** — only on truly CRITICAL conditions: disk < 10 %, memory pressure Critical AND swap > 8 GB, or new JetsamEvent with `vm-compressor-space-shortage`. Anything else is noise.
3. **Cleanup tiers** — start zero-risk (caches, orphan data), only escalate to project artifacts and sudo categories if needed. Mole's `mo purge` and `mo clean` are the right primary tools.
4. **Mole is the safety floor** — even when running shell commands by hand, follow Mole's path-validation rules: never delete inside `/System`, `/bin`, `/usr`, `/etc`, `/var/db` outside specific allowlisted subpaths; bin/ only under .NET; vendor/ only under PHP; protect AI/password/VPN/keychain bundle IDs.

## Standard workflows

### A. "Free space NOW" (incident response)

1. **Triage** — read `references/triage.md`, identify which signal fired and how urgent.
2. **Snapshot baseline** — `df -h /System/Volumes/Data` and write down free GB.
3. **Run all scans, don't delete yet** — `du` audit of `$HOME` subdirs, `mo clean --dry-run`, `mo purge --dry-run --debug`, `docker system df -v`, `~/Downloads` audit. Capture everything; **deletion comes only after user picks via the UI**.
4. **Resolve unknown items before building JSON** — for every candidate > 500 MB whose purpose you cannot explain in one sentence (unfamiliar app, unfamiliar bundle ID, unfamiliar dotfolder, vendor-specific cache, ML model weights, VM image, etc.), **research it first**: check `references/never-touch.md` for a known entry, then delegate a quick lookup to the `web-searcher` subagent ("what is `<path or bundle id>` on macOS, is it safe to delete in 2026"). Wait for the answer, then write a concrete `description` (1-3 sentences in the user's language) into the item — *what it is*, *who created it*, *what feature uses it*, *what breaks if deleted*, *whether it auto-recreates*. **Never show the report with vague placeholders like "unknown" or "ML data"** — that defeats the point of the UI. If a web lookup contradicts `never-touch.md`, prefer the web answer (it's fresher) and propose an update to the reference file.
5. **Build the data JSON** — every candidate becomes a structured `item` (id, label, path, size_bytes, age_days, kind, command, **mandatory `description`**, optional `protected` + `warning`). Write to `/tmp/cleanup-data-<ts>.json`. Use schema from `assets/render-cleanup-plan.py` docstring.
6. **Render and open the cleanup UI**:
   ```bash
   python3 .../assets/render-cleanup-plan.py /tmp/cleanup-data-<ts>.json
   ```
   The script starts a one-shot HTTP server on `127.0.0.1:18347`, opens the page in the user's default browser, and **blocks** until the user clicks Submit or Cancel. On submit it writes `/tmp/cleanup-selection-<ts>.json` and prints that path to stdout. Tell the user out loud: "браузер открыт — поставь галочки, нажми Submit, потом пингани меня". Then **stop and wait**.
7. **After the user pings** — read the selection JSON, render the user's choices back in chat (categories, item list, total GB, any protected overrides flagged ⚠), and **ask one explicit confirmation** before deleting. Don't run anything until they say "go".
8. **Apply via the helper script — never hand-rolled `rm`**:
   ```bash
   python3 .../assets/apply-cleanup-selection.py /tmp/cleanup-selection-<ts>.json
   ```
   The script reads `selected_items` from the selection JSON and executes each item's `command` field, with built-in safeguards: protected items must appear in `protected_overrides` or are skipped; commands are validated against a hard-protected path list and a `..`-component check before execution; every action is logged to `~/.config/mole/operations.log` in Mole-compatible TSV. `--dry-run` previews without executing. **Do not write your own `rm` blocks in the apply phase** — that's how you delete items the user explicitly unchecked. The selection JSON is the single source of truth; if it's not in `selected_items`, it does not get deleted. Run `df -h /System/Volumes/Data` before and after for the user-visible delta.
9. **Stop at goal** — most users target 100 GB free. Don't go below that just for sport.

The Python script is bash-3.2-friendly, uses only stdlib, and is safe to run from inside the agent's shell. Hard-protected items (per `references/never-touch.md`) must always appear in the UI with `"protected": true` + a concrete `warning` string — the UI dims them and requires a per-item confirm dialog before they can be checked. **Never** omit a protected item that user data depends on (Telegram tdata, Bear database, password-manager containers, etc.) — visibility teaches the user the surrounding risk.

### B. "Set up alerting" (new machine or first time)

1. Copy `assets/mac-health-check` to `~/bin/mac-health-check` (mkdir first; chmod +x).
2. Copy `assets/com.local.mac-health-check.plist` to `~/Library/LaunchAgents/`.
3. Copy `assets/config.sh` to `~/.config/mac-health/config.sh` (mkdir first).
4. `brew install vjeantet/tap/alerter` (NOT terminal-notifier — it's broken in 2026 on Sequoia/Tahoe).
5. `brew install --cask stats` for passive layer.
6. `launchctl load -w ~/Library/LaunchAgents/com.local.mac-health-check.plist`.
7. First run permission prompt: open `alerter` once interactively (`alerter --message test`) so macOS asks for Notification Center permission.
8. Tell the user: 7-day calibration is silent (logs only). Edit `config.sh` after a week if pattern noisy.

Verify with `launchctl list | grep mac-health` (should show PID and exit 0) and `tail -f ~/Library/Logs/mac-health/health.log`.

### C. "Already have alerter, but it stopped working / making noise"

Read `references/alerting.md` § Troubleshooting. Common causes:
- Stuck/old `terminal-notifier` (the cask) instead of `alerter` — replace.
- LaunchAgent not loading after macOS update — `launchctl bootstrap gui/$(id -u) <plist>`.
- Notifications going to Script Editor — TCC permission was revoked, re-grant.
- Constant alerts during heavy dev work — `touch ~/.config/mac-health/silent` to suppress.

### D. "Uninstall an app cleanly"

`mo uninstall <app>` — Mole scans 12+ locations for app traces (Application Support, Containers, Group Containers, Caches, Preferences, Saved State, LaunchAgents, LaunchDaemons, login items, etc.). Always show dry-run first, never bypass.

## Safety rules (non-negotiable)

1. **Never delete without dry-run + user confirmation** for any tier ≥ 5 or any sudo operation.
2. **Never bypass `references/never-touch.md`** — even if user explicitly asks. Push back, explain the consequence.
3. **`mo purge` and `mo clean` always with `--dry-run` first.** Show estimated reclaim, get confirm.
4. **For Time Machine backups: `tmutil delete <path>`, never `rm`.** TM-tagged paths require the `tmutil` API.
5. **For sudo cleanup of `/Library`, `/private/var/db/*`**: only the allowlisted subpaths from `references/never-touch.md` § Sudo allowlist.
6. **No auto-cleanup hooks tied to alerts.** Alerts notify; user decides. Documented anti-pattern (Google SRE, also confirmed by community 2025-2026 — see `references/alerting.md`).
7. **Don't delete swap files.** `rm /private/var/vm/swapfile*` while running = guaranteed kernel panic.
8. **Apply phase reads only the selection JSON.** Never hand-roll `rm` blocks or hard-code paths from the earlier scan when applying. Real incident: agent applied the default-selected recordings list from the original scan, ignoring that the user had unchecked them in the UI before submitting. The fix is structural — use `assets/apply-cleanup-selection.py` which iterates `selected_items` from the selection JSON only.

## Domain quirks captured

- macOS Tahoe (26.x) ships `/bin/bash` 3.2.57. `set -u` + `local var` (no init) = unbound on first reference. The shipped script handles this.
- LaunchAgent **does not inherit user PATH**. Plist must declare `EnvironmentVariables.PATH` and use absolute paths for interpreters.
- `StartInterval` clock pauses during sleep on Apple Silicon laptops (radar 6630231). Use `StartCalendarInterval` with explicit minute entries (the shipped plist has all 12).
- `terminal-notifier` is effectively unmaintained (last release 2019-11) and silently fails on Sequoia/Tahoe Apple Silicon. Use `alerter` instead.
- `osascript display notification` from launchd attributes to "Script Editor" and is unreliable. Use `alerter` from launchd context.
- `log show --last 6m` is too slow (30+ s) for periodic checks. Poll `/Library/Logs/DiagnosticReports/JetsamEvent-*.ips` instead — async write delay is acceptable on a 5-min cadence.
- `JetsamEvent-*.ips` files live in `/Library/Logs/DiagnosticReports/` (system-wide), NOT `~/Library/Logs/DiagnosticReports/`.
- APFS purgeable space lags behind actual deletion by minutes. After cleanup, `df` may not show the change immediately; wait or run `diskutil info /System/Volumes/Data | grep "Container Free"`.
- **Claude Desktop `vm_bundles/claudevm.bundle/` is Claude Cowork**, not "Claude Code sandbox" — it's a ~10 GB Ubuntu VM image (`rootfs.img`, `sessiondata.img`, `efivars.fd`, `vmIP`) for Anthropic's sandboxed code-execution feature. It is **auto-provisioned at every Claude Desktop launch** via an SHA1 integrity check, so its recent mtime ≠ user activity. Technically safe to delete (no chat/MCP impact), but Claude Desktop **silently re-downloads ~10 GB on next launch** and runs at ~55 % CPU while doing so. The Claude Code CLI does NOT use this bundle. Recommended classification: Tier 10 discuss-first with quit-Claude-Desktop pre-step and a warning that the bundle returns until Anthropic ships an opt-out toggle (open in [anthropics/claude-code#57371](https://github.com/anthropics/claude-code/issues/57371)).
- **General rule**: if you encounter a folder/bundle you can't describe in one sentence (especially > 500 MB), don't guess — delegate a quick lookup to the `web-searcher` subagent before writing the item's `description`. See Workflow A step 4.

## Outcomes scale

A representative recovery from a Mac that hit ~8 % free after long memory-pressure sessions on a heavily-loaded dev profile (Docker, multiple AI tools, IDEs, browsers):

- ~25 % of total disk capacity recovered in a 4-hour session
- Largest single contribution: project build artifacts via `mo purge` (~30–50 GB across many scan paths)
- Stale IDE installations + caches + preferences: ~10 GB
- Docker reclaim (unused images, dead builders, orphan volumes): ~10 GB
- `~/Downloads` review (old installers, recordings, archived repos): ~15 GB
- Package-manager caches (npm, pnpm, gradle, maven, cargo, brew): ~5 GB
- Sudo-tier cleanup (system logs, vendor-app depots): ~5–10 GB

Active alerter installed with 7-day calibration window; verified via synthetic disk-trigger test before going live. Stats menubar app installed for passive monitoring.

Numbers scale with workload and disk size. Light users will see less; heavy AI/Docker/IDE users will see more.