MacBook Setup for AI Engineering: macOS Tools and Workflow

This is the setup I apply to a fresh MacBook before doing AI engineering work. It covers the command-line tools macOS does not include, Homebrew, Python with uv, the shell, terminal and editor choices, Docker, and local AI tools.

The useful boundary is between required foundations and personal preferences. Xcode Command Line Tools, Homebrew, Git, and a Python workflow are foundations. Warp, iTerm2, Powerlevel10k, Cursor, and local model runners are choices. The commands below install the foundation; the later sections record the choices I make on top. This is a documented setup sequence, not a bit-for-bit reproducible machine image.

Install the macOS foundations

Start with Apple’s command-line tools:

xcode-select --install

macOS opens a dialog that walks through the installation.

Homebrew is the package manager I use for the rest of this setup:

Download the installer at a reviewed revision, verify its digest, inspect it, and then run the local copy:

(
    set -e
    homebrew_installer="$(mktemp)"
    trap 'rm -f "$homebrew_installer"' EXIT
    curl --proto '=https' --tlsv1.2 --fail --location --show-error https://raw.githubusercontent.com/Homebrew/install/150c69df1e54b0b74c9fcca5a201410a2300816a/install.sh -o "$homebrew_installer"
    printf '%s  %s\n' 12479a24be3f5307eecac7cde670fad7118640f031229e964f544b1367b52a41 "$homebrew_installer" | shasum -a 256 --check
    less "$homebrew_installer"
    /bin/bash "$homebrew_installer"
)

The Homebrew installation guide explains the changes and default prefixes. On Apple Silicon, Homebrew uses /opt/homebrew. Run the brew shellenv command printed at the end so brew is available in the current shell. The installer may also configure the Homebrew prefix for future login shells. The commit pin and SHA-256 cover the bootstrap file; update them together after review. Homebrew itself is a rolling package manager, so later brew install commands still resolve the then-current formula versions.

Install the command-line tools I use regularly:

brew install openssl readline sqlite3 xz zlib uv htop gitmoji pandoc ncdu tmux

The packages fall into three groups:

  • openssl, readline, sqlite3, xz, and zlib provide libraries for common Python and command-line workloads.
  • uv manages Python versions, environments, dependencies, commands, and lockfiles.
  • htop, tmux, ncdu, gitmoji, and pandoc cover process monitoring, terminal sessions, disk inspection, commit formatting, and document conversion.

Use uv for everyday Python work

Before adopting uv, I used pyenv to manage Python environments. uv now covers my everyday workflow. Install the latest Python version managed by uv:

uv python install

That installs the latest version available to uv at setup time. For a project-specific version, use the pinning workflow in my quick guide to managing Python on macOS with uv.

Install pyenv separately only when you need its source-build path:

brew install pyenv

I use pyenv when I need a source-built interpreter or custom CPython build options that uv’s prebuilt distributions do not provide. Remove pyenv from the shell plugin list below if you skip this optional install.

Choose a terminal

The default macOS Terminal is fine. I used iTerm2 for years and recently moved to Warp, a Rust-based terminal with built-in AI features. The choice does not affect the later setup.

If you stay with iTerm2, these are the two settings I change:

Enable natural text editing

  1. Open Preferences → Profiles → Keys → Key Mappings.
  2. Open the Presets… dropdown.
  3. Select “Natural Text Editing”.

Pick a color theme

  1. Browse themes at iTerm2-Color-Schemes.
  2. Open Preferences → Profiles → Colors → Color Presets…
  3. Select Import and choose the downloaded theme.

Configure Zsh

macOS uses Zsh as its default login shell. I use the bundled /bin/zsh; install Homebrew’s Zsh only when you need a specific newer upstream version.

Check the installed Zsh and the current login shell:

echo "$SHELL"
command -v zsh
zsh --version

If Zsh is installed but is not selected as the login shell, switch to the bundled copy:

chsh -s /bin/zsh

Open a new terminal after changing the login shell.

Oh My Zsh adds the defaults and plugin system I use. Its standard installer follows a moving branch. For a pinned setup, clone without checking out that branch, select the reviewed commit, and only then copy the template:

