A one-shot setup for a fresh Mac. Run one script and you get a fast terminal, a polished shell, modern CLI tools, polyglot language runtimes, container tooling, and local + agentic AI tools. Everything is chosen to be lightweight and current as of mid-2026.
Clone (or copy) this folder anywhere, then:
chmod +x setup.sh
./setup.shRun it in a local, interactive terminal — the first run installs Homebrew, which asks for your password once.
The script is idempotent — safe to run again anytime. It checks before
installing, refreshes only the configs it owns (a config you've replaced with a
real file is yours — it's skipped and reported, never clobbered; see
Extending below), and maintains a single marked block in ~/.zshrc that
re-runs refresh in place — so re-running ./setup.sh always delivers the
latest shell config too.
It also self-lints: it installs shellcheck and runs it against itself at the
end (non-fatal — warnings print but never abort the run). Skip that pass with
SKIP_SELFCHECK=1 ./setup.sh.
When it finishes: quit and reopen your terminal (or open Ghostty) so the shell changes load.
| File | Goes to | What it is |
|---|---|---|
setup.sh |
— | The installer. Run this. |
starship.toml |
~/.config/starship.toml |
Prompt config (git, language versions, k8s context). |
ghostty.config |
~/.config/ghostty/config |
Terminal font, theme, keybinds. |
zellij.kdl |
~/.config/zellij/config.kdl |
Terminal multiplexer config. |
zshrc-additions.zsh |
sourced from ~/.zshrc |
Shell init (mise, starship, zoxide, atuin, fzf, aliases). |
gitconfig-devbook |
included from ~/.gitconfig |
Git defaults: delta pager, zdiff3, modern QoL flags. |
AGENTS.md |
imported by ~/.claude/CLAUDE.md |
Machine-wide brief for AI coding agents: which tools are here and how to use them. |
MODELS.md |
— | Model-sovereignty posture: local-first AI, swappable vendors. |
HISTORY.md |
— | Sparse major-milestone history; routine detail remains in Git history. |
agent-vault/ |
— | Optional pinned local Agent Vault deployment; keeps configured API credentials out of agent transcripts. |
machine-steward-reviewer.md |
— | Reusable reviewer persona for infra changes (see below). |
CLAUDE.md + .claude/agents/ |
— | Wires Claude Code into this repo's conventions and reviewer. |
githooks/pre-commit |
wired via core.hooksPath |
Sentinel leak gate: blocks commits matching private patterns (the patterns live outside this repo — see Extending). |
setup.sh symlinks the configs into place for you (staged via ~/.dotfiles),
and wires gitconfig-devbook in with a single reversible include.path line —
your own ~/.gitconfig settings always win over it. The staged copies in
~/.dotfiles are read-only artifacts: edit this repo (public) or
~/.dotfiles.local (private), never the staged middle — a stray edit there
fails loudly instead of being silently lost on the next run.
Terminal & shell
- Ghostty — GPU-accelerated, native macOS terminal. Fast, minimal config. Your iTerm2 replacement.
- IosevkaTerm Nerd Font — open, feature-rich patched font so prompt/file icons render.
- Starship — fast, informative prompt (git state, language versions, k8s context).
- Zellij — terminal multiplexer (panes, tabs, detachable sessions). Friendlier than tmux; keybinds show on screen.
Modern CLI toolkit (drop-in upgrades for old Unix tools)
eza(ls),bat(cat),fd(find),ripgrep/rg(grep),sd(sed),dust(du),bottom/btm(top)fzffuzzy finder (ctrl-t),zoxidesmart cd (z),atuinsearchable history (owns ctrl-r)git-deltapretty diffs,lazygitgit TUI,ghGitHub CLI; Sourcetree (Git GUI)jq/yqfor JSON/YAML,direnvper-project env,tldrconcise man pagesagemodern file encryption,op(1Password CLI) for secrets in scripts- Firefox — the independent, non-Chromium browser engine: cross-engine dev testing, and an anti-monoculture daily driver (the
MODELS.mdposture, applied to browsers)
Language runtimes
- mise — polyglot version manager (replaces nvm/pyenv/rbenv/g). Rust-fast, one tool for Node, Python, Go, and Bun. Installs Node LTS, Python 3.12, Go, and Bun globally; override per-project with a
mise.toml. (Rust uses rustup; Lean uses elan.) - uv — Astral's ultrafast Python package/project manager. Use it for venvs and dependencies (
uv init,uv add,uv run). Great for ML/AI work. - elan — official Lean toolchain manager (rustup's equivalent for Lean). Installs latest stable Lean 4 globally and auto-switches to whatever a project's
lean-toolchainfile pins.
Containers / DevOps
- OrbStack — fast, light Docker Desktop replacement (Docker + a local Kubernetes). Big battery/RAM win.
kubectl,k9s(cluster TUI),helm.- Agent Vault — optional loopback-only credential broker for low-value agent API access. Enable with
INSTALL_AGENT_VAULT=1 ./setup.sh; seeagent-vault/README.mdfor the threat model and recovery contract.
AI-forward
- Ollama — run LLMs locally, vendor-free (
ollama pull qwen3-coder:30b,ollama run ...). Your sovereign default; seeMODELS.mdfor picks by RAM tier. - Claude Code — Anthropic's agentic CLI, installed via the official native installer (signed, auto-updating, no Node dependency). Run
claudein any repo. The one intentional vendor tool, balanced by keeping everything else local-capable. - Zed — fast, AI-native editor (its assistant can point at your local Ollama too).
- Agent context (
AGENTS.md) — a machine-wide brief that tells any coding agent which tools are installed here and how you expect them used (mise, uv,rg/fd, OrbStack,op, …). Wired into Claude Code globally; fan it out per-repo withagents-here. See below. - No cloud-agent CLI is installed by default — add a neutral one later (e.g. opencode: open source, bring-your-own-model), pointed at Ollama first. See
MODELS.md.
- Ghostty font: if it didn't auto-apply, set font to
IosevkaTerm Nerd Font(already inghostty.config). - git: nothing to do —
setup.shwiresgitconfig-devbook(delta pager, zdiff3 conflicts,push.autoSetupRemote, rerere, histogram diffs) into your global config via oneinclude.pathline. To undo it all:git config --global --unset include.path ~/.dotfiles/gitconfig-devbook. - mise: run
mise doctorto confirm it's wired up. Per project,mise use node@22etc. writes amise.toml. - atuin (optional):
atuin registerto sync/encrypt history across machines, or just use it locally. - Ollama:
ollama pull qwen3-coder:30b(32 GB RAM; seeMODELS.mdfor the 16 GB and 64 GB+ picks). - Claude Code: run
claudein a project and follow the login prompt. - Agent Vault (optional): start OrbStack, run
INSTALL_AGENT_VAULT=1 ./setup.sh, save the generated master password in 1Password without printing it, then complete the one-time setup inagent-vault/README.md.
- Node/TS:
mise use node@lts→npm create vite@latestorpnpm, etc. - Bun: available globally via mise; pin a project with
mise use bun@<version>. - Python/ML:
uv init myproj && cd myproj && uv add pandas torch→uv run python .... No manual venv activation. - Go:
mise use go@latest→go mod init. - Rust: rustup already set stable as default →
cargo new. - Lean:
lake new myproj→lake build. elan picks the toolchain from the project'slean-toolchainfile automatically. - Env vars/secrets: drop a
.envrcin the project (direnv allow) — auto-loads oncd.
AI coding tools each read their own instruction file, so the same environment
facts get repeated — or, worse, an agent guesses wrong (reaches for pip,
assumes Docker Desktop, greps instead of rg). AGENTS.md is the single
source of truth: behavioral rules, not an inventory — how this machine
expects tools to be used.
- Machine-wide (automatic):
setup.shstagesAGENTS.mdto~/.dotfiles/AGENTS.mdand adds one reversible marked block to~/.claude/CLAUDE.mdthat@-imports it, so Claude Code loads it in every session, in every repo. Your own global memory in that file is left intact; delete the<!-- >>> dev-env agents >>> -->block to undo it. If a private overlay brief exists (~/.dotfiles.local/AGENTS.local.md), setup composes public + private into~/.config/dev-env/AGENTS.composed.mdand imports that instead — agents without a safe private channel simply get the public brief, because private content is never written anywhere agit pushcould publish. - Per-repo (one command): run
agents-herein a project to write a starterAGENTS.mdand point the project-scoped tools at it — Claude Code (CLAUDE.md) and GitHub Copilot (.github/copilot-instructions.md) via symlink. Zed, Cursor, and Codex readAGENTS.mdnatively, so they need nothing. Real (non-symlink) files are skipped, never clobbered.
Why only Claude Code gets the machine-global wiring: it's the one agent
devbook installs that has a true user-level memory file. Copilot, Zed, and
Cursor are project-scoped by design — agents-here is the honest way to reach
them, one repo at a time, from the same source file.
devbook is a public baseline. Anything personal — private aliases, a work
git identity, machine-specific agent context — belongs in ~/.dotfiles.local,
a private repo the baseline knows how to consume but never reads into
itself (setup.sh only ever stages files from this checkout, so private
content structurally cannot transit into the public layer).
To start one:
mkdir -p ~/.dotfiles.local && git init ~/.dotfiles.localthen add any of the files below — all optional — and re-run ./setup.sh;
the hooks notice whatever exists. On a fresh machine the order doesn't matter:
clone devbook and run it, restore or clone your overlay to ~/.dotfiles.local,
run ./setup.sh once more — both orders converge on the same state.
zshrc.zsh— sourced last by the shell init, so it overrides anything here.gitconfig— wired in as a secondinclude.path; unwired automatically if the overlay disappears.ghostty.config— pulled in by an optional include (missing file = no-op).AGENTS.local.md— composed with the public brief (see the agents section).sentinels.txt— private strings the pre-commit gate blocks from ever being committed here, one fixed string per line, matched case-insensitively (the hook ships in this repo; the patterns stay private).setup.sh— your own installer, run (contained) at the end of this one. Skip it withSKIP_LOCAL=1 ./setup.sh.
Three layers, three fates: this repo is the public source (GitHub protects
it), ~/.dotfiles.local is the private source (your backups protect it),
and ~/.dotfiles + ~/.config/dev-env are disposable artifacts —
re-running regenerates them, so protect nothing there. Whole-file configs with
no include mechanism (starship, zellij) use the takeover contract instead:
replace the symlink with a real file to own it — setup then skips it and
reports the divergence (also into ~/.config/dev-env/manifest.txt); delete
your file and re-run to return to baseline. Removing the overlay entirely and
re-running restores the exactly-public state: composite deleted, include
unwired, hooks silent (to unwire the leak-gate hook itself:
git config --unset core.hooksPath in this checkout).
dev-up(alias installed by the zshrc block) refreshes everything in one go: brew packages, mise runtimes, and the Rust toolchain.- Re-running
./setup.shanytime picks up new additions to this repo — it's idempotent. - Agent Vault is opt-in and stateful: re-run
INSTALL_AGENT_VAULT=1 ./setup.shto reconcile its pinned container, and preserve both~/.config/dev-env/agent-vault/.envand thedevbook-agent-vault-datavolume when migrating. Never place credentials in the repo; the complete local procedure is inagent-vault/README.md.
This repo carries its own reviewer. machine-steward-reviewer.md is a
steward + conservator persona for auditing infra changes, and
.claude/agents/machine-steward.md registers it as a Claude Code subagent —
so inside claude, any change to setup.sh or the dotfiles gets reviewed for
correctness, idempotency, reversibility, and key-custody issues before it
ships. CLAUDE.md makes that the default workflow rather than a thing to remember.
- Future baseline candidate:
ruff. Python repositories commonly make it a CI gate, and a missing global binary forces an otherwise unnecessaryuvxdownload during local validation. It is small, broadly useful, and available as a Homebrew formula; consider adding it to a futuresetup.shupdate after the usual idempotency and README-sync review. - Future agent-workflow candidate:
pecia. Local dogfood found its deterministic, append-only work ledger useful for long-running roadmap custody. Do not baseline it yet: Pecia v2'sinitanddoctorcurrently disagree about the merge attribute, and adopter repositories still need a portable hook and distribution contract. Reconsider after those are resolved and the usual idempotency, clean-export, and README-sync checks pass. - Prefer WezTerm over Ghostty? Replace the Ghostty line with
brew_cask weztermand skipghostty.config. - Don't want Zed? Delete the
brew_cask zedline. - Already have a browser you love? Delete the
brew_cask firefoxline. - Want tmux instead of Zellij? Swap
brew_formula zellijforbrew_formula tmux(configs differ). - Everything here is Homebrew-managed:
brew upgradekeeps it all current.