macOS dotfiles managed with GNU Stow and Homebrew. Safe to re-run anytime -- every step is idempotent.
| Category | Configs |
|---|---|
| Shell | fish, readline |
| Git | git, delta, jj |
| Editor | neovim, zed |
| Terminal | ghostty, tmux |
| SSH | 1Password SSH agent |
| aerc, isync, msmtp, notmuch | |
| AI | claude, codex, opencode |
| Automation | hammerspoon, karabiner |
| Media | newsboat, pianobar |
-
Sign in to iCloud in System Settings → Apple ID. The bootstrap installs Mac App Store apps, which requires an active Apple ID.
-
Install and open 1Password.app, sign in, and enable the SSH agent: Settings → Developer → "Use the SSH agent". Step 4 of the bootstrap (
03-1password.sh) signs in to the 1Password CLI and verifies the SSH agent socket — it will warn but not fail if the app isn't running yet, but SSH won't work until it is. -
Open Terminal.app (the only tool available on a stock Mac).
xcode-select --install # if you want git before the bootstrap handles it
git clone https://github.com/ewilliam/dotfiles.git ~/Projects/dotfiles
cd ~/Projects/dotfiles
bash bootstrap.shClone over HTTPS since SSH isn't available yet — the bootstrap installs 1Password, configures the SSH agent, and sets up ~/.ssh/config for you. After bootstrap, pushes and future clones will use SSH automatically.
The bootstrap runs eight steps in order:
| Step | Script | What it does | Sudo? |
|---|---|---|---|
| 1 | 00-xcode.sh |
Install Xcode Command Line Tools | No |
| 2 | 01-brew.sh |
Install Homebrew, run brew bundle (~90 packages) |
Yes |
| 3 | 02-stow.sh |
Symlink all configs from stow/ into $HOME |
No |
| 4 | 03-1password.sh |
Sign in to 1Password CLI, verify SSH agent | No |
| 5 | 04-shell.sh |
Set fish as default shell, install Fisher plugins | Yes |
| 6 | 05-tools.sh |
Install mise runtimes (Node, Python, Erlang, Elixir, pnpm, uv) | No |
| 7 | 06-mas.sh |
Install Mac App Store apps | No |
| 8 | 07-macos.sh |
Apply ~80 system defaults (Finder, Dock, keyboard, etc.) | Yes |
You'll be prompted for your computer name during step 8 (defaults to ewa-mbp).
Expect ~15–30 minutes. The Erlang compile is the slowest part.
Restart your Mac. Keyboard repeat rate, hot corners, and several system defaults won't take effect until you do.
Then handle the things that can't be automated:
Enable the 1Password SSH agent — open 1Password → Settings → Developer → "Use the SSH agent". The bootstrap already configured ~/.ssh/config to point to it. Once enabled, switch your dotfiles remote to SSH:
cd ~/Projects/dotfiles
git remote set-url origin [email protected]:ewilliam/dotfiles.gitAuthenticate the GitHub CLI — the bootstrap installs gh but doesn't log in. Sign in to your account:
gh auth login # follow the prompts for ewilliamOnce accounts are added, switch between them anytime with the fish helpers:
ghme # switch to ewilliam ([email protected])
ghwho # show current userThis updates both gh auth and git config --global (user.name, user.email, github.user) in one step. The .gitconfig is also wired to use gh auth git-credential for HTTPS pushes, so once you're logged in credentials just work.
Populate ~/.secrets.fish — the bootstrap creates this file empty. Add API keys and tokens your tools need:
set -gx ANTHROPIC_API_KEY "sk-ant-..."
set -gx OPENAI_API_KEY "sk-..."Set up aerc email — copy the template and fill in your credentials:
cp ~/.config/aerc/accounts.conf.template ~/.config/aerc/accounts.conf
$EDITOR ~/.config/aerc/accounts.confThe template uses op item get to pull passwords from 1Password at runtime.
Grant macOS permissions — you'll get prompted on first launch, but proactively grant these in System Settings → Privacy & Security:
| Permission | Apps |
|---|---|
| Accessibility | Hammerspoon, Karabiner-Elements, Raycast |
| Input Monitoring | Karabiner-Elements |
| Full Disk Access | Ghostty |
| Screen Recording | Hammerspoon |
Sign into apps that need manual authentication: Arc, Chrome, Firefox, Slack, Discord, Teams, Dropbox, Backblaze, Notion, Obsidian, Steam, ChatGPT, Claude.
Re-pin Dock apps — the bootstrap clears all pinned Dock apps for a clean slate. Drag back whatever you want, or leave it empty and let running apps fill it.
cd ~/Projects/dotfiles
git pull
bash bootstrap.shRelay is a Bun + TypeScript CLI for running implementation plans one slice at a time. See tools/relay/README.md for the full guide.
relay install restows the relay package, which manages ~/.local/bin/relay and ~/.agents/skills/relay-plan; the package's bin entry points to tools/relay/bin/relay.ts.
Common commands:
relay lint-plan --repo ~/Projects/dotfiles --plan docs/plans/2026-05-21-relay.md
relay --repo ~/Projects/dotfiles --plan docs/plans/2026-05-21-relay.md --verify "bun test tools/relay/test"
relay --repo ~/Projects/dotfiles --plan docs/plans/feature.md --codex-timeout 1h --pr
relay install.
├── bootstrap.sh # entry point
├── Brewfile # homebrew dependencies
├── setup/
│ ├── lib.sh # shared helpers (logging, spinner)
│ ├── 00-xcode.sh # xcode command line tools
│ ├── 01-brew.sh # homebrew + brew bundle
│ ├── 02-stow.sh # gnu stow symlinks
│ ├── 03-1password.sh # 1password cli + ssh agent
│ ├── 04-shell.sh # fish shell + fisher plugins
│ ├── 05-tools.sh # mise runtimes
│ ├── 06-mas.sh # mac app store
│ └── 07-macos.sh # macos system defaults
└── stow/ # symlinked configs
├── aerc/
├── fish/
├── ghostty/
├── git/
├── hammerspoon/
├── karabiner/
├── newsboat/
├── nvim/
├── pianobar/
├── readline/
├── ssh/
├── tmux/
└── zed/
Vi keybindings everywhere: fish, readline, neovim, zed, aerc, newsboat, tmux.
| Binding | Action |
|---|---|
Cmd+Alt + H/J/K/L |
Push window left/down/up/right |
Cmd+Alt + Y/U/I/O |
Smart resize left/down/up/right |
Cmd+Alt + M |
Maximize window |
Cmd+Alt + F |
Toggle fullscreen |
Cmd+Alt + [ / ] |
Decrease / increase grid |
Ctrl+Space then key |
Launch app — arc, chrome, dash, e=zed, finder, g=tower, messages, safari, t=ghostty |
| Binding | Action |
|---|---|
Caps Lock hold |
Control |
Caps Lock tap |
Escape |
Fn + I/J/K/L |
Arrow keys (up/left/down/right) |
| Abbr | Expands to |
|---|---|
vi |
nvim |
cc |
claude |
cx |
codex |
oc |
opencode |
lzg |
lazygit |
gst |
git status |
gco |
git checkout |
pnd |
pnpm dev |
muxn |
tmux new-session -s |
frl |
reload fish config |
emacs |
echo lmao |