(
    set -e
    omz_dir="$HOME/.oh-my-zsh"
    if [ -L "$HOME/.zshrc" ]; then
        printf 'Refusing to replace symlink: %s\n' "$HOME/.zshrc" >&2
        exit 1
    fi
    if [ -e "$HOME/.zshrc" ] && [ ! -f "$HOME/.zshrc" ]; then
        printf 'Refusing to replace non-regular file: %s\n' "$HOME/.zshrc" >&2
        exit 1
    fi
    if [ -e "$HOME/.zshrc" ]; then
        omz_backup="$(mktemp "$HOME/.zshrc.pre-oh-my-zsh.XXXXXX")"
        cp "$HOME/.zshrc" "$omz_backup"
        printf 'Existing .zshrc backed up to %s\n' "$omz_backup"
    fi
    git clone --filter=blob:none --no-checkout https://github.com/ohmyzsh/ohmyzsh "$omz_dir"
    git -C "$omz_dir" checkout --detach 4b657407c98bbc8830ae66c2ac7ff3d737c55a83
    test "$(git -C "$omz_dir" rev-parse HEAD)" = 4b657407c98bbc8830ae66c2ac7ff3d737c55a83
    new_zshrc="$(mktemp "$HOME/.zshrc.new.XXXXXX")"
    trap 'rm -f "$new_zshrc"' EXIT
    cp "$omz_dir/templates/zshrc.zsh-template" "$new_zshrc"
    test -s "$new_zshrc"
    test ! -d "$HOME/.zshrc"
    mv -f "$new_zshrc" "$HOME/.zshrc"
)

The block stops before replacing .zshrc if clone or commit verification fails. It refuses symlinks and non-regular files, backs up an existing file under a unique .zshrc.pre-oh-my-zsh.* name, and replaces it with a verified temporary copy in one rename. Compare the printed backup with the template and restore your local settings before opening a new shell. The checkout is pinned to commit 4b65740; update the hash only after reviewing a newer revision.

Run the shell-configuration snippets from one setup shell, with no other process editing .zshrc or the plugin and theme destinations. They refuse existing or symlinked destinations, but they are not a multi-process package manager.

Add plugins

Install zsh-autosuggestions and zsh-syntax-highlighting in Oh My Zsh’s custom plugin directory:

install_pinned_zsh_repo() (
    set -e
    repo_url="$1"
    commit="$2"
    destination="$3"
    if [ -L "$destination" ]; then
        printf 'Refusing symlink destination: %s\n' "$destination" >&2
        return 1
    fi
    if [ -d "$destination/.git" ] && [ "$(git -C "$destination" rev-parse HEAD)" = "$commit" ]; then
        return 0
    fi
    destination_parent="$(dirname "$destination")"
    mkdir -p "$destination_parent"
    stage="$(mktemp -d "$destination_parent/.pinned-zsh.XXXXXX")"
    cleanup_stage() { rm -rf "$stage"; }
    trap cleanup_stage EXIT
    git clone --filter=blob:none --no-checkout "$repo_url" "$stage/repo"
    git -C "$stage/repo" checkout --detach "$commit"
    test "$(git -C "$stage/repo" rev-parse HEAD)" = "$commit"
    test ! -e "$destination"
    test ! -L "$destination"
    mv -h -n "$stage/repo" "$destination"
    test ! -e "$stage/repo"
    test "$(git -C "$destination" rev-parse HEAD)" = "$commit"
)

install_pinned_zsh_repo https://github.com/zsh-users/zsh-autosuggestions e52ee8ca55bcc56a17c828767a3f98f22a68d4eb "${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autosuggestions"
install_pinned_zsh_repo https://github.com/zsh-users/zsh-syntax-highlighting.git db085e4661f6aafd24e5acb5b2e17e4dd5dddf3e "${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting"

The pinned zsh-autosuggestions commit and zsh-syntax-highlighting commit correspond to the published v0.7.1 and 0.8.0 releases. Update the hashes only after reviewing a newer release.

Edit ~/.zshrc to load them with the plugins I use:

plugins=(
    aws bgnotify brew docker docker-compose
    emoji forklift gcloud git history iterm2
    keychain kubectl macos pre-commit
    pyenv pylint python screen themes
    tmux virtualenv vscode
    zsh-autosuggestions zsh-syntax-highlighting
)

Keep zsh-syntax-highlighting last in the array. The Oh My Zsh plugins wiki describes the bundled plugins. The two external plugins suggest commands from history and highlight commands as you type; follow the install instructions in each repository.

Add Powerlevel10k and its font

Powerlevel10k is the Zsh theme I use. It shows the working directory, Git status, and active Python environment in the prompt, and provides an interactive configuration wizard. Install it for Oh My Zsh, then select the theme in ~/.zshrc:

(
    set -e
    p10k_dir="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k"
    if [ -L "$p10k_dir" ]; then
        printf 'Refusing symlink destination: %s\n' "$p10k_dir" >&2
        exit 1
    fi
    if [ -d "$p10k_dir/.git" ] && [ "$(git -C "$p10k_dir" rev-parse HEAD)" = 35833ea15f14b71dbcebc7e54c104d8d56ca5268 ]; then
        exit 0
    fi
    test ! -e "$p10k_dir"
    p10k_parent="$(dirname "$p10k_dir")"
    mkdir -p "$p10k_parent"
    stage="$(mktemp -d "$p10k_parent/.pinned-p10k.XXXXXX")"
    cleanup_stage() { rm -rf "$stage"; }
    trap cleanup_stage EXIT
    git clone --filter=blob:none --no-checkout https://github.com/romkatv/powerlevel10k.git "$stage/repo"
    git -C "$stage/repo" checkout --detach 35833ea15f14b71dbcebc7e54c104d8d56ca5268
    test "$(git -C "$stage/repo" rev-parse HEAD)" = 35833ea15f14b71dbcebc7e54c104d8d56ca5268
    test ! -L "$p10k_dir"
    mv -h -n "$stage/repo" "$p10k_dir"
    test ! -e "$stage/repo"
    test "$(git -C "$p10k_dir" rev-parse HEAD)" = 35833ea15f14b71dbcebc7e54c104d8d56ca5268
)

The pinned Powerlevel10k commit is referenced by the v1.20.0 release.

The Git object IDs above prevent silent version drift after you obtain them; they do not authenticate the maintainer by themselves. These commands trust GitHub’s HTTPS endpoint and the repository accounts behind it. For a stronger trust model, verify a signed tag or commit against a maintainer key you obtained separately before moving the staged checkout into place.

ZSH_THEME="powerlevel10k/powerlevel10k"

Open a new shell and run p10k configure.

If you use VS Code’s integrated terminal, install the recommended font before setting the terminal font so Powerlevel10k icons render. p10k configure can install the font automatically in iTerm2. For other terminals, download and install the four TTF files from the Powerlevel10k font guide.

In VS Code, set the terminal font to MesloLGS NF:

  1. Open your editor’s settings.
  2. Search for terminal.integrated.fontFamily.
  3. Set it to MesloLGS NF.

Choose editors and AI assistants

I keep one IDE open and an AI tool or two beside it.

IDEs

  • Cursor is a VS Code fork with AI pair programming built in.
  • VS Code has the larger extension catalog.

AI assistants

  • OpenAI Codex is OpenAI’s coding agent.
  • Claude is Anthropic’s assistant, which I reach for on harder tasks.

These days I use Cursor with Codex and Claude Code running in parallel.

Choose containers and local model tools

The remaining tools depend on the work I plan to do on the machine:

Docker Desktop and local model runners are independent choices. Install them when your projects need containers or local inference; the shell and Python setup does not depend on either one.

Record the setup boundaries

This is a personal setup, not a minimal or universal macOS baseline. Remove what you do not use. It records reviewed bootstrap revisions and plugin releases, but Homebrew formulas and GUI applications still move. The parts I keep consistent are the boundaries:

  1. Use the bundled /bin/zsh unless a project needs a specific newer Zsh.
  2. Use uv for everyday Python installation and project environments; keep pyenv for source-built or custom CPython interpreters.
  3. Keep install commands, secret-free dotfiles, editor extensions, and model locations documented so the next setup is mechanical.

References