Disk, memory & shell hygiene for a Mac that runs a lot of AI coding agents on limited RAM and disk — diagnose, reclaim safely, watch for jetsam-inducing memory pressure, and audit stray git worktrees, without ever deleting work that isn't provably recoverable.
It's a macOS port of a WSL2 optimization writeup. On WSL2 the failure mode was silent OOM — the kernel dismantled the session while protecting the memory hogs. A 16 GB Mac running fleets of agents dies two ways: disk exhaustion (package caches, agent runtimes, editor workspace state, and stray git worktrees that grow on every run and never self-reclaim) and, closer to the original, memory pressure — a fleet of agents (each a heavy process tree that fans out subagents) exhausts RAM + compressor + swap until the kernel's jetsam killer force-quits whatever app it can reach. This toolkit covers both.
The design goal, borrowed from that writeup, is to change the shape of the failure: from "silent and fatal" to "observable and bounded." You get a report you can read, a reclaim that's safe by construction, and a watcher that acts at a threshold and tells you what it did.
git clone https://github.com/kylebrodeur/mac-optimize.git
cd mac-optimize
make install # deploy tools to ~/.local/bin + load the launchd agents
make doctor # verify the installPure clone-and-make — no tap, no package manager. install.sh copies the
tools into ~/.local/bin, vendors the shared library, and loads the launchd
automation with your per-user paths filled in. Re-running it is safe.
git clone https://github.com/kylebrodeur/mac-optimize.git
cd mac-optimize
make install # deploy tools to ~/.local/bin + load the launchd watcher
make doctor # verify the install (tools + launchd, and skills if present)
diskreport # see where your disk went (read-only)
mac-reclaim # reclaim safe caches now
worktree-audit # find stray git worktrees~/.local/bin must be on your PATH. Re-running make install is safe.
| Tool | What it does |
|---|---|
diskreport |
Read-only "where did my disk go?" — top consumers in Application Support, Caches, and dev/agent caches, plus the big "review-tier" state (VS Code workspaceStorage, Claude vm_bundles, .claude/projects) and recent reclaim history. Deletes nothing. --scan [PATH] adds a one-shot dust walk of PATH (default $HOME) to catch big single files/dirs the fixed buckets don't look inside — opt-in since it walks the whole tree. For open-ended interactive digging, use ncdu directly (brew install ncdu dust) — navigate, sort, delete in place. |
mac-reclaim |
Reclaims in two tiers. Safe tier (default) clears caches that rebuild on demand (pnpm store prune, uv cache prune, npm _cacache, codex runtimes, .ShipIt updaters, stale logs) — safe by construction. --deep prunes idle vm_bundles, local-agent-mode-sessions, and .claude/projects only with evidence they're unused, behind --dry-run, an allowlist, lsof open-file guards, and a keep-newest floor. Orphaned VS Code workspaceStorage is reported as REVIEW/protected and never removed until an archive-first backup and explicit verified prune workflow exists. |
worktree-audit |
(shared via agent-machine-lib — same copy as wsl-optimize) Finds stray git worktrees and classifies each SAFE (clean + every commit reachable from another ref) or REVIEW (dirty or has commits that exist nowhere else). --prune removes SAFE ones; --backup archives REVIEW ones to git bundles so they become safe to prune. |
diskguard |
The launchd watcher (an earlyoom analog). At login + every 3 h: below 20 GB free it runs the safe reclaim and posts a non-blocking notification; below 10 GB it posts an urgent notice pointing at the manual deep tools. Never runs a destructive prune unattended. |
memguard |
The memory analog of diskguard (the real earlyoom port). A launchd watcher at login + every 5 min: it reads the kernel's own memory-pressure level (kern.memorystatus_vm_pressure_level) and free-RAM %, and at the warn/critical thresholds posts a non-blocking notification naming the largest RAM consumer so you can act before jetsam picks the victim. Never kills a process or deletes app state. In the disk↔swap coupling quadrant (RAM tight and disk too low for swap to grow — the state that force-panicked one of these machines via a watchdogd timeout) it now also auto-triggers mac-reclaim's SAFE tier immediately (same rebuildable-cache-only tier diskguard already runs on its own schedule, just sooner) and nags every cycle instead of every 30 min until the coupling clears. |
codex-backup |
Backs up, indexes, prunes, and restores ~/.codex/sessions — the large single-copy JSONL logs Codex writes per run (10 GB+ is normal). backup rsyncs them to an external drive cumulatively (never --delete), so a later prune frees local space while the backup keeps everything. index inventories every session by age bucket (45+/30-45/15-30/<15 days idle), size, project (cwd), and backup status. prune --older-than N deletes local sessions idle ≥ N days only when verified present in the backup — dry-run by default, keeps the N newest, and never touches anything not backed up. restore copies sessions back by date/uuid/project/all and never clobbers a newer local file. verify reports drift. Resolves the drive from --dest, $CODEX_BACKUP_DEST, ~/.config/mac-optimize/codex-backup.conf, or the first mounted volume with a mac-optimize-backups/ folder; a weekly launchd agent runs backup --quiet and no-ops when the drive is absent. |
diskhealth |
Read-only SMART + filesystem health for the SSDs in the Mac. Wraps smartctl (smartmontools) to report overall health, temperature, available spare, wear (percentage used), media errors, and — the one that catches external drives — the power-cycle/unsafe-shutdown pattern (a drive power-cycling ~10×/hour or with hundreds of unsafe shutdowns is being yanked from power, not failing). --verify adds a read-only diskutil verifyVolume on mounted volumes. Requires brew install smartmontools. |
mac-optimize-doctor |
Read-only health check (make doctor): confirms the tools are on PATH and the launchd agents are loaded + valid, and points you at npx skills list for an agent-agnostic skills check. Never downloads or executes remote code. Exits non-zero on any failure. |
Four principles, in order of trust:
- Observable before action.
diskreportanswers "what's using my disk" without touching anything. Diagnose first. - Safe by construction. The default
mac-reclaimonly clears caches the owning tool rebuilds on demand —pnpm/uvprune only unreferenced packages; npm's_cacacheis a re-download cache; installednode_modulesare never touched. It cannot remove something you're using. - Evidence before deletion. The deep tier and worktree removal require proof an item is disposable: idle
vm_bundles,local-agent-mode-sessions, and.claude/projects; a worktree whose every commit is reachable from another branch/tag/remote. Orphaned VS CodeworkspaceStorageis reported as REVIEW/protected and is never removed until an archive-first backup and explicit verified prune workflow exists. Anything unproven is protected, never deleted. - Bounded automation.
diskguardruns unattended, but only ever the safe tier, and only at a threshold — turning a silent disk-fill into an observable, self-healing event it logs and notifies about.memguardis the memory counterpart: it watches the kernel's pressure level and warns early (naming the offender), and never kills a process or deletes app state — but it will run the same bounded safe tierdiskguarduses, immediately, the moment disk is what's stopping swap from growing. That one lever was already proven safe elsewhere in this repo; the only thing that changed is when it fires.
Commands & flags
| Command | Effect |
|---|---|
diskreport |
Read-only disk report. |
diskreport --scan [PATH] |
+ one-shot dust walk of PATH (default $HOME) for big single files/dirs the fixed buckets miss. Requires dust. |
mac-reclaim |
Reclaim safe caches (unattended-safe). |
mac-reclaim --deep --dry-run |
Preview deep prune — deletes nothing, prints each candidate + why it's unused. |
mac-reclaim --deep |
Deep prune (prompts). Add --yes to skip the prompt (automation). |
mac-reclaim --quiet |
Summary line only. |
worktree-audit [ROOT…] |
Audit stray worktrees. Precedence: positional ROOT…, else $WORKTREE_ROOTS, else common roots (~/workspace, ~/projects, ~/src, ~/code). |
worktree-audit --prune |
Remove SAFE worktrees + clear stale registrations. |
worktree-audit --backup [--prune] |
Archive REVIEW worktrees to git bundles, then optionally prune the archived ones. |
Environment variables
| Var | Default | Meaning |
|---|---|---|
KEEP_DAYS |
30 |
Age gate for the deep tier. |
KEEP_RECENT |
5 |
Always keep the N newest entries per category. |
WARN_GB |
20 |
diskguard reclaims + notifies below this. |
CRIT_GB |
15 |
diskguard posts an urgent notice below this; memguard uses it as the disk floor for its coupling alert. |
FREE_WARN_PCT |
15 |
memguard warns when free memory is at/below this %. |
FREE_CRIT_PCT |
5 |
memguard posts an urgent (jetsam-imminent) notice at/below this %. |
NOTIFY_COOLDOWN |
1800 |
memguard seconds between repeat same-level banners. |
COUPLING_NOTIFY_COOLDOWN |
300 |
memguard seconds between repeat banners while the disk↔swap coupling note is active — tighter than NOTIFY_COOLDOWN on purpose. |
RECLAIM_COOLDOWN |
900 |
memguard minimum seconds between its own auto-triggered safe reclaims in the coupling quadrant. 0 disables auto-reclaim (notify-only, prior behavior). |
WORKTREE_BACKUP_DIR |
~/.local/share/worktree-backups |
Where worktree bundles land. |
WORKTREE_ROOTS |
unset | Constrain worktree-audit when no positional roots are passed. If set, defaults are not scanned; a nonexistent path yields no repos instead of falling back. |
Allowlist paths from deep pruning in ~/.config/mac-reclaim/keep.txt (one substring per line).
Age alone is a bad signal (a project you use weekly but not in 30 days shouldn't vanish). --deep handles state as follows:
- VS Code
workspaceStoragewhoseworkspace.jsonpoints to a folder that no longer exists is reported as REVIEW/protected;mac-reclaimnever removes it until an archive-first backup and explicit verified prune workflow exists. If the project folder is still there, it's kept regardless of age. - The only items
--deepremoves arevm_bundles/local-agent-mode-sessions/.claude/projectsthat are idle pastKEEP_DAYS, beyond the newestKEEP_RECENT, not currently open (lsof), and not matched by~/.config/mac-reclaim/keep.txt.
Preview it first — this deletes nothing: mac-reclaim --deep --dry-run.
REVIEW worktrees hold work that exists nowhere else — so you can't safely prune them to reclaim disk. --backup fixes that by archiving them losslessly first:
worktree-audit --backup # list REVIEW worktrees, pick which to archive
worktree-audit --backup --prune # archive the picked ones, then remove them
worktree-audit --backup --yes # archive ALL REVIEW worktrees, no promptSelection accepts indices, ranges (1-3,5), or all. Each backup is an incremental git bundle (only the commits unique to that worktree — small, since the base stays in the surviving repo), verified with git bundle verify before it counts. If the worktree is dirty, the uncommitted diff and untracked files are captured alongside (*.uncommitted.patch, *.untracked.tar.gz). A worktree is pruned only if its backup verified.
Backups land in $WORKTREE_BACKUP_DIR with a manifest.tsv recording each one and a ready-to-paste restore command. To restore into the surviving repo:
git -C <repo> fetch <bundle> 'refs/heads/<branch>:refs/heads/<branch>'
# if it was dirty, in a checkout of that branch:
git apply <stem>.uncommitted.patch
tar xzf <stem>.untracked.tar.gz -C <worktree>These solve adjacent problems well; this repo defers to them rather than shipping weaker copies.
| Tool | Why |
|---|---|
wsl-optimize |
The WSL2 sibling. On WSL the silent killer is memory (the OOM killer reaps session plumbing while protecting the hogs) plus a virtual disk that only grows. Shares worktree-audit and lib/common.sh with this repo. |
agent-machine-lib |
The shared bash primitives both repos vendor: platform detection, deletion guards, and the safe-tier cache reclaim. Refresh with make vendor-lib. |
agent-session-kill |
Agent transcript cleanup done properly: trash-first deletion, protection lists for auth/settings/skills/memory, and coverage of Pi/OMP/Copilot Chat. mac-reclaim --deep delegates ~/.claude/projects to it when installed and only falls back to its own pruning otherwise. |
macOS (Apple Silicon or Intel). The suite is bash-first with zero runtime dependencies; the safe-tier cache reclaim and worktree-audit come from agent-machine-lib, vendored into lib/ and bin/ (not a submodule, so the zero-dependency promise holds). Two tools — codex-backup and vscode-chat-backup — are Python 3 (standard library only, already on macOS). codex-backup uses rsync and prefers Homebrew's rsync 3.x when present, falling back to the system openrsync. Homebrew, pnpm, uv, bun, and nvm are all optional: their caches are pruned only if present (each guarded by command -v). ncdu/dust (brew install ncdu dust) are optional too — diskreport --scan and ad hoc interactive digging degrade to a hint if they're missing.
make install also installs four launchd agents:
com.mac-optimize.diskguard— at login + every 3 hcom.mac-optimize.memguard— at login + every 5 mincom.mac-optimize.mac-reclaim— weekly, Sundays 11:00 (daytime, so the laptop is awake)com.mac-optimize.codex-backup— weekly, Sundays 11:30 (codex-backup backup --quiet; no-ops when the external drive isn't mounted, never deletes)
The plists are templates; install.sh fills in $HOME and a cross-arch PATH at install time, so nothing is hardcoded to one machine.
Notification permission (one-time): the first low-disk or low-memory banner may require allowing notifications for the invoking process (osascript / Script Editor) under System Settings → Notifications. No blocking dialogs are ever used, so a missed banner is cosmetic — the guard still runs and logs to ~/Library/Logs/diskguard.log / ~/Library/Logs/memguard.log.
make uninstall # unloads agents, removes deployed scripts; repo left intactskills/ ships agent-agnostic skills in the open Agent Skills format (a SKILL.md per folder — no vendor lock-in, no plugin manifest). Any skills-compatible agent (Claude Code, Gemini CLI, Cursor, opencode, Goose, …) can load them so it knows when and how to drive these tools.
| Skill | Triggers on |
|---|---|
mac-optimize |
"clean up my mac", "free up space", low disk, slow machine, "audit git worktrees" — diagnose → safe reclaim → deep dry-run → worktree backup/prune. |
mac-optimize-setup |
"install mac-optimize", "set up disk automation", fresh-machine setup, Keychain token migration, uninstall. |
Install the skills into your agent with the skills CLI — no clone required:
npx skills add kylebrodeur/mac-optimize # interactive: pick skills + agents
npx skills add kylebrodeur/mac-optimize --list # list what's available
npx skills add kylebrodeur/mac-optimize --skill mac-optimize -a claude-code -y # non-interactiveOr point your agent directly at skills/, or (Claude Code) run make install-skills to symlink skills/* into ~/.claude/skills/. Validate against the spec with skills-ref validate ./skills/mac-optimize.
Full clone → install → run → maintain → uninstall verification, with assertions and STOP checks at each step: TESTING.md.
MIT © 2026 Kyle Brodeur