codealive-ai/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 process persistently burns CPU, 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/CPU 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, or broken app installs.
npx skills add https://github.com/CodeAlive-AI/ai-driven-development --skill 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.
Trigger on any of:
JetsamEvent-*.ips with vm-compressor-space-shortage| 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: disk/memory/Jetsam critical triggers plus sustained CPU anomaly detection, incident lifecycle, hysteresis, notifier choices, install/restore commands |
| assets/mac-health-check | Production-ready bash script (~250 lines, bash 3.2 compatible) |
| assets/mac-health-action | Background action dispatcher for read-only Codex/Claude investigations and explicitly confirmed graceful process stopping |
| 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.
brew install --cask stats) — you see issues forming, not just when they explode.mo purge and mo clean are the right primary tools./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.references/triage.md, identify which signal fired and how urgent.df -h /System/Volumes/Data and write down free GB.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.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.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. 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.
rm: 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.
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.
assets/mac-health-check and assets/mac-health-action to ~/bin/ (mkdir first; chmod +x).assets/com.local.mac-health-check.plist to ~/Library/LaunchAgents/.assets/config.sh to ~/.config/mac-health/config.sh (mkdir first).brew install vjeantet/tap/alerter (NOT terminal-notifier — it's broken in 2026 on Sequoia/Tahoe).brew install --cask stats for passive layer.launchctl load -w ~/Library/LaunchAgents/com.local.mac-health-check.plist.alerter once interactively (alerter --message test) so macOS asks for Notification Center permission.config.sh after a week if pattern noisy.The calibration window applies to the original disk/memory/Jetsam critical sensors. CPU uses conservative developer-workstation defaults and starts immediately; process advisories are silent and deduplicated for the full incident lifetime.
Verify with launchctl list | grep mac-health (should show PID and exit 0) and tail -f ~/Library/Logs/mac-health/health.log.
Read references/alerting.md § Troubleshooting. Common causes:
terminal-notifier (the cask) instead of alerter — replace.launchctl bootstrap gui/$(id -u) <plist>.touch ~/.config/mac-health/silent to suppress.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.
references/never-touch.md — even if user explicitly asks. Push back, explain the consequence.mo purge and mo clean always with --dry-run first. Show estimated reclaim, get confirm.tmutil delete <path>, never rm. TM-tagged paths require the tmutil API./Library, /private/var/db/*: only the allowlisted subpaths from references/never-touch.md § Sudo allowlist.references/alerting.md).Stop Process…, but only as an explicit user action. Revalidate PID/executable identity and ownership, confirm, send SIGTERM first, and require a separate confirmation before SIGKILL.rm /private/var/vm/swapfile* while running = guaranteed kernel panic.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./bin/bash 3.2.57. set -u + local var (no init) = unbound on first reference. The shipped script handles this.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/.ps %cpu is a decaying average over up to one minute and is measured relative to one logical core, so a process may exceed 100 %. Whole-system CPU from the second iostat sample is 0–100 % across the machine. iostat is much lighter than starting top every five minutes..app bundle in the executable/parent chain, then from the executable fallback. Alerts show both App and Process so helpers such as Codex (Renderer) are attributed to ChatGPT.df may not show the change immediately; wait or run diskutil info /System/Volumes/Data | grep "Container Free".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).web-searcher subagent before writing the item's description. See Workflow A step 4.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):
mo purge (~30–50 GB across many scan paths)~/Downloads review (old installers, recordings, archived repos): ~15 GBActive 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.
Take codealive-ai/maintaining-macos-health from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference brew.
Without those the skill loads but fails at the first command.