agentleFS
PaldomPaldom/mac-os-setup-skills/skills/mac-maintenance/SKILL.md

mac-maintenance

Skill0 starsChanged 3 months agoMITView on GitHub

Runs recurring update maintenance on a developer Mac - brew update/upgrade with cask auto_updates semantics, cleanup, Brewfile re-capture, mas and macOS updates, Oh My Zsh and runtime managers, safe disk reclaim. Use for "update everything", "upgrade brew packages", "run maintenance", "why didn't brew update chrome". Not for first-time installs, broken Homebrew, or project dependency bumps.

  • Installs packages

3 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • 0

    Common

    Most of the 3 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Timing check: ask/warn if the user is mid-release or pre-demo — upgrade waves before deadlines are self-inflicted outages.

    line 23
  2. Very specific:

    Never blind-upgrade databases across majors — versioned formulae (postgresql@17) stay within their major, but unversioned installs …

    line 28
  3. Very specific:

    Before upgrading, scan the outdated list for major-version-sensitive packages: database services installed via *unversioned* formulae …

    line 45
  4. Very specific:

    Ask where the tracked Brewfile lives (~/Brewfile or ~/dotfiles/Brewfile), then update it via candidate + diff — a blind dump --force …

    line 67
  5. Very specific:

    Read-only first: docker system df, brew cleanup --dry-run, du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null.

    line 108

“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Mac Maintenance

The recurring "keep it current" pass, run safely: show before changing, respect self-updating apps and pinned majors, never surprise-restart the machine. Designed to be re-run weekly/monthly — every step is idempotent.

When NOT to use

  • First-time setup of anything → the respective setup skill
  • Broken brew (doctor errors) → homebrew-setup
  • Project dependency upgrades (package.json, lockfiles) → project work
  • macOS major-version migration → manual, deliberate; only point at System Settings

Safety rails

  • Timing check: ask/warn if the user is mid-release or pre-demo — upgrade waves before deadlines are self-inflicted outages.
  • Show brew outdated before brew upgrade; list, then act.
  • softwareupdate --install may restart — explicit consent, never --restart unattended; prefer --recommended --os-only for scripts.
  • Never blind-upgrade databases across majors — versioned formulae (postgresql@17) stay within their major, but unversioned installs (postgresql alias, mysql) can jump and strand the data dir (pg_upgrade territory). Never brew bundle cleanup --force or docker system prune without showing the removal list first.
  • Report what changed, what was skipped and why.

Workflow

1. Homebrew core pass

brew update                 # refresh brew + metadata (not the packages)
brew outdated               # SHOW the user this list first
brew services list          # data-bearing services in the outdated list?

Before upgrading, scan the outdated list for major-version-sensitive packages: database services installed via unversioned formulae (postgresql alias, mysql, mongodb-*) can jump majors and strand data (versioned postgresql@17 stays within 17.x — that's why we pin). Exclude those from the pass (brew pin <formula> makes it stick) or handle them deliberately with the user. Then:

brew upgrade                # upgrade unpinned formulae + non-auto-update casks
brew cleanup --dry-run && brew autoremove --dry-run   # preview what goes
brew cleanup && brew autoremove                        # then reclaim
brew doctor                 # advisory warnings are fine; errors are not

Cask semantics — the perennial confusion: casks marked auto_updates (Chrome, VS Code, Slack, …) are skipped by design — the apps update themselves. Forcing brew ownership: brew upgrade --cask --greedy (or the narrower --greedy-auto-updates) — usually unnecessary; default to letting self-updaters work.

2. Brewfile re-capture (if the user keeps one)

Ask where the tracked Brewfile lives (~/Brewfile or ~/dotfiles/Brewfile), then update it via candidate + diff — a blind dump --force flattens curated comments and captures accidental installs:

BF=~/Brewfile   # or ~/dotfiles/Brewfile - the user's tracked copy
brew bundle check --file="$BF" || echo "drift"
brew bundle dump --describe --file=/tmp/Brewfile.candidate
diff -u "$BF" /tmp/Brewfile.candidate     # user approves, then replace $BF

3. App Store + macOS

mas outdated && mas update            # App Store apps (signed-in App Store required)
softwareupdate --list                 # read-only
# with consent + restart warning:
sudo softwareupdate --install --recommended --os-only

4. Shell & runtime layer (detect, then update what exists)

[ -d ~/.oh-my-zsh ] && zsh -ic 'omz update'                 # omz is a shell function - needs interactive zsh
command -v mise >/dev/null && { brew upgrade mise 2>/dev/null; mise outdated; mise upgrade; }
[ -d ~/.nvm ] && echo "nvm: on request - nvm install 'lts/*' --reinstall-packages-from=default (new version, migrates globals; old versions stay until nvm uninstall)"
command -v pyenv >/dev/null && echo "pyenv: new Python versions are installed, not upgraded - pyenv install <ver> on request"
command -v uv >/dev/null && uv tool upgrade --all           # uv itself updates via brew
command -v pipx >/dev/null && pipx upgrade-all
command -v rustup >/dev/null && rustup update               # toolchains; rustup itself via brew
command -v gh >/dev/null && gh extension upgrade --all
npm outdated -g 2>/dev/null; echo "npm -g: review the list above (outdated exits non-zero when items exist), then npm update -g"

Rules: managers update within configured ranges (mise respects mise.toml; pnpm respects packageManager pins) — don't bump project pins here. gcloud installed via brew cask updates via brew, not components update (dual-ownership drift).

5. Disk reclaim (on request)

Read-only first: docker system df, brew cleanup --dry-run, du -sh ~/Library/Developer/Xcode/DerivedData 2>/dev/null. Then safe reclaims: brew cleanup -s, npm cache verify. Destructive tier — show the list, then confirm: docker system prune (volumes excluded by default; --volumes deletes data — treat as data loss); xcrun simctl list devices unavailable then xcrun simctl delete unavailable (irreversibly deletes those simulators and their app data); DerivedData deletion (rebuild cost).

6. One-command habit (optional)

brew install topgrade runs ~everything (brew, mas, npm, rustup, mise, uv, omz, gh, …). Ship it with a reviewed config, not defaults:

# ~/.config/topgrade.toml
[misc]
disable = ["system", "mas"]   # OS + App Store stay manual/consented
[brew]
autoremove = true

topgrade --dry-run first. Prefer the manual routine above when the user dislikes meta-tools.

7. Report

Summarize: upgraded (with notable version jumps), skipped (auto-update casks, pinned majors, deferred OS update), disk reclaimed, and anything that now needs a new shell/restart.

Output spec

Done means: the pass ran in the order above with consents honored, brew doctor is clean-or-advisory, Brewfile matches reality (if kept), and the user got the change report. Nothing pinned or major-version -sensitive moved without explicit approval.

Gotchas

  • brew update vs upgrade naming trips apt users — update = metadata, upgrade = packages.
  • brew auto-updates itself before installs (24h cadence) — a "quick install" sometimes triggers a long metadata refresh; that's normal.
  • mas update needs the App Store signed in; it can't purchase.
  • nvm install --lts installs a new version — old ones remain until nvm uninstall; global packages migrate only with --reinstall-packages-from.
  • uv self update fails on brew installs by design (standalone-installer feature) — brew upgrade uv.
  • Post-upgrade breakage checklist: xcode-select -p after macOS updates (CLT pointer), reshim after runtime-manager changes, restart brew services if a database minor bumped.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.