{"slug": "claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native", "title": "Claude Code prompt: set up a new MacBook Pro (2026) as a power-terminal + AI-native workstation", "summary": "A developer published a reusable Claude Code prompt and companion bootstrap script for provisioning a new Apple Silicon MacBook Pro as a terminal-centric, AI-native engineering workstation. The setup covers idempotent Homebrew installs, dotfile backup and checkpointing via a state file, and batched decision points for shell, terminal, multiplexer, dotfile, and container tooling. It also includes a zsh wrapper for switching Claude Code between API key and OAuth authentication without storing secrets in shell config files.", "body_md": "Paste everything below the line into a fresh `claude` session on the new machine.\nWritten for **Apple Silicon macOS (Tahoe 26.x / Sequoia 15.x)**, Claude Code, and a\nhuman who lives in the terminal.\n\nWhy this exists: most \"new Mac setup\" gists are a wall of `brew install`. This is a\n*prompt* — it gives an agent the constraints, the decision points, the ordering, and\nthe verification step, so you get a working machine instead of a half-applied script.\n\n| File | What it is | \n|---|---|\n| `macbook-pro-2026-setup-prompt.md` | **this file** — the prompt you paste into Claude Code | \n| `bootstrap.sh` | idempotent executable version of PHASES 1–9. **Run `./bootstrap.sh --dry-run` first.** | \n| `zsh_plugins.txt` | antidote bundle → `~/.zsh_plugins.txt` (Oh My Zsh plugins, no Oh My Zsh) | \n| `starship.toml` | lean two-line prompt → `~/.config/starship.toml` | \n| `p10k.zsh` | *optional alternative prompt* — Powerlevel10k config →`~/.p10k.zsh` | \n| `claude-wrapper.zsh` | `claude()` shell wrapper — switch between API key / OAuth token, permissive by default | \n\nTwo ways to use this:\n\n- **Agent-driven** (recommended) — paste this file into`claude` . It asks the DECISIONS, adapts, and explains.\n- **Script-driven** —`./bootstrap.sh --dry-run` to preview, then run it. No sudo, resumable via`--from <phase>` .\n\nThey compose: run `bootstrap.sh` for the mechanical 80%, then hand the agent this file for the judgement calls.\n\nYou are setting up a brand-new MacBook Pro (Apple Silicon) for a **senior engineer who\nis a terminal power user and works with AI agents daily**. You have shell access via\nyour Bash tool. Treat this machine as a long-lived primary workstation.\n\nYour job is not to run a script. Your job is to **build a reproducible, agent-friendly\ndevelopment environment and leave behind the artifacts that let it be rebuilt in 20\nminutes**.\n\n1. **Idempotent.** Every step must be safe to re-run. Guard with`command -v x >/dev/null ||` ,`brew list x || brew install x` ,`grep -q ... file || echo ... >> file` . Never blindly append.\n2. **Never clobber.** Before overwriting any dotfile,`cp` it to`~/.setup-backup/<timestamp>/` .\n3. **Checkpoint.** Maintain`~/.setup-state.json` —`{phase, completed: [], skipped: [], notes: []}` .\nUpdate it after every phase. If the session dies, a fresh agent must be able to read it and resume.\n4. **Ask before** : anything needing`sudo` , changing the login shell, installing a kernel\nextension or VPN, touching System Settings that affects security, or spending money\n(paid apps, cloud resources).\n5. **Batch the questions.** Ask all open decisions in ONE round up front (see DECISIONS),\nnot one at a time across an hour.\n6. **Prefer `brew` formulae/casks** over curl-pipe-bash. When upstream only ships an\ninstaller script, read it first and say what it does.\n7. **Show, don't claim.** End each phase with the actual command output proving it worked.\n\"Installed successfully\" without output is not acceptable.\n8. **No secrets in files.** No API keys in`.zshrc` , ever. See PHASE 8.\n\n| # | Question | Default if user says \"you pick\" | \n|---|---|---|\n| 1 | Shell: **zsh** (macOS default, universal) or**fish** (best interactive UX, non-POSIX)? | zsh — agents and scripts assume POSIX | \n| 1b | Plugin manager: **antidote** (static file + deferred loading; loads Oh My Zsh plugins without Oh My Zsh),**Oh My Zsh** (simpler, slower), or plain? | antidote | \n| 1c | Prompt: **starship** (cross-shell, TOML) or**Powerlevel10k** (zsh-only, instant prompt)? | starship | \n| 2 | Terminal: **Ghostty** (fast, native, 2026 default),**WezTerm** (Lua-scriptable),**iTerm2** (mature),**Warp** (AI-native, telemetry)? | Ghostty | \n| 3 | Multiplexer: **tmux** ,**Zellij** , or terminal-native splits only? | tmux (agents/ssh assume it) | \n| 4 | Dotfile strategy: **chezmoi** ,**GNU stow** , or**bare git repo** ? | chezmoi (templating + secrets integration) | \n| 5 | Containers: **OrbStack** (fast, low battery),**Docker Desktop** ,**colima** , or none? | OrbStack | \n| 6 | Password/secret manager already in use? (1Password, Bitwarden, keychain-only) | ask — do not guess | \n| 6b | Editor/IDE: **VS Code** (or a fork: Cursor/Windsurf/Kiro),**JetBrains** ,**Zed** ,**Neovim** , or terminal-only? | ask — this is the most personal choice here | \n| 7 | Which languages actually matter? (node/py/go/rust/ruby/java/…) | node + python + go, add on demand | \n| 8 | Personal or work machine? (changes SSH/GPG signing, MDM constraints, telemetry tolerance) | ask | \n\nAlso run recon before asking, so your questions are informed:\n\n```\nsw_vers; uname -m; echo \"---\"\ncommand -v brew git zsh fish ghostty antidote starship fnm uv go claude 2>/dev/null\necho \"---\"; ls -a ~ | head -50\necho \"---\"; csrutil status 2>/dev/null; fdesetup status 2>/dev/null\necho \"---\"; profiles status -type enrollment 2>/dev/null   # is this MDM-managed?\n```\n\n- Xcode Command Line Tools: `xcode-select -p || xcode-select --install` (this is\ninteractive/GUI — tell the user to click through, then poll until`xcode-select -p` succeeds).\n- **Homebrew** at`/opt/homebrew` (arm64). Add the shellenv line to`~/.zprofile` , not`~/.zshrc` .\n- Create a `Brewfile`**from the start** in the dotfiles repo. Every install goes through it:`brew bundle --file=~/dotfiles/Brewfile` . This is the reproducibility artifact.\n- `mkdir -p ~/.local/bin ~/Developer ~/.config` and ensure`~/.local/bin` is first on`PATH` .\n- Rosetta only if the user names an actual x86-only dependency. Do not install it \"just in case\".\n\n- Terminal emulator per DECISION 2. Config in `~/.config/ghostty/config` (XDG, git-trackable).\n- **Nerd Font** — required or every glyph in the prompt renders as tofu:`brew install --cask font-jetbrains-mono-nerd-font` (or Maple Mono NF / Berkeley Mono if owned).\nSet it in the terminal config, and verify by echoing a glyph.\n- Prompt: **starship** —`brew install starship` , config at`~/.config/starship.toml` . See PHASE 2b.\nKeep it fast — set`command_timeout` and disable modules that stat the filesystem on\nevery prompt in big repos. A slow prompt is a tax you pay thousands of times a day.\n- Multiplexer per DECISION 3, config in `~/.config/` .\n- Plugin manager + prompt: **antidote + starship** — see PHASE 2b, big enough for its own phase.\n- **Benchmark startup and report the number** :`hyperfine --warmup 3 'zsh -i -c exit'` .\nTarget < 150ms. If it's over, bisect with`zsh -xv` and fix it now, not later.\n\n**antidote** is the plugin manager; **starship** is the prompt. Neither is Oh My Zsh — but\nyou keep OMZ's plugin library, which is the part actually worth having.\n\nOh My Zsh is two things bolted together: a **plugin library** (excellent, ~300 plugins,\n~150 git aliases) and a **framework loader** (sources everything serially on every shell\nstart, 150–400ms). antidote lets you keep the first and throw away the second:\n\n```\nohmyzsh/ohmyzsh path:plugins/git      # ← the OMZ git plugin, no OMZ install\n```\n\nIt works by generating a **static, pre-compiled load script**. Startup sources one flat\nfile instead of walking 20 git repos. It also supports `kind:defer` (via `zsh-defer`) to\nload plugins *after* the prompt paints, and `kind:fpath` for completions that should go on\n`fpath` without being sourced at all.\n\n```\nbrew install antidote\n```\n\nBundle file → `~/.zsh_plugins.txt` (**`zsh_plugins.txt` in this gist** is a working one).\n\n```\n# --- antidote: regenerate the static file only when the bundle changed -------\nzstyle ':antidote:bundle' use-friendly-names 'yes'\nantidote_dir=\"$(brew --prefix)/opt/antidote/share/antidote\"\n[[ -e $antidote_dir/antidote.zsh ]] && source $antidote_dir/antidote.zsh\n\nzsh_plugins=${ZDOTDIR:-$HOME}/.zsh_plugins\nif [[ ! ${zsh_plugins}.zsh -nt ${zsh_plugins}.txt ]]; then\n  antidote bundle <${zsh_plugins}.txt >|${zsh_plugins}.zsh\nfi\nsource ${zsh_plugins}.zsh\n\nautoload -Uz compinit && compinit -C     # -C skips the security check: much faster\n\n# --- tool inits: MUST come after antidote load -------------------------------\n# These rebind keys and hook precmd. Load them before the plugins and the\n# plugins will overwrite the bindings — silently, with no error anywhere.\neval \"$(starship init zsh)\"\neval \"$(fnm env --use-on-cd --shell zsh --version-file-strategy=recursive --resolve-engines)\"\neval \"$(zoxide init zsh)\"\neval \"$(direnv hook zsh)\"\neval \"$(atuin init zsh --disable-up-arrow)\"   # keeps ↑ for history-substring-search\nsource <(fzf --zsh)\n\nZSH_AUTOSUGGEST_STRATEGY=(history completion)\nZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20\n```\n\n1. **`zsh-syntax-highlighting` must be last** in the bundle. It wraps every widget defined\nbefore it; anything loaded afterwards simply isn't highlighted.\n2. `zsh-history-substring-search` comes**after** syntax-highlighting.\n3. `ohmyzsh/ohmyzsh path:lib/*` comes**before** any`path:plugins/*` that depends on it.\n4. Everything that hooks `precmd` or rebinds keys — starship, fnm, zoxide, atuin, fzf,\ndirenv — goes in`.zshrc`**after**` source ${zsh_plugins}.zsh` .\n\n```\nbrew install starship          # config → ~/.config/starship.toml\n```\n\n**`starship.toml` in this gist** is a lean two-line prompt. The setting that matters most:\n\n```\ncommand_timeout = 200    # default is 500ms — long enough to feel broken\n```\n\nAny module slower than that is dropped for that render rather than stalling your shell.\nThe config also turns **prod Kubernetes contexts red**, on the same reasoning that root's\nprompt is red: make the state you can regret visually unmistakable.\n\nPrefer Powerlevel10k? `p10k.zsh` in this gist is a full hand-written config with instant\nprompt and transient prompt. **Pick exactly one** — running both wastes startup and only\none can own the prompt.\n\nantidote's whole point is that you stop paying for plugins you aren't using:\n\n- **Measure** :`hyperfine --warmup 3 'zsh -i -c exit'` .**Target < 100ms** — antidote should\nbeat an OMZ setup comfortably. If it doesn't, something in`.zshrc` is doing real work.\n- **Profile** :`zmodload zsh/zprof` as line 1 of`.zshrc` ,`zprof` as the last line.\n- **Defer** anything non-essential: append`kind:defer` in the bundle file.\n- `compinit -C` skips the daily security audit of`fpath` . Worth 20–70ms. Drop the`-C` if\nyou install completions from untrusted sources.\n\n| Conflict | Resolution | \n|---|---|\n| OMZ `nvm` /`pyenv` plugins vs**fnm/uv** | don't bundle them — see PHASE 5 | \n| OMZ theme vs **starship** | no OMZ theme exists here; starship owns the prompt | \n| **atuin** vs`history-substring-search` | `atuin init zsh --disable-up-arrow` | \n| OMZ `fzf` plugin vs`fzf --zsh` | pick one, or you double-bind `Ctrl-R` | \n| OMZ `ssh-agent` vs**1Password agent** | don't bundle it, use `IdentityAgent` (PHASE 8) | \n| `aws` /`kubectl` /`terraform` OMZ plugins | shell out at load; add `kind:defer` or skip | \n\nYou still get OMZ's ~150 git aliases (`gst`, `gco`, `gp`) — and they are still\n**interactive-only**. Your Bash tool, CI, and any `bash -c` will not see them. PHASE 4's\nrule is unchanged: anything an agent must call has to be a real executable in\n`~/.local/bin`.\n\nPut this in `~/.claude/CLAUDE.md` so the model doesn't guess wrong:\n\nShell is zsh + antidote loading Oh My Zsh plugins. Aliases (`gst`, `gco`, `k`, …) exist\nonly in interactive shells — always use full commands (`git status`) in tool calls.\n\nInstall via Brewfile. For each, wire up the shell integration *and* verify it:\n\n| Replaces | Tool | Why it earns its place | \n|---|---|---|\n| `cat` | **bat** | syntax highlight + git gutter; `--style=plain` for piping | \n| `ls` | **eza** | git status column, tree mode, sane colors | \n| `grep` | **ripgrep** | 10–100× faster, respects `.gitignore` by default | \n| `find` | **fd** | sane syntax, parallel, gitignore-aware | \n| `cd` | **zoxide** | frecency jumps: `z proj` | \n| `du` | **dust** /`df` →**duf** | actually readable | \n| `ps` /`top` | **procs** ,**btop** | modern output | \n| `sed` /`awk` | **sd** ,**choose** | for the 90% case; keep the originals | \n| `diff` | **delta** | wire into `git.pager` , not just standalone | \n| history | **atuin** | SQLite history, sync, `Ctrl-R` that actually works | \n| — | **fzf** | the connective tissue; install `fzf --zsh` keybindings | \n| — | **jq** +**yq** +**gron** | JSON/YAML surgery; `gron` makes JSON greppable | \n| — | **hyperfine** ,**tokei** ,**watchexec** | benchmark, count, re-run on change | \n| — | **gh** ,**lazygit** ,**git-absorb** | GitHub CLI, TUI git, auto-fixup commits | \n| — | **httpie** or**xh** ,**curlie** | HTTP without flag archaeology | \n| — | **tealdeer** (`tldr` ) | examples instead of man pages | \n| — | **direnv** (or mise's env) | per-project env vars | \n\nIf installing only two: **fzf + zoxide**. They remove the most daily friction.\n\nThis is what turns a pretty terminal into one an AI agent can actually drive.\n\n1. \n**Aliases don't exist for tools.** A non-interactive shell (`bash -c \"...\"` , CI, an\nagent's Bash tool) does not source your interactive aliases. So:Anything you want *both* you and an agent to call must be a**real executable in\n`~/.local/bin`** , not an alias or an interactive-only function.Write them as small `#!/usr/bin/env bash` scripts with`set -euo pipefail` , and keep\naliases only for pure typing shortcuts.\n2. \n**Guard the interactive-only parts** so non-interactive shells stay fast and quiet:\n\n```\n[[ -o interactive ]] || return    # top of the interactive-only section\n```\n\n Split config: `~/.zshenv` (env, always),`~/.zprofile` (login/PATH),`~/.zshrc` (interactive).\n3. \n**Kill every pager.** A pager in a non-TTY is a hung tool call:\n\n```\nexport PAGER=cat GH_PAGER=cat\nexport LESS='-FRX'\ngit config --global core.pager 'delta --paging=never'\n```\n\n Also `export HOMEBREW_NO_ENV_HINTS=1` and set`GIT_TERMINAL_PROMPT=0` so git fails\nfast instead of blocking on a credential prompt.\n4. \n**Non-interactive by default.**`alias` -free wrappers should never prompt. Prefer`brew install -q` ,`npm ci --no-fund --no-audit` ,`gh --json` . Anything that might\nopen`$EDITOR` needs`--no-edit` or`EDITOR=true` .\n5. \n**Structured output where it exists.** Teach the setup (and CLAUDE.md) to reach for`gh ... --json` ,`docker ... --format json` ,`jq` ,`sd` ,`rg --json` . Agents parse\nJSON reliably and scrape human tables badly.\n6. \n**Safety rails that survive an agent typo:**\n  - `brew install trash` ; make`rm` in*interactive* zsh a wrapper around`trash` , and\nleave real`rm` for scripts.\n  - `git config --global rerere.enabled true` ,`push.default simple` ,`pull.rebase true` ,`init.defaultBranch main` ,`fetch.prune true` .\n  - Set up `git commit --no-gpg-sign` fallback if signing would prompt for a passphrase\nin a non-TTY (or use an SSH-agent-backed signing key that doesn't prompt).\n7. \n**`.inputrc` / zsh keybindings** for real line editing:`bindkey -e` , word-wise`Alt-←/→` ,`Ctrl-x Ctrl-e` to open the current command in`$EDITOR` .\n\nThe 2026 shift is away from a single polyglot manager toward **fast, native, per-language\ntools** — plus the language's own built-in mechanism where one now exists. Use `mise` only\nfor the long tail.\n\n| Language | Tool | Why this one | \n|---|---|---|\n| **Node** | `fnm` | Rust; ~40× faster than nvm; auto-switches on `cd` | \n| **Python** | `uv` | replaces pip + virtualenv + poetry + **pyenv** , all in one | \n| **Rust** | `rustup` | canonical, no alternative worth considering | \n| **Go** | *(built in)* | `GOTOOLCHAIN` +`go.mod` — no third-party manager needed | \n| everything else | `mise` | Java, Ruby, Erlang, Deno, Bun, CLI tools | \n\n```\nbrew install fnm\nfnm install --lts && fnm default lts-latest\neval \"$(fnm env --use-on-cd --shell zsh --version-file-strategy=recursive --resolve-engines)\"\n```\n\n- `--use-on-cd` switches version automatically when you enter a project.\n- `--version-file-strategy=recursive` looks in parent dirs, so it works from a subdirectory\nof a monorepo, not just the root.\n- `--resolve-engines` honours`\"engines\": {\"node\": \"...\"}` in`package.json` .\n- Reads both `.nvmrc` and`.node-version` , so it's a drop-in for an nvm team.\n\n**Corepack gotcha (new in 2026):** Corepack was **removed from Node 25+** in January 2026.\nIf you rely on `packageManager` in `package.json` to pin pnpm/yarn, install it yourself:\n`npm install -g corepack`. `fnm --corepack-enabled` only helps on versions that still\nbundle it. Silent breakage on a fresh machine otherwise.\n\n`uv` manages **interpreters as well as packages** — pyenv is redundant.\n\n```\nbrew install uv\nuv python install 3.13 3.12     # interpreters → ~/.local/share/uv/python/\nuv python pin 3.13              # writes .python-version for this project\n```\n\nDaily use: `uv venv`, `uv sync`, `uv run <script>`, `uv add <pkg>`, `uv tool install <cli>`,\n`uvx <cli>` for one-off runs. **Never `pip install --user`**, and never install into system\nPython — uv's interpreters are isolated from it by design.\n\n```\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path\nrustup component add clippy rustfmt rust-analyzer\n```\n\nUse `rustup`, **not** `brew install rust` — brew's is a fixed version with no toolchain\nswitching, and it collides with rustup if both are on `PATH`. Per-project pinning is\n`rust-toolchain.toml`, which rustup honours automatically.\n\nSince Go 1.21 the toolchain manages itself. There is no reason to run `goenv` or `g`:\n\n```\nbrew install go\nexport GOTOOLCHAIN=auto        # in ~/.zshenv\n```\n\nA `go.mod` with a `toolchain` directive causes the `go` command to **download and use that\nexact toolchain automatically**. Everyone building the module gets the same compiler with\nno version-manager coordination — the \"works on my machine\" class of Go bug is gone.\n\n```\ngo 1.24            // minimum version required to build\ntoolchain go1.25.1 // preferred toolchain, fetched on demand\n```\n\n`GOTOOLCHAIN=auto` opts into that. `GOTOOLCHAIN=local` disables it and pins you to whatever\n`brew` installed — which is what you want in a hermetic CI image, not on a laptop.\n\n```\nbrew install mise\nmise use -g java@temurin-21 ruby@3.4 deno@latest\neval \"$(mise activate zsh)\"\n```\n\nKeep mise **out of** Node/Python/Rust/Go. Two managers racing for the same `PATH` entry is a\ngenuinely miserable class of bug — you get a version that depends on the order your `.zshrc`\nhappened to `eval`. One language, one owner.\n\n```\ngit config --global init.defaultBranch main\ngit config --global pull.rebase true\ngit config --global push.default simple\ngit config --global fetch.prune true\ngit config --global rerere.enabled true          # remember conflict resolutions\ngit config --global core.pager 'delta --paging=never'\n```\n\nWhatever the user names. If Neovim, start from LazyVim or kickstart rather than hand-rolling\na config, and set `EDITOR`/` VISUAL` consistently.\n\nThe one decision in this document you should **not** default. Ask, then set it up properly.\n\nFor an AI-native workstation the axis that matters isn't syntax highlighting or plugin\ncount — it's **how Claude Code connects to the editor**. Claude Code ships an IDE protocol\n(a local WebSocket speaking MCP) that gives the agent your selection, open files, and\ndiagnostics, and renders diffs inline instead of in the terminal. Support tiers differ:\n\n| Editor | Claude Code integration | Trade | \n|---|---|---|\n| **VS Code** + forks (Cursor, Windsurf, Kiro) | **official extension** | biggest ecosystem; Electron, so heaviest | \n| **JetBrains** (IntelliJ, PyCharm, WebStorm, GoLand, …) | **official plugin** | best refactoring/debugger in class; slow start, RAM-hungry | \n| **Zed** | evolving — native support in progress; CLI works today | fastest editor here, Rust, great collab | \n| **Neovim** | community plugin implementing the same WebSocket protocol | lowest latency, fully terminal-native; you own the config | \n| **anything + a terminal** | the CLI itself, always | zero integration work, no inline diffs | \n\n**Nobody is locked out.** The CLI works in any terminal, including the built-in terminal of\nan editor with no extension at all. The extension buys inline diffs and automatic context —\nnot access.\n\n1. **`claude --ide` auto-connects** on startup when exactly one valid IDE is available. Live\nconnections appear as lock files in`~/.claude/ide/` . If the agent doesn't seem to see\nyour open file, check that directory before debugging anything else.\n2. **The extension and the CLI share conversation history.** Start in the editor, continue in\nthe terminal with`claude --resume` , or the reverse. You are not picking a silo — so\noptimise the choice for how you like to*edit* , not for where the agent lives.\n\n- Already in JetBrains → **stay** . The plugin is first-class; nothing below beats its\ndebugger or refactoring, and switching editors to chase an agent is a bad trade.\n- Wants maximum AI integration → **Cursor** (VS Code fork, so the Claude Code extension\nworks, plus its own agent). Accept the Electron footprint.\n- Wants speed and a native feel → **Zed** . Genuinely faster than VS Code; run Claude Code in\nits terminal until native support lands.\n- Terminal purist → **Neovim** with LazyVim or kickstart. Do**not** hand-roll from an empty`init.lua` on a machine you need working today.\n- Undecided → **VS Code** . Largest ecosystem, official extension, easiest to leave.\n\nThis is where PHASE 4 and this phase intersect, and it's easy to get subtly wrong.\n\n```\n# ~/.zshenv — applies to every shell\nexport EDITOR=\"code --wait\"     # or: \"cursor --wait\", \"zed --wait\", \"nvim\"\nexport VISUAL=\"$EDITOR\"\n```\n\n`--wait` is **required** for GUI editors: without it the command returns instantly, git sees\nan empty buffer and aborts your commit. But the same flag is a **hang** in a non-interactive\nshell — the agent's tool call blocks forever on a window nobody will close. So:\n\n```\n# non-interactive shells must never open an editor at all\n[[ -o interactive ]] || export GIT_EDITOR=true\n```\n\nPair that with `GIT_TERMINAL_PROMPT=0` from PHASE 4 and git fails fast in tool calls instead\nof blocking. A terminal editor (`nvim`, `vim`) doesn't need `--wait` and doesn't have this\nproblem — one more quiet point in favour of the terminal-native option.\n\n- The CLI launcher (`code` ,`cursor` ,`zed` ) so`$EDITOR` resolves — VS Code needs*Shell Command: Install 'code' command in PATH* from the command palette.\n- Your language servers via the editor, **not** duplicated with the ones in PHASE 6.\n- `ApplePressAndHoldEnabled -bool false` from PHASE 7 — without it, holding a key in Vim\nmode shows the accent-picker instead of repeating. Already in this document; it matters\nmost here.\n\n- \n`claude` CLI installed and authenticated (`claude doctor` must pass — show its output).\n- \n**`~/.claude/CLAUDE.md`** — global operating instructions. Keep it short and*specific* ;\nit's loaded into every session, so every line costs context. Good content:\n  - which package manager/toolchain this machine uses (`uv` , not`pip` ;`pnpm` , not`npm` )\n  - \"prefer `rg` /`fd` over`grep` /`find` \", \"use`gh --json` for GitHub\"\n  - project layout conventions (`~/Developer/<org>/<repo>` )\n  - the user's commit-message and PR conventions\n  - what NOT to do on this machine (e.g. never push to `main` , never`brew upgrade` unprompted)\n- which package manager/toolchain this machine uses (\n- \n**`~/.claude/settings.json`** — the permission allowlist is the single highest-leverage\nthing for a terminal power user. Allowlist the read-only commands you run constantly so\nyou stop approving them:`rg` ,`fd` ,`eza` ,`bat` ,`git status/diff/log` ,`gh pr view` ,`jq` ,`ls` ,`cat` ,`tokei` . Deny-list the destructive ones outright.\n- \n**Hooks** for the policy layer that shouldn't depend on the model remembering:\nformat-on-edit (`PostToolUse` ), block`rm -rf /` and`git push --force` on protected\nbranches (`PreToolUse` ), desktop notification on`Stop` .\n- \n**MCP servers** — add narrowly, they cost context. The 80% stack:**Context7** (live\nlibrary docs),**GitHub** ,**Playwright** (browser). Add Postgres/Sentry/Linear only if\nthe user actually uses them daily. Prefer project-scoped`.mcp.json` over global.\n- \n**Skills and subagents** in`~/.claude/skills/` for repeatable multi-step workflows.\n- \nOptional second opinion: keep one alternate agent CLI installed for cross-checking, and `ollama` if the user wants local models for offline/private work.\n- \n**Status line** : show model, cwd, git branch, and context remaining.\n- \n**`claude()` shell wrapper** — see`claude-wrapper.zsh` in this gist. Paste into`~/.zshrc` .\nIt solves three things the bare CLI doesn't:\n  1. **Two credentials, one switch.** Holds both an`ANTHROPIC_API_KEY` and a`CLAUDE_CODE_OAUTH_TOKEN` ;`claude-api` /`claude-oauth` /`claude-use <mode>` pick between them. API key = pay-per-token console billing; OAuth token = your\nClaude subscription. Which one you're on is a billing decision, so make it explicit.\n  2. **Mutual exclusion.** If both variables are visible to the CLI its choice is\nambiguous — you get confusing 401s or bill the wrong account. The wrapper runs in a\nsubshell that`unset` s the one it isn't using, so exactly one ever reaches`claude` .Do **not** implement this as`env -u OTHER_VAR VAR=x command claude` .`env` execs an\nexternal program, so it cannot invoke zsh's`command` builtin — it either fails with\n\"command not found\" or silently runs some unrelated`/usr/local/bin/command` on your\nPATH. A subshell keeps the builtin and confines the`unset` .\n  3. **Default flags, safely.** Injects`--dangerously-skip-permissions` , but suppresses it\nfor subcommands that reject it (`mcp` ,`doctor` ,`setup-token` ,`config` , …), as root,\nwhen you already passed a permission flag, and whenever`CLAUDE_SAFE=1` is set.\n **This means every `claude` run can edit files and execute commands without asking.** That is the point of the flag, but it is a real trade: use`claude-safe` in any directory\nyou don't fully trust, and keep the`deny` list in`settings.json` populated regardless.\n\nApply with `defaults write`, then `killall Finder Dock SystemUIServer`. Confirm the list\nwith the user first — these change muscle memory:\n\n```\ndefaults write NSGlobalDomain KeyRepeat -int 2\ndefaults write NSGlobalDomain InitialKeyRepeat -int 15\ndefaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false   # key repeat in editors\ndefaults write NSGlobalDomain AppleShowAllExtensions -bool true\ndefaults write com.apple.finder AppleShowAllFiles -bool true\ndefaults write com.apple.finder ShowPathbar -bool true\ndefaults write com.apple.finder FXPreferredViewStyle -string \"Nlsv\"\ndefaults write com.apple.dock autohide-time-modifier -float 0.15\ndefaults write com.apple.dock show-recents -bool false\ndefaults write com.apple.screencapture location -string \"$HOME/Screenshots\"\ndefaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true\n```\n\nApps worth the slot: **Raycast** (replaces Spotlight; scriptable, has a Claude/AI layer),\n**AeroSpace** or **Rectangle** (tiling/window management), **Karabiner-Elements**\n(Caps Lock → Ctrl/Esc — the single best keyboard change for a terminal user),\n**Hammerspoon** if they like Lua automation.\n\n- **SSH key** :`ssh-keygen -t ed25519 -C \"<email>\"` , add to keychain via`~/.ssh/config` (`UseKeychain yes` ,`AddKeysToAgent yes` ,`IdentityFile ~/.ssh/id_ed25519` ).\n- Prefer a **1Password (or Secretive) SSH agent** so keys live in the Secure Enclave and\nnever touch disk unencrypted. Set`IdentityAgent` accordingly.\n- **Sign commits with the SSH key** , not GPG — far less friction:`git config --global gpg.format ssh` +`user.signingkey` +`commit.gpgsign true` .\n- Secrets in the shell: never literal values in dotfiles. Use `op read op://vault/item/field` at call time, or macOS keychain (`security find-generic-password -w -s <svc>` ).\nA dotfiles repo containing a real key is a breach waiting for a`git push --public` .\n- Confirm **FileVault is on** , firewall enabled, and Touch ID works for`sudo` (`/etc/pam.d/sudo_local` — this needs sudo, so ask first).\n- Add a pre-commit secret scanner (`gitleaks` or`trufflehog` ) as a global git hook.\n\n1. Write `~/.local/bin/doctor` — one script that checks every tool resolves, the shell\nstarts under budget,`git` /`gh` /`ssh -T git@github.com` auth works,`claude doctor` passes, and`fnm` /`uv` /`go` /`rustup` each resolve a version.**Run it and paste the full\noutput.**\n2. Commit the dotfiles repo (chezmoi/stow/bare) and push it. Include `Brewfile` ,`~/.config/*` ,`~/.claude/` , and a`bootstrap.sh` that replays PHASES 1–8 on a fresh Mac.**`bootstrap.sh` in this gist is a working starting point** — bash 3.2 compatible (macOS\nships bash 3.2, so no associative arrays /`mapfile` /`${var,,}` ),`--dry-run` capable,\nnever calls sudo, and prints a \"Needs a human\" list for the GUI-only steps.\n3. Write a short `SETUP.md` : what was installed,**what was deliberately skipped and why** ,\nwhat still needs the user's hands (GUI logins, App Store, MDM-blocked items).\n4. Final report format:\n  - ✅ Done (with proof)\n  - ⏭️ Skipped + reason\n  - ⚠️ Needs a human\n  - 📊 `zsh` startup ms,`brew list | wc -l` , disk used\n\n- Installing 80 brew packages \"because the gist said so\". Every tool is context the user must hold. If they won't use it this month, skip it and note it in SETUP.md.\n- Pasting a 30-line bundle file in by reflex. Every non-deferred entry is sourced on every\nshell start. Bundle what you use, profile with `zprof` ,`kind:defer` or drop the rest.\n- Running two version managers for one language (mise *and* fnm for Node). Whichever`eval` s last wins the`PATH` , so your version silently depends on`.zshrc` line order.\n- Curl-pipe-bash from a URL you haven't read.\n- Symlinking a dotfiles repo over existing config without backing up.\n- Claiming a phase is done without output. Run the command. Show the result.\n- Editing `/etc/*` or running`sudo` without asking.\n- Putting an API key in `.zshrc` and then committing`.zshrc` to a public dotfiles repo.\nIf you keep credentials in shell config (see`claude-wrapper.zsh` ),`chmod 600` it and\nsplit the secrets into a gitignored`~/.claude-auth.zsh` that`.zshrc` sources.\n\nIf you're a fresh agent picking this up: `cat ~/.setup-state.json`, then continue from\n`phase`. Re-running completed phases must be harmless — that's what RULE 1 is for.\n\n- Ghostty + Starship modern terminal setup — [https://alexanderkey.com/blog/how-to-set-up-a-modern-terminal-with-ghostty-zsh-and-starship/](https://alexanderkey.com/blog/how-to-set-up-a-modern-terminal-with-ghostty-zsh-and-starship/)\n- The Ultimate Terminal Stack in 2026 — [https://medium.com/vmacwrites/the-ultimate-terminal-stack-in-2026-a-cross-platform-guide-for-macos-linux-and-windows-c0d1f93cd9cc](https://medium.com/vmacwrites/the-ultimate-terminal-stack-in-2026-a-cross-platform-guide-for-macos-linux-and-windows-c0d1f93cd9cc)\n- Modern Rust CLI tools (eza, bat, fd, zoxide…) — [https://32blog.com/en/cli/cli-modern-rust-tools](https://32blog.com/en/cli/cli-modern-rust-tools)\n- antidote — [https://antidote.sh/](https://antidote.sh/) ·  bundle annotations:[https://github.com/mattmc3/antidote](https://github.com/mattmc3/antidote)\n- Oh My Zsh plugin directory (loadable via antidote) — [https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins](https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins)\n- starship config reference — [https://starship.rs/config/](https://starship.rs/config/)\n- fnm — [https://github.com/Schniz/fnm](https://github.com/Schniz/fnm)\n- uv: managing Python versions — [https://docs.astral.sh/uv/concepts/python-versions/](https://docs.astral.sh/uv/concepts/python-versions/)\n- Go toolchains (GOTOOLCHAIN) — [https://go.dev/doc/toolchain](https://go.dev/doc/toolchain)\n- Claude Code IDE integrations — [https://developertoolkit.ai/en/claude-code/quick-start/ide-integration/](https://developertoolkit.ai/en/claude-code/quick-start/ide-integration/)\n- Zed vs every editor — [https://zed.dev/compare](https://zed.dev/compare)\n- Powerlevel10k (alternative prompt) — [https://github.com/romkatv/powerlevel10k](https://github.com/romkatv/powerlevel10k)\n- mise (for the long tail) — [https://mise.jdx.dev/](https://mise.jdx.dev/)\n- Claude Code + MCP guide 2026 — [https://www.jitendrazaa.com/blog/ai/claude-code-complete-guide-2026-from-basics-to-advanced-mcp-2/](https://www.jitendrazaa.com/blog/ai/claude-code-complete-guide-2026-from-basics-to-advanced-mcp-2/)\n- Best MCP servers for Claude Code 2026 — [https://evomap.ai/blog/best-mcp-servers-for-claude-code-2026](https://evomap.ai/blog/best-mcp-servers-for-claude-code-2026)", "url": "https://wpnews.pro/news/claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native", "canonical_source": "https://gist.github.com/lxyea/bbff8b5685cdf4238c3ff5b374169bbc", "published_at": "2026-09-18 06:23:24+00:00", "updated_at": "2026-09-18 06:24:37.185200+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents", "ai-products"], "entities": ["Claude Code", "Anthropic", "Apple", "MacBook Pro", "Homebrew", "Ghostty", "OrbStack", "chezmoi"], "alternates": {"html": "https://wpnews.pro/news/claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native", "markdown": "https://wpnews.pro/news/claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native.md", "text": "https://wpnews.pro/news/claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native.txt", "jsonld": "https://wpnews.pro/news/claude-code-prompt-set-up-a-new-macbook-pro-2026-as-a-power-terminal-ai-native.jsonld"}}