Skip to content

Latest commit

 

History

35 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mac-optimize

License: MIT Platform: macOS Shell: bash Dependencies: none Agent Skills

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.

Install

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 install

Pure 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.

Quickstart

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.

Tools

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.

How it works

Four principles, in order of trust:

  1. Observable before action. diskreport answers "what's using my disk" without touching anything. Diagnose first.
  2. Safe by construction. The default mac-reclaim only clears caches the owning tool rebuilds on demand — pnpm/uv prune only unreferenced packages; npm's _cacache is a re-download cache; installed node_modules are never touched. It cannot remove something you're using.
  3. 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 Code workspaceStorage is 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.
  4. Bounded automation. diskguard runs 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. memguard is 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 tier diskguard uses, 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.

Options

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).

Why mac-reclaim --deep is trustworthy

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 workspaceStorage whose workspace.json points to a folder that no longer exists is reported as REVIEW/protected; mac-reclaim never 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 --deep removes are vm_bundles / local-agent-mode-sessions / .claude/projects that are idle past KEEP_DAYS, beyond the newest KEEP_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.

Backing up REVIEW worktrees (worktree-audit --backup)

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 prompt

Selection 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>

Related tools

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.

Requirements

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.

Automation & uninstall

make install also installs four launchd agents:

  • com.mac-optimize.diskguard — at login + every 3 h
  • com.mac-optimize.memguard — at login + every 5 min
  • com.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 intact

Agent skills

skills/ 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-interactive

Or 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.

Testing

Full clone → install → run → maintain → uninstall verification, with assertions and STOP checks at each step: TESTING.md.

License

MIT © 2026 Kyle Brodeur

About

macOS disk, memory & shell hygiene for machines running many AI coding agents: safe disk reclaim, git-worktree audit/backup, Codex-session backup, and launchd automation. Bash-first, agent-agnostic Agent Skills.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages