Turn a fresh Mac (or an existing one) into a repeatable, documented environment: shell, Homebrew stack, language toolchains, and optional AI/DevOps extras — driven by plain-text inventory, not one-off scripts you forget to re-run.
Who this is for: developers and platform engineers who want a single workflow to install, reconcile, and audit tooling — similar in spirit to infrastructure-as-code, applied to a laptop.
| Outcome | How |
|---|---|
| Predictable installs | Desired packages live in inventory/ (Brewfile, lists, manifests). |
| See before you change | macctl plan shows what is missing vs inventory; apply installs only the gap. |
| Safe adoption of an existing Mac | macctl import merges what is already installed into inventory (with backups); nothing is uninstalled. |
| Health checks | macctl doctor, macctl lint (ShellCheck), and optional bootstrap/post-setup.sh. |
| Shell + SRE ergonomics | Zsh config, helpers, and guided wizards under config/zsh/. |
macctl is the product surface: one binary-style entrypoint (bin/macctl) that loads a small engine (core/) and plugins (modules/). You do not need to remember which script installs what.
plan Diff inventory vs this Mac (stderr); use --only=brew,python,... to scope
apply Install missing items; --dry-run logs commands without running them
import Merge detected installs into inventory; --dry-run to preview; backups under state/import-backups/
sync Refresh state snapshots; add --pull-inventory to run import after snapshots
doctor Quick sanity checks (paths, brew, optional module hooks)
lint Run ShellCheck on shell entrypoints (requires shellcheck on PATH)
Flags can appear before or after the subcommand (for example macctl --only=brew plan). Run from the repo root or call ~/.dotfiles/bin/macctl with an absolute path.
Architecture and extension points: docs/MACCTL-ARCHITECTURE.md · Import behavior: docs/IMPORT.md · Day-two usage: docs/USAGE.md.
-
Clone to the canonical path (
.zshrcexpectsDOTFILES=$HOME/.dotfiles):git clone https://github.com/erasmo-dominguez-stuff/macctl ~/.dotfiles cd ~/.dotfiles
-
Run setup (installs Homebrew / Oh My Zsh if needed, symlinks
config/into$HOME, runs phasedmacctl apply):chmod +x install.sh bin/macctl bootstrap/*.sh modules/*.sh ./install.sh
-
Open a new shell, then optionally
./bootstrap/post-setup.shandmackup restoreif you use Mackup.
-
Clone the repo (same path as above).
-
Preview, then merge live state into inventory:
./bin/macctl import --dry-run ./bin/macctl import
-
Review diffs, then align the machine to the merged inventory when you are ready:
./bin/macctl plan ./bin/macctl apply --dry-run ./bin/macctl apply
- Refresh snapshots:
./bin/macctl sync(add--write/--pull-inventoryas needed; seedocs/USAGE.md). - Tighten shell quality:
./bin/macctl lint. - Edit lists under
inventory/and re-runplan/applyfor the modules you changed.
- Platform: Homebrew (formulae, casks, MAS, VS Code extensions via Brewfile + lists).
- Languages & runtimes: Python (pip inventory), Node globals, Go-related brew lines,
uv/ pyenv flow via bootstrap where enabled. - Editor: Vim / vim-plug path via the
vimmodule (seedocs/USAGE.mdfor markers likestate/vim-plugins.ok). - AI / DevOps add-ons: Curated
iainventory (brew +uv piplists); extend withinventory/ia/manifest.txtanduv-packages.txt. - Shell & productivity: Zsh, themes, aliases, SRE-oriented helpers and wizards under
config/zsh/. - Optional: Mackup for app settings;
.pre-commit.ymlfor repo hygiene when you work inside this tree.
Deep inventory map: inventory/TOOLS.md.
| Area | Role |
|---|---|
bin/macctl |
CLI entry → core/main.sh |
core/ |
Engine: helpers, registry, runner, module-order, import.sh |
modules/ |
One plugin per ecosystem (brew, golang, python, node, vim, ia) |
inventory/ |
Desired state (the source of truth for reconciliation) |
config/ |
Files symlinked into $HOME (zsh, git, mac prefs) |
bootstrap/ |
First-run and maintenance scripts |
state/ |
Snapshots and import backups (usually gitignored) |
Canonical Brew inventory lives at inventory/brew/Brewfile (no root-level duplicate file). See the table above for the mental model; file-level detail stays in docs/MACCTL-ARCHITECTURE.md.
- Packages & apps:
inventory/brew/Brewfile,inventory/brew/casks.txt, and otherinventory/*lists. - Shell:
config/zsh/.zshrc,config/zsh/helpers/,config/zsh/wizards/. - Git / macOS defaults:
config/git/,config/mac/.macos(apply macOS prefs manually when you want them).
- Pre-commit failures: follow the hook output (formatters, linters, etc.).
- Paths: keep the clone at
~/.dotfilesunless you intentionally retargetDOTFILESin your shell. - Diagnostics:
brew doctor,./bin/macctl doctor,./bin/macctl lint,./bootstrap/post-setup.sh. - AI tooling issues: confirm
brew/uvpieces frominventory/ia/and re-runmacctl plan --only=ia.
-
uv and pre-commit are wired through bootstrap and inventory where enabled; adjust
.pre-commit.ymlfor your team. -
Agent-style stacks (Python / Go): this repo inventories common libraries and CLIs; heavy custom builds (for example compiling
llama.cpp) stay manual by design. -
Quick checks:
./bin/macctl apply --only=python,ia ./bin/macctl apply --only=vim
brew install mackup
mackup backupDefaults and options: Mackup documentation.
MIT — see LICENSE.md.