Fresh-machine dev setup

A reproducible setup log for a new Mac. Each step has a copyable command — run them top-to-bottom and you'll end up with the same environment. This worked for me on my machine on the date below; tools drift, so your mileage may vary.

macOS · Apple Silicon · shell: zsh + Oh My Zsh + Starship · last verified: 2026-05-19

Foundation

1. Xcode Command Line Tools

Provides git, clang, make, and the macOS SDK headers that virtually every native build needs. Homebrew itself depends on this. A GUI prompt will appear — accept it.

xcode-select --install

2. Homebrew

The macOS package manager. Everything else CLI-shaped flows through it.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Then add brew to your PATH (Apple Silicon installs to /opt/homebrew):

echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

Applications

3. Ghostty — terminal emulator

GPU-accelerated terminal with platform-native UI. Replaces Apple's Terminal.app. Use this from here on instead of Terminal.app for the steps below.

brew install --cask ghostty

4. Zed — code editor

Fast, Rust-based multiplayer editor. Open files from the terminal with zed . after install.

brew install --cask zed

5. Raycast — launcher · clipboard · window mgmt · AI

Spotlight replacement that grows into a launcher, clipboard history, window manager, snippet manager, calculator/converter, and AI scratchpad. Free for personal use. Universal Mac dev productivity boost — almost everyone who installs it wonders how they lived without it.

brew install --cask raycast

After install, open Raycast and let it take over +Space (it'll guide you through disabling macOS Spotlight's hotkey). A few extensions worth enabling immediately:

Shell tooling

6. Modern CLI tools

Eight tools installed in one go. What they do:

brew install eza bat fd ripgrep fzf zoxide git-delta starship

7. Oh My Zsh

Plugin/theme framework for zsh. The flags below run the installer non-interactively so it doesn't try to switch shells or drop you into a new zsh session.

RUNZSH=no CHSH=no KEEP_ZSHRC=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended

8. zsh plugins — autosuggestions + syntax-highlighting

Adds grey ghost-text suggestions and red/green command coloring.

git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions \
  ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting \
  ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting

Node toolchain

9. nvm — Node Version Manager

Per-shell, per-project Node version switching. Installed via the official script (do not use brew's nvm — caveats are painful).

Heads up: the version tag in the URL may have advanced. Check nvm-sh/nvm releases for the latest before running.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash

The installer appends the nvm loader to ~/.zshrc. After running it, open a new terminal so nvm resolves.

10. Node.js & pnpm

Install the latest LTS Node, then add pnpm as your default package manager.

nvm install --lts
nvm alias default 'lts/*'
npm install -g pnpm

Python toolchain

11. uv — Python project & version manager

Modern Rust-based Python toolchain from Astral. Single binary that replaces pip, pipx, poetry, pyenv, venv, and twine. 10–100× faster than pip. Handles Python version installs, virtualenvs, dependency locking, and script execution.

Install uv:

brew install uv

Install a default Python so python / python3 resolve globally to a modern version (macOS only ships an old 3.9 at /usr/bin/python3 and locks it behind PEP 668):

uv python install --default 3.13

The --default flag is marked experimental in uv's docs — works today, but the interface may change.

Useful starting points:

Shell config

12. ~/.zshrc

This file wires everything together: loads Oh My Zsh, enables the plugins, sets up Starship, aliases the modern tools to their classic names, and configures sensible history defaults.

Paste this entire block into ~/.zshrc, replacing whatever Oh My Zsh's installer left there:

# ---------- PATH ----------
export PATH="$HOME/.local/bin:$PATH"

# ---------- Oh My Zsh ----------
export ZSH="$HOME/.oh-my-zsh"

# Theme is empty because Starship handles the prompt (see below).
# Swap to "robbyrussell" or "agnoster" if you want OMZ's prompt instead.
ZSH_THEME=""

plugins=(
  git
  zsh-autosuggestions
  zsh-syntax-highlighting   # must be last
)

source "$ZSH/oh-my-zsh.sh"

# ---------- Prompt ----------
eval "$(starship init zsh)"

# ---------- Modern CLI replacements ----------
# eza (better ls) — --icons requires a Nerd Font in your terminal (see step 16)
alias ls='eza --icons --group-directories-first'
alias ll='eza -lah --icons --git --group-directories-first'
alias la='eza -a --icons --group-directories-first'
alias lt='eza --tree --icons --level=2 --group-directories-first'

# bat (better cat)
alias cat='bat --paging=never'
export BAT_THEME="ansi"

# zoxide (smarter cd) — use `z <partial-dir>` to jump anywhere visited
eval "$(zoxide init zsh)"

# fzf — Ctrl-R history, Ctrl-T file picker, Alt-C dir picker
source <(fzf --zsh)

# ---------- Quality of life ----------
setopt AUTO_CD              # `documents` ≡ `cd documents`
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_REDUCE_BLANKS
setopt SHARE_HISTORY
HISTSIZE=50000
SAVEHIST=50000

# ---------- Extra aliases ----------
alias ..='cd ..'
alias ...='cd ../..'
alias gs='git status'
alias gd='git diff'
alias gco='git checkout'
alias gp='git pull'

Then reload:

exec zsh

GitHub

13. GitHub CLI — gh

Official GitHub command-line tool. Manage PRs, issues, repos, releases, and Actions runs directly from the terminal. Also handles git credential auth so you don't need a personal access token in a config file.

Install:

brew install gh

Authenticate (interactive — opens your browser):

gh auth login

Pick the defaults for most prompts: GitHub.com → HTTPS → Login with web browser. You'll paste a one-time code into the browser tab it opens.

A few things you'll use often:

Git configuration

14. Git identity & delta

Identity — Every commit you make is stamped with a name and email. Git doesn't verify these, but they're permanently embedded and exposed in any public repo. Set them globally once; override per-repo when needed (e.g. work projects).

Replace the placeholders with your name and email, then run:

git config --global user.name "Your Name"
git config --global user.email "you@example.com"

For repos that need a different identity (e.g. a work account), cd into the repo and run git config user.email "work@example.com" — that writes to the local .git/config and overrides the global value just for that repo.

git-delta — Wires git-delta (installed in step 6) into git as the default pager and diff filter. After running, git diff / git show / git log -p render with syntax highlighting and side-by-side layout. Press n/N inside the pager to navigate between files.

git config --global core.pager "delta"
git config --global interactive.diffFilter "delta --color-only"
git config --global delta.navigate true
git config --global delta.side-by-side true
git config --global merge.conflictstyle zdiff3

Verify both are in place:

git config --global --list | grep -E "user|delta|pager|diffFilter|conflictstyle"

SSH access

15. SSH key for GitHub — ed25519 + macOS Keychain

An SSH key lets you push/pull from GitHub (and any other server) without typing credentials each time. gh already handles GitHub over HTTPS, so this is technically optional for GitHub — but it's the standard way to talk to any non-GitHub server (VPS, work boxes), so worth doing once. One key per machine — never copy private keys between computers.

Important: these commands are interactive (prompt for a passphrase). Run them in a real Ghostty window — not via Claude Code's ! prefix, which can't open browser tabs and won't reliably handle the OAuth handoff at the end.

1. Create the SSH directory with safe permissions:

mkdir -p ~/.ssh && chmod 700 ~/.ssh

2. Write ~/.ssh/config — tells SSH to integrate with the macOS Keychain so you only ever type the passphrase once:

cat > ~/.ssh/config <<'EOF'
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519
EOF
chmod 600 ~/.ssh/config

3. Generate an ed25519 key (modern, replaces RSA). Replace the email with yours. Prompts you to type a passphrase twice — pick one, the Keychain will remember it.

ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/id_ed25519

4. Add the key to the macOS Keychain — one final passphrase prompt, then it's stored forever:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

5. Upload the public key to GitHub. Two options:

Option A (web UI — recommended, no extra scopes): copy the public key to your clipboard, then paste it at github.com/settings/keys → "New SSH key" → Key type: Authentication Key.

pbcopy < ~/.ssh/id_ed25519.pub

Option B (via gh CLI): needs an extra scope, which triggers a browser-based auth refresh.

gh auth refresh -h github.com -s admin:public_key
gh ssh-key add ~/.ssh/id_ed25519.pub --title "$(scutil --get ComputerName)"

6. Test the connection:

ssh -T git@github.com

First time will ask you to confirm GitHub's host fingerprint — type yes. On success you'll see: Hi <username>! You've successfully authenticated, but GitHub does not provide shell access. That's the expected response (it's not an error).

Fonts

16. JetBrainsMono Nerd Font + Ghostty wiring

A monospaced font with thousands of extra icon glyphs patched in (file-type icons, git symbols, language logos). Required for eza --icons to render properly and for Starship to display its icon-based modules.

Install the font:

brew install --cask font-jetbrains-mono-nerd-font

Point Ghostty at the font (creates ~/.config/ghostty/config):

mkdir -p ~/.config/ghostty
cat > ~/.config/ghostty/config <<'EOF'
font-family = JetBrainsMono Nerd Font Mono
font-size = 14
EOF
Important: the font family is JetBrainsMono Nerd Font Mono (with Mono at the end), not JetBrainsMono Nerd Font. The latter is the proportional variant and Ghostty will silently fall back to its default. To list installed variants: ghostty +list-fonts | grep -i jetbrains.

Reload Ghostty's config with +Shift+, (or quit and reopen). You'll see the new font and the ligatures kick in: != renders as , => as , -> as .

AI tools

17. AI assistants — Claude desktop · Claude Code · Codex

Three separate tools, two from Anthropic and one from OpenAI. Different interfaces, different strengths — most folks end up using more than one.

Claude desktop — Mac app for chatting with Claude (the same thing as claude.ai but in a native window with Spotlight-style global hotkey). Download the .dmg from the website:

open https://claude.ai/download

Claude Code — Anthropic's terminal coding assistant (this is what runs when you type claude in a terminal). Installs to ~/.local/bin/claude, which is already on your PATH from step 12.

curl -fsSL https://claude.ai/install.sh | bash

Codex desktop — OpenAI's coding assistant Mac app. Download the .dmg from the website:

open https://openai.com/codex/

Codex CLI — open-source terminal version of Codex, written in Rust. Included with ChatGPT Plus / Pro / Business / Edu / Enterprise. Run codex in any directory to launch the interactive TUI.

brew install --cask codex

Worth considering later

Daily cheat sheet