A portable macOS dev environment I can stand up on a fresh machine in one command.
git clone git@github.com:<you>/dotfiles.git ~/dotfiles
cd~/dotfiles
./install.shOne script, one setup: the installer installs Homebrew packages, symlinks
configs, sets up zsh, installs runtimes via mise, and wires up the AI coding
CLIs. It only prompts for what it must (git identity, default shell).
./install.sh # the whole setup
./install.sh --yes # no prompts, accept all defaults
./install.sh --link-only # just (re)create the symlinks
./install.sh --doctor # verify symlinks, tools, mise trust, git identityWith just installed (it's in the Brewfile):
just # list recipes
just link # (re)create symlinks
just doctor # health-check the setup
just update # git pull + refresh packages + relink
just upgrade # brew update/upgrade + mise upgrade (everything installed)
just new-mac # full bootstrapinstall.sh bootstrap (entry point; also --link-only / --doctor)
Justfile daily commands (just link / doctor / update / upgrade)
bootstrap/lib.sh shared shell helpers (prompts, logging, symlink)
packages/Brewfile everything: CLI toolbelt, cloud/infra, databases, GUI apps, fonts
zsh/ curated zsh (oh-my-zsh + powerlevel10k + plugins)
├── .zshrc loader
├── exports.zsh env & PATH
├── aliases.zsh aliases
└── functions.zsh shell functions
mise/config.toml global runtime versions (ruby/node/python/…)
atuin/config.toml atuin shell history (daemon mode, sync, Ctrl-R)
git/ portable gitconfig + global gitignore
cmux/settings.json cmux app settings (keybinds; font/theme inherited from ghostty)
herdr/config.toml herdr multiplexer config
zed/ Zed editor (settings + keymap + tasks; secrets stay local)
ghostty/ nvim/ app configs (symlinked into ~/.config etc.)
install.sh links repo files into place, backing up anything real it replaces
(<file>.bak-<timestamp>):
| Repo file | → Target |
|---|---|
ghostty/ | ~/.config/ghostty (cmux reads this too) |
cmux/settings.json | ~/.config/cmux/settings.json |
herdr/config.toml | ~/.config/herdr/config.toml |
nvim/ | ~/.config/nvim |
zsh/.zshrc | ~/.zshrc |
git/.gitconfig | ~/.gitconfig |
git/.gitignore | ~/.gitignore |
mise/config.toml | ~/.config/mise/config.toml |
atuin/config.toml | ~/.config/atuin/config.toml |
zed/settings.json | ~/.config/zed/settings.json (rendered via envsubst, not a symlink — see below) |
zed/keymap.json | ~/.config/zed/keymap.json |
zed/tasks.json | ~/.config/zed/tasks.json |
Two layers, and they stack — one is the window, the other tiles inside it:
- cmux (the window) — a native macOS terminal built on libghostty, tuned for
running AI agents in parallel. It reads the terminal font, colors, and theme
from
~/.config/ghostty/config, so the Dracula theme + JetBrainsMono Nerd Font are shared with Ghostty automatically. Only cmux's app-level keybinds live incmux/settings.json(split-focus mapped toopt+shift+hjklto match Ghostty). - herdr (the multiplexer) — replaces tmux/zellij. Agent-aware panes, plus persistent sessions you can detach and re-attach over SSH (even from a phone).
Ghostty stays installed as a fallback window; cmux is the daily driver.
Three untracked files hold anything that differs per machine/job:
~/.gitconfig.local— your name & email. The installer prompts for these, so you never accidentally commit with the wrong identity at a new job. Also the place for commit signing (see the comment ingit/.gitconfig).~/.zshrc.local— work paths, secrets, per-machine overrides. Sourced last.ghostty/local— window geometry & anything monitor-specific. Optional (config-file = ?local), gitignored, loaded last so it wins.
One tracked file needs care: zed/settings.json ships with ${ZED_*}
placeholders in its context_servers block (Zed has no env-var interpolation
of its own). just link renders it through envsubst into
~/.config/zed/settings.json, filling those in from your shell environment —
export the real values in ~/.zshrc.local:
export ZED_DATABASE_URL="postgresql://user:pass@localhost:5432/dbname"export ZED_GITHUB_PAT="ghp_..."export ZED_CONTEXT7_API_KEY="ctx7sk-..."The rendered file is the one Zed actually reads and writes, so edit the repo's
zed/settings.json template for structural changes, then re-run just link
to re-render (install.sh --doctor flags stale/unrendered placeholders).
Installed by the AI step: Claude Code & Codex (Homebrew casks) and
pi (pi.dev, via npm -g @earendil-works/pi-coding-agent).
git clone git@github.com:<you>/dotfiles.git ~/dotfiles && cd ~/dotfiles(the firstgitcall triggers the Xcode CLT install on a clean Mac)./install.sh(it also installs Xcode CLT & Homebrew if missing)- Open a new terminal (
exec zsh) - Open
nvimonce so lazy.nvim installs the pinned plugin versions (lazy-lock.json) atuin import zshto seed history;atuin loginto sync it from other machines (the e2e key lives in~/.local/share/atuin/key— grab it from an old machine withatuin key)just doctorto confirm everything landed