You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adam — M5 Max install + config, fully explained (2026-05-27)
Tip
TL;DR — if you just want it done: clone this gist and run :
# HTTPS clone — works on a fresh Mac. `gh gist clone` uses SSH and fails until# gist.github.com host-key is trusted, which it isn't on a clean install.
git clone https://gist.github.com/fad46568b34a09bf453fd48a57123252.git ~/m5-setup
cd~/m5-setup
chmod +x setup.sh verify.sh
brew bundle install --file=Brewfile
./setup.sh
setup.sh does EVERYTHING in this doc automatically — writes all configs, generates SSH key, uploads to GitHub via gh, pulls Ollama models, sets sysctl, etc. The walkthrough below explains what each step does in case you want to understand or do it piece-by-piece.
Note
Step-by-step setup where every command is explained. Not "paste this", but "here's what this does to your Mac and why". For when you want to understand, not just copy.
Note
Where we are — 2026-05-27 18:18 · PASS 108 / FAIL 18 / WARN 9
1 Audit
2 Install
3 Ghostty
4 Claude
5 Verify
7 Cleanup
8 Review
✅
⬜
⬜
⬜
⬜
⬜
⬜
Next: Prompt 2 (Install) — 18 items missing from the spec.
Full detail:00-STATUS.md · regen with ./verify.sh --status-md. Edit this block by hand as you go, or let Prompts 1/2/5/7 refresh 00-STATUS.md for you.
🎛️ Command Center — 8 paste-ready prompts
This section is the operational interface for the M5 install. The walkthrough below is the reference doc; these 8 prompts are what you actually paste into Claude Code on the new machine.
How to use: Open Claude Code in any fresh session on the M5. Read the conversational note above the prompt you want, then copy the entire code block (the lines between the triple backticks) and paste it. Each block carries its own Hard Context, so a fresh session can run it cold with no prior conversation history.
The eight prompts cover one full setup cycle, in order:
#
Prompt
What it does
When to run
0
Status
Read 00-STATUS.md (the 5th gist file) to see ✅ done / 🟡 partial / ⬜ todo / ⚠️ drift for every phase. Regenerable via ~/m5-setup/verify.sh --status-md
Any time — start here on every session
1
Audit
Reads current state of the Mac, writes a 3-bucket report
First thing on the new machine
2
Install
Installs everything from the MISSING list
After Prompt 1
3
Ghostty Polish
Verifies the 10-item polish block is present in Ghostty config (appends if missing)
After Prompt 2 (config must exist)
4
Claude Code Config
Configures Claude Code itself: settings, env, base MCPs
After Prompt 2
5
Verification
Smoke-tests the critical paths end-to-end
After Prompts 2-4
6
Debugging
When something breaks, investigate scientifically
Any time after install
7
Cleanup + Docs
Removes dropped items, syncs docs with reality
Any time after install
8
Final Review
Adversarial pass over the gist itself
Before declaring done
Tip
The quickest "where am I" check: open 00-STATUS.md in the gist (or in ~/m5-setup/00-STATUS.md locally). It shows what's done, what's pending, and which prompt to run next. Regenerated by running ~/m5-setup/verify.sh --status-md — every Prompt 1, 2, 5, or 7 run refreshes it.
This is scoped to installation, setup, and configuration of a clean M5 only. No project repositories are cloned yet, no dotfiles are imported from the old Mac, no project-specific plugins are installed. That comes in a separate cycle once the base is solid.
Important
Paste the whole code block, including the HARD CONTEXT lines at the top. The conversational text and tables above each block are for you, the human — don't paste those.
Prompt 1 — Audit (read-only)
The first thing you run on the new Mac. This prompt is purely diagnostic — Claude reads the current state, runs the gist's verify.sh against this machine, and writes a clean three-bucket report you can review before any installs happen. Nothing gets changed; nothing gets installed. On a freshly-unboxed M5 you should expect a long MISSING list (most of the spec) and a short WORKING list (Xcode CLI, Homebrew if you pre-installed it, defaults).
When to use: First action on the new Mac, or any time you want a "where am I" snapshot.
Expected outcome: A markdown report at /tmp/m5-audit-YYYYMMDD.md showing WORKING / MISSING / CLEANUP. Prompt 2 reads this file.
/goal — Audit my M5 Max against the gist spec. READ-ONLY.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
Clone if missing (HTTPS — `gh gist clone` fails on fresh Mac because
the gist.github.com SSH host-key isn't trusted yet):
git clone https://gist.github.com/fad46568b34a09bf453fd48a57123252.git ~/m5-setup
- Files in gist: adam-mac-setup.md (this walkthrough), Brewfile, setup.sh, verify.sh, 00-STATUS.md
LOCKED DECISIONS (do NOT re-debate):
- Clean install · no Migration Assistant · no project import
- Ghostty + Antidote + Starship + Atuin + mise + uv
- Brave primary / Chrome backup / Firefox testing
- VS Code (chosen over Cursor for unrestricted local-LLM use via Continue.dev)
- Obsidian for ALL notes (no Notion)
- Apple Music (no Spotify)
- OrbStack (no Docker Desktop)
- Ollama at localhost:11434 = LOCAL MODELS ONLY. `claude` (Anthropic CLI, Max sub)
stays on cloud — `ANTHROPIC_BASE_URL` MUST remain unset. Local-model coding uses
a SEPARATE tool (opencode primary, Crush fallback) wired to Ollama via its
own config, NEVER by repointing `claude`.
- whisper-cpp ONLY (NEVER `pip install openai-whisper`)
- Foundry via curl installer (foundryup → ~/.foundry/bin), NOT brew — matches M5 reality
- sui + supabase now in homebrew-core (no taps)
- iogpu.wired_limit_mb=122880 (120 GB cap — NEVER 124000)
- Dropped (do not re-add): mstystudio, msty, rectangle, sleeve, wezterm, procs, bottom, httpie, spotify
RULES:
- Mnemosyne: every "latest"/"best" claim must trace to a primary URL
(formulae.brew.sh, github releases, vendor changelog). Tag [UNVERIFIED] otherwise.
- Git identity: commit as Adam Boudjemaa <boudjemaa.adam@gmail.com>.
NEVER use `-c user.name=...` overrides on `git commit`.
- This is a FRESH machine. No project repos cloned yet. No dotfile imports.
Pure install + setup + config.
DO:
1. Clone the gist to ~/m5-setup if it isn't already there:
git clone https://gist.github.com/fad46568b34a09bf453fd48a57123252.git ~/m5-setup
2. chmod +x ~/m5-setup/verify.sh ~/m5-setup/setup.sh
3. Run ~/m5-setup/verify.sh > /tmp/verify-$(date +%Y%m%d-%H%M).log 2>&1
4. Independent checks (run in parallel where possible):
- claude --version + which claude
- gh auth status
- mise current
- ollama list
- forge/cast/anvil --version
- git config --global --get user.signingkey + gpg.format
- sw_vers, df -h /, uname -m
5. Write /tmp/m5-audit-$(date +%Y%m%d).md with three buckets:
✅ WORKING (every PASS line), ❌ MISSING (every FAIL + the exact TO_INSTALL command),
⚠️ CLEANUP (every WARN + the TO_REMOVE command + the macOS defaults that need applying).
Rank items in MISSING by load-bearing-ness: (1) blocks other tools (e.g. mise blocks node
blocks Claude Code), (2) blocks daily workflow (Ghostty, Claude Code),
(3) one-off polish (CleanShot, Shottr, Stats, etc).
DO NOT:
- Install anything
- Edit any config file
- Run any TO_REMOVE / uninstall commands
- Re-debate any locked decision
SUCCESS:
- /tmp/m5-audit-YYYYMMDD.md exists with all three sections populated
- Audit names file:line for every FAIL so the next session can act precisely
- One-paragraph end-of-turn summary: PASS/FAIL/WARN counts + the top 5 most
load-bearing missing items
Prompt 2 — Install (idempotent)
The heavy lifter. After the audit identifies what's missing, this prompt fills the gap. It walks through every MISSING item from the audit, runs the exact install command, and logs the result. Idempotent — re-running it after a partial install (you closed the lid, you switched networks) safely picks up where it left off. Expect ~30 minutes of wall-clock on a fast connection and roughly 10 GB of downloads. Some items will surface as manual blockers (Hazel license, VS Code Settings Sync OAuth, 1Password OAuth) — those stop and ask, they don't fake completion.
When to use: Right after Prompt 1, with /tmp/m5-audit-*.md on disk.
Expected outcome:verify.sh re-run shows ≤5 FAILs (license-gated and OAuth-blocked items only). Install log at /tmp/m5-install-fix.log.
/goal — Continue M5 setup from MISSING list in /tmp/m5-audit-*.md. Idempotent.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup (clone the Gist URL above + `.git` if missing)
- Files: adam-mac-setup.md, Brewfile, setup.sh, verify.sh, 00-STATUS.md
LOCKED DECISIONS (do NOT re-debate):
- Clean install · no Migration Assistant · no project import
- Ghostty + Antidote + Starship + Atuin + mise + uv
- Brave primary / Chrome backup / Firefox testing
- VS Code (chosen over Cursor for unrestricted local-LLM use via Continue.dev)
- Obsidian for ALL notes (no Notion)
- Apple Music (no Spotify)
- OrbStack (no Docker Desktop)
- Ollama at localhost:11434 = LOCAL MODELS ONLY (drives opencode/Crush/Continue.dev,
NEVER the `claude` CLI — that stays on Anthropic cloud)
- ANTHROPIC_BASE_URL must stay UNSET — never export, never add to ~/.zshrc
- whisper-cpp ONLY (NEVER `pip install openai-whisper`)
- Foundry via curl installer (foundryup → ~/.foundry/bin), NOT brew
- sui + supabase in homebrew-core now (no taps)
- iogpu.wired_limit_mb=122880 (120 GB cap — NEVER 124000)
- Dropped: mstystudio, msty, rectangle, sleeve, wezterm, procs, bottom, httpie, spotify, notion, notion-calendar
RULES:
- Mnemosyne: tag [UNVERIFIED] anything not traceable to a primary source
- Git identity: Adam Boudjemaa <boudjemaa.adam@gmail.com>, no -c overrides
- FRESH machine: pure install/setup/config, no project imports
DO:
1. Read /tmp/m5-audit-YYYYMMDD.md (or re-run ~/m5-setup/verify.sh if missing)
2. Each MISSING formula/cask: `brew install <name>` — skip if `brew list <name>` succeeds
3. For tap-namespaced casks: `brew tap` first, THEN install:
- cyfrin/tap → aderyn · stripe/stripe-cli → stripe
- DO NOT re-add mystenlabs/tap or supabase/tap (moved to homebrew-core)
4. Python tools: `uv tool install slither-analyzer halmos eth-wake`
5. Activate mise THIS shell first (mandatory before npm; node lives in mise):
eval "$(mise activate zsh)"
Then npm globals (vercel+pnpm required by verify.sh):
npm install -g @anthropic-ai/claude-code vercel pnpm
Skip packages already in `npm list -g --depth=0`.
6. Ollama: `ollama pull huihui_ai/qwen3-abliterated:30b huihui_ai/qwen3-coder-abliterated:30b-a3b-instruct-q8_0 nomic-embed-text` —
skip pulls already in `ollama list`
7. Foundry: if `forge` is missing, `curl -L https://foundry.paradigm.xyz | bash && foundryup`
8. macOS defaults from setup.sh §9 (Finder hidden, KeyRepeat=2, dock autohide, smart dashes OFF)
9. iogpu cap (asks sudo — surface to user, no silent-fail):
sudo sysctl iogpu.wired_limit_mb=122880
Persist via LaunchDaemon (Tahoe skips /etc/sysctl.conf). setup.sh §13 writes
/Library/LaunchDaemons/com.adam.iogpu-wired-limit.plist and loads it.
10. For each config missing from audit, re-run the relevant setup.sh block
(uses `backup_if_exists`, preserves existing configs)
11. Log every install command + exit code + timestamp to /tmp/m5-install-fix.log
DO NOT:
- `brew uninstall` ANYTHING (cleanup is Prompt 7)
- Overwrite ~/.zshrc / ~/.gitconfig / ~/.config/* without a .bak first
- Use `sudo brew install` (brew refuses; investigate if seen)
- Re-pull Ollama models already present
- Skip `xcode-select -p` foundation check before anything
- Install spotify (Apple Music is locked)
- Export ANTHROPIC_BASE_URL anywhere (keep `claude` on Anthropic cloud)
- Continue past a manual blocker (Hazel, VS Code OAuth, 1Password OAuth);
STOP and surface to user
SUCCESS:
- Re-run ~/m5-setup/verify.sh exits 0 OR shows only license-gated items
- /tmp/m5-install-fix.log has every command + exit code + timestamp
- One-paragraph end-of-turn: N installed, M skipped, K blockers surfaced
(with specific action human needs)
Prompt 3 — Ghostty Polish (append only)
A safety-net top-up for the Ghostty config. The current setup.sh already bakes the v2 polish into the GHOSTTY heredoc (Section 21.3), so on a fresh M5 install this prompt is essentially a no-op. It exists for two cases: (a) the Ghostty config was written by an older setup.sh that pre-dates 2026-05-27 and needs the 10 items appended, or (b) someone hand-edited the config and the marker block went missing. The prompt verifies first, repairs only if needed — never overwrites.
When to use: After Prompt 2 if ~/.config/ghostty/config exists but lacks the v2 polish marker. Skipped automatically on fresh installs.
Expected outcome: No-op on a fresh M5 (polish already present), or 10 new lines appended on an older config. Takes <30 seconds either way.
/goal — Append Ghostty v2 polish (10 items). APPEND ONLY — never overwrite.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
- Files: adam-mac-setup.md, Brewfile, setup.sh, verify.sh, 00-STATUS.md
- This prompt operates on ~/.config/ghostty/config (written by setup.sh §7)
LOCKED DECISIONS:
- Theme = cyberdream (paint Neon Noir cyan/magenta/violet OVER it)
- Liquid Glass: background-opacity 0.85, background-blur macos-glass-regular
- Font = JetBrainsMono Nerd Font, size 14, thicken on
- The 10-item polish set is locked (Scout A, 2026-05-27, sourced to ghostty.org/docs)
- Append only — heredoc `>>` is the only allowed redirect, never `>`
RULES:
- Mnemosyne: every option must trace to ghostty.org/docs/config/reference
or the 1.3.0 release notes. Tag [UNVERIFIED] otherwise.
- Git identity: Adam Boudjemaa <boudjemaa.adam@gmail.com>, no -c overrides
- FRESH machine: no project context required for this prompt
DO:
1. If ~/.config/ghostty/config does NOT exist, STOP — surface to user that
they need to run Prompt 2 (Install) first. Do not create the file from scratch.
2. Back up: cp ~/.config/ghostty/config ~/.config/ghostty/config.bak-$(date +%s)
3. STOP-check: if `grep -q "v2 polish" ~/.config/ghostty/config` succeeds,
the polish is already applied — exit cleanly without re-appending
4. Append the 10-item polish block via:
cat >> ~/.config/ghostty/config <<'POLISH'
# === v2 polish (Scout A, 2026-05-27) — 10 verified items, all sourced to ghostty.org/docs ===
keybind = global:cmd+grave_accent=toggle_quick_terminal
quick-terminal-position = top
quick-terminal-animation-duration = 0.18
quick-terminal-screen = main
quick-terminal-autohide = true
keybind = cmd+shift+p=toggle_command_palette
title-command = printf '%s %s' "$(basename "$PWD")" "$(git branch --show-current 2>/dev/null)"
bell-features = visual,audio,no-system
bell-audio-volume = 0.4
notify-on-command-finish = unfocused
notify-on-command-finish-action = notify
notify-on-command-finish-after = 10s
window-inherit-working-directory = true
tab-inherit-working-directory = true
split-inherit-working-directory = true
scrollbar = system
macos-option-as-alt = left
unfocused-split-fill = #0A0A14
resize-overlay = after-first
window-padding-color = extend
alpha-blending = native
POLISH
5. Verify the bottom of the file: `tail -25 ~/.config/ghostty/config`
6. Reload Ghostty: `cmd+shift+,` (reload-config keybind) or quit + relaunch
DO NOT:
- Overwrite ~/.config/ghostty/config (heredoc `>` is forbidden — only `>>`)
- Rewrite the theme (cyberdream stays)
- Touch ~/.config/ghostty/themes/neon-noir
- Add options outside the 10-item set (every option must trace to a primary doc)
- Re-bind cmd+t / cmd+d / cmd+w / cmd+enter (already in base config)
SUCCESS:
- `grep -c "v2 polish" ~/.config/ghostty/config` returns ≥1
- Backup file ~/.config/ghostty/config.bak-NNNNNN exists
- Quick Terminal opens with cmd+` from any app in macOS
- Command Palette opens with cmd+shift+p inside Ghostty
- Tab title shows `<dirname> <branch>` (not literal `$(basename...)`).
If the title shows the literal command string, confirm
`shell-integration-features = title` is set higher up in the config (it should be)
Prompt 4 — Claude Code Config (base only)
The Claude Code essentials, nothing project-specific. This prompt is for the brand-new M5: Claude Code is installed by Prompt 2, but it still needs its env vars wired into ~/.zshrc, its settings file pinned to the right model, the Ghostty multi-line paste workaround alias (cc), and a small set of generic MCPs that are useful in any project (filesystem, GitHub, Brave Search, Context7). No project-specific plugins yet — those go in a separate cycle once you've actually cloned project repos. Think of this as "Claude Code is now usable on this Mac with zero project context."
When to use: After Prompt 2 has installed @anthropic-ai/claude-code, before you clone any project repos.
Expected outcome:claude starts cleanly, claude mcp list shows 4-5 generic MCPs, the cc shell alias works in Ghostty.
/goal — Configure Claude Code base on a fresh M5. Generic MCPs only — no project plugins.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
LOCKED DECISIONS:
- Claude Code via npm globals: @anthropic-ai/claude-code
- Settings file: ~/.claude/settings.json (model pin + hooks + permissions)
- Shell alias: `cc='TERM=xterm-256color claude'` (Ghostty multi-line paste fix)
- Env vars in ~/.zshrc: CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1,
CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=15 (matches setup.sh + settings.json)
- FRESH machine — GENERIC MCPs only. Project-specific plugins out of scope.
RULES:
- Mnemosyne: every MCP needs a verifiable upstream URL. Tag [UNVERIFIED] otherwise.
- Git identity: Adam Boudjemaa <boudjemaa.adam@gmail.com>, no -c overrides
- No project-specific plugins (project repos don't exist on a fresh M5).
DO:
1. Sanity: `claude --version` returns 2.x. If missing, surface and stop.
2. Confirm gh auth: `gh auth status`. If not, surface — user runs `gh auth login`.
3. Write ~/.claude/settings.json from setup.sh §7 template:
- model pinned to current Opus suffix (read setup.sh verbatim — names rotate)
- permissions: Bash, Read, Edit, Write, Grep, Glob allowed
- hooks block if template has one
Backup first: cp settings.json settings.json.bak-$(date +%s)
4. Append to ~/.zshrc (skip if marker already present):
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=15
alias cc='TERM=xterm-256color claude' # Ghostty multi-line paste workaround
5. Install GENERIC base MCPs only.
Always-on (no key):
claude mcp add --transport stdio --scope user filesystem -- npx -y @modelcontextprotocol/server-filesystem ~
claude mcp add --transport http --scope user context7 https://mcp.context7.com/mcp
claude mcp add --transport stdio --scope user sequential-thinking -- npx -y @modelcontextprotocol/server-sequential-thinking
Key-gated (skip with warning if env var absent):
[ -n "$GITHUB_PERSONAL_ACCESS_TOKEN" ] && claude mcp add --transport stdio --scope user github -- npx -y @modelcontextprotocol/server-github \
|| echo "skip github MCP — set GITHUB_PERSONAL_ACCESS_TOKEN first"
[ -n "$BRAVE_API_KEY" ] && claude mcp add --transport stdio --scope user brave-search -- npx -y @modelcontextprotocol/server-brave-search \
|| echo "skip brave-search MCP — set BRAVE_API_KEY first"
6. Verify `claude mcp list` — expect 4-5 entries (filesystem, github, context7, sequential-thinking, optionally brave-search).
7. Smoke test in fresh Ghostty tab: `cc` launches Claude Code with TERM=xterm-256color (paste multi-line block — newlines must survive).
DO NOT:
- Set ANTHROPIC_BASE_URL or ANTHROPIC_AUTH_TOKEN — even commented out. `claude` MUST stay on Anthropic cloud (Claude Max). Local-model CLI is separate (§22.5).
- Install any project-specific plugin (no project repos on a fresh M5).
- Re-install Claude Code (Prompt 2 handled it)
- Skip the `claude --version` sanity check
- Overwrite ~/.claude/settings.json without backing it up first
- Guess a model name suffix — read setup.sh verbatim
- Add MCPs requiring API keys the user hasn't exported
SUCCESS:
- `claude --version` shows 2.x
- `claude mcp list` shows ≥4 generic MCPs
- `cc` alias works in Ghostty (multi-line paste preserves newlines)
- ~/.zshrc has the 2 CLAUDE_CODE_* env vars + `cc` alias
- ~/.claude/settings.json exists and parses as valid JSON (`jq . settings.json`)
- One-paragraph end-of-turn: N MCPs added, M skipped (no API key), settings written, alias active — Claude Code base-ready, project plugins deferred
Prompt 5 — Verification (smoke test)
Trust but verify.verify.sh from the gist checks installation completeness (is X on PATH?), but it doesn't check function (does X actually work end-to-end?). This prompt runs both: the full verify.sh plus 8 functional smoke tests covering the load-bearing tools — Foundry, Ollama, whisper-cpp, SSH→GitHub, Claude Code, mise runtimes, the GPU memory cap, and MLX Metal. Read-only, surfaces blockers, doesn't try to fix them.
When to use: After Prompts 2-4. Re-run any time you suspect drift.
Expected outcome: A green report at /tmp/m5-verify-YYYYMMDD.md or a precise list of what's still broken and how to fix it.
/goal — Verify M5 setup end-to-end. Read-only smoke test.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
LOCKED DECISIONS (do NOT re-debate):
- iogpu.wired_limit_mb must equal 122880 (120 GB cap on 128 GB Mac)
- whisper-cpp ONLY (the pip whisper path is broken on Apple Silicon)
- Foundry: forge/cast/anvil all v1.7+
- Ollama at localhost:11434, models huihui_ai/qwen3-coder-abliterated:30b-a3b-instruct-q8_0 + nomic-embed-text minimum.
Verify `echo "$ANTHROPIC_BASE_URL"` returns EMPTY (else claude is hijacked to local).
RULES:
- Read-only — verification surfaces failures, does NOT fix them
- Mnemosyne: tag [UNVERIFIED] anything you can't confirm directly
- FRESH machine: tests must not assume any project repo exists
DO (run in parallel where possible):
1. Re-run ~/m5-setup/verify.sh > /tmp/verify-final-$(date +%Y%m%d-%H%M).log 2>&1
2. Smoke tests (8 functional checks):
a. forge --version && cast --version && anvil --version → all v1.7+
b. ollama list → confirms service running + lists installed models
c. Whisper functional test:
say "this is a test of the whisper system" -o /tmp/test.aiff
ffmpeg -y -i /tmp/test.aiff -ar 16000 -ac 1 -c:a pcm_s16le /tmp/test.wav
whisper-cli -m ~/.cache/whisper-cpp/ggml-large-v3-turbo.bin -f /tmp/test.wav -l en
→ expect transcript with "test" in it within ~5 seconds
d. ssh -T git@github.com 2>&1 | grep "successfully authenticated"
e. claude --version → returns 2.x cleanly
f. mise current → shows node, python, rust, go all pinned (not empty)
g. sysctl iogpu.wired_limit_mb → returns 122880
h. ~/1_PROJECTS/mlx-lab/.venv/bin/python -c "import mlx.core as mx; print(mx.metal.is_available())"
→ returns True
3. Ghostty interactive checks (ask user to confirm):
- cmd+` opens Quick Terminal with slide animation
- cmd+shift+p opens Command Palette
- Tab title shows <dirname> <branch> (not literal command string)
4. Claude Code check: `claude mcp list` shows the ≥4 generic MCPs from Prompt 4
5. Write /tmp/m5-verify-$(date +%Y%m%d).md with PASS/FAIL per check.
For every FAIL, include the exact remediation command.
DO NOT:
- Install or uninstall anything (this is diagnostic only)
- Edit a config to "fix" a FAIL — surface it, stop, hand off to Prompt 6 (Debugging)
- Trust a green verify.sh alone without running the 8 functional smoke tests
(verify.sh tests presence, smoke tests test function)
- Run smoke test (c) if no microphone available — `say` works headless but flag it
SUCCESS:
- verify.sh exits 0
- All 8 functional smoke tests pass, OR each FAIL has a precise remediation noted
- /tmp/m5-verify-YYYYMMDD.md is on disk with a final PASS/FAIL tally
- End-of-turn line is either "✅ All green" OR "⚠️ N failures:" + bulleted list
Prompt 6 — Debugging (investigate, don't rebuild)
When something breaks, resist the urge to nuke and rebuild. This prompt is the scientific-method version of "fix it": capture the symptom precisely, form a written hypothesis before changing anything, test the hypothesis with the smallest possible experiment, apply ONE fix at a time, and re-verify. The output is a debug journal at /tmp/m5-debug-*.md that documents the whole loop — useful for future-you and useful if the fix turns out to be wrong.
When to use: Anytime a tool stops working or a verify check flips from green to red.
Expected outcome: A root cause in writing, one fix applied, verify.sh is green again. Wall-clock varies — minutes for simple PATH issues, hours for genuine drift.
/goal — A tool is broken. Investigate scientifically. Don't rebuild.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
LOCKED DECISIONS:
- `setup.sh` is NOT a debugger — running it again overwrites configs.
Only re-run setup.sh blocks individually after you know what's wrong.
- iogpu.wired_limit_mb sysctl needs sudo — always surface to user, never silent-sudo
RULES:
- One hypothesis at a time. One fix at a time. Re-test between each.
- Mnemosyne: every claim about "it worked before" must be verified against
the current binary, not against memory. Tag [UNVERIFIED] otherwise.
- Git identity: Adam Boudjemaa <boudjemaa.adam@gmail.com>, no -c overrides
- FRESH machine context: no project repos exist yet
DO:
1. Capture the symptom precisely. Open /tmp/m5-debug-$(date +%Y%m%d-%H%M).md and write:
- Exact command run
- Exact error message (stderr verbatim)
- Exit code
- Environment: echo $TERM $SHELL, which <bin>, brew --version, uname -a, sw_vers
2. Form a hypothesis BEFORE touching anything. Write in the debug file:
Hypothesis: <X> is broken because <Y>.
Evidence: <Z>.
3. Falsify the hypothesis with the smallest possible test. Some lenses:
- Diff /tmp/verify-*.log between the last-known-good run and now
- `brew doctor` + `brew config` to surface PATH / arch leakage
- `cat ~/Library/Logs/Homebrew/<formula>/*.log` for build-time failures
- `dscl . -read /Users/$USER` for unexpected shell config
- `launchctl list | grep <name>` if it's a service
- Run the failing command with `--verbose` or strace-equivalent
- Bisect via `brew uninstall` + `brew install` ONLY when all other paths are exhausted
4. Document each test + result in the debug file. Number them.
5. Only AFTER the root cause is written down: apply ONE fix.
6. Re-run ~/m5-setup/verify.sh to confirm no regression elsewhere.
DO NOT:
- Run setup.sh as a "general reset" — it rewrites configs and hides drift
- `brew reinstall` without first reading the failure log
- Touch sysctl iogpu.wired_limit_mb without sudo confirmation from the user
- Delete ~/.config/<tool>/ without backing up first (cp <dir> <dir>.bak-$(date +%s))
- Apply more than one fix per test cycle — you'll never know which one worked
- Trust "it worked before" without re-running against the current binary
SUCCESS:
- /tmp/m5-debug-YYYYMMDD-HHMM.md has all five sections:
Symptom / Hypothesis / Tests / Root cause / Fix
- The fix is reversible (a backup exists, or the change is a single config line)
- verify.sh is green OR cleanly explains the remaining FAIL with a doc reason
- One-paragraph end-of-turn: "Root cause: <X>. Fix: <Y>. Verified by: <Z>."
Prompt 7 — Cleanup + Documentation
Periodic hygiene — two separate jobs. This prompt bundles (1) uninstall cleanup of dropped apps that verify.sh flags as WARN, and (2) doc-sync edits where the gist files have drifted from current reality. Both happen with explicit y/N from the human — no silent uninstalls, no silent gist pushes. After uninstalls, brew cleanup frees disk; after gist edits, gh gist edit pushes each file individually with a commit message describing the drift.
When to use: Monthly hygiene, or right after a verify run that shows new WARNs, or after you've noticed a gist file is wrong.
Expected outcome: Zero WARN entries from verify.sh, gist files updated and pushed. Wall-clock ~10-20 minutes (most time is waiting for y/N responses).
/goal — Remove dropped items + sync gist docs with reality.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
- Files in gist: adam-mac-setup.md, Brewfile, setup.sh, verify.sh, 00-STATUS.md
LOCKED DECISIONS:
- Dropped (do NOT re-add): mstystudio, msty, rectangle, sleeve, wezterm,
procs, bottom, httpie, spotify, docker (Docker Desktop), cursor, tableplus, gcloud-cli
- VS Code (Continue.dev) replaces Cursor · OrbStack replaces Docker Desktop · Apple Music replaces Spotify
- whisper-cpp replaces openai-whisper
RULES:
- Every uninstall asks for y/N confirmation per item — NEVER batch-uninstall silently
- Mnemosyne: every "drift" claim must show file:line and the contradicting reality
- Git identity: commits to the gist as Adam Boudjemaa <boudjemaa.adam@gmail.com>
- FRESH machine: no project repos in ~/projects yet — leave that path alone
DO:
1. Re-run ~/m5-setup/verify.sh — capture the WARN list and TO_REMOVE candidates
2. For each TO_REMOVE candidate, STOP and surface the list to the human — wait for
an explicit y/N reply per item. Do NOT batch. Likely candidates:
brew uninstall --cask mstystudio
brew uninstall --cask msty
brew uninstall --cask notion notion-calendar (dropped 2026-05-27)
brew uninstall --cask slack signal (dropped 2026-05-27)
brew uninstall --cask spotify (only if user confirms Apple Music is in use)
brew uninstall --cask docker (only if OrbStack is present)
brew uninstall --cask cursor (VS Code + Continue.dev is the chosen editor)
3. After confirmed uninstalls: `brew cleanup --prune=all` to free disk
4. Cross-check gist files vs current reality:
- Brewfile: any cask/formula renamed since last edit? (use formulae.brew.sh)
- adam-mac-setup.md: any Step's tool deprecated, renamed, or replaced?
- setup.sh: any heredoc writes a config the walkthrough no longer describes?
- verify.sh: any check that fails-by-design (false positive)?
5. For each drift, edit the file LOCALLY in ~/m5-setup/ then push:
gh gist edit fad46568b34a09bf453fd48a57123252 -f <filename> ~/m5-setup/<filename>
(one push per file — gh gist edit accepts one file at a time)
6. After all pushes, re-fetch to confirm: gh gist view fad46568b34a09bf453fd48a57123252 -f <filename> | wc -l
DO NOT:
- brew uninstall ANYTHING without explicit y/N from the user — even items in the
dropped list (the user may have a current exception in mind)
- `brew cleanup -s` (aggressive — wipes unfinished downloads)
- Delete config files outside ~/.config/<tool>/ paths
- Re-add Rectangle / Spotify / mstystudio / msty / wezterm / Docker Desktop / VS Code
to the gist files even if you find them used somewhere — the drop is intentional
- Push gist updates with a generic commit message — describe WHAT drift you fixed
SUCCESS:
- ~/m5-setup/verify.sh shows 0 WARN entries
- All 4 gist files reflect current binary reality
- The gist commit log shows precise change descriptions for each file pushed
(gh gist view fad46568b34a09bf453fd48a57123252 to read)
- One-paragraph end-of-turn: N items removed, M doc drifts patched, K bytes
freed by `brew cleanup`
Prompt 8 — Final Review (adversarial)
The last line of defense against quietly-broken setups. This prompt is read-only and ruthless: it treats every claim in the gist as suspect, mentally pastes each of Prompts 1-7 into a hypothetical fresh session with zero prior history, and predicts where each one would fail. It reads every line of the four gist files (no skimming), grades findings as BLOCKER / NIT / FYI, and writes a triage report. Run this before declaring "the M5 is done."
When to use: Before declaring setup complete, or after editing any gist file via Prompt 7.
Expected outcome:/tmp/m5-review-YYYYMMDD.md with triaged findings + a ship/patch/restart verdict. Takes ~5-10 minutes wall-clock.
/goal — Adversarial review of the gist. Find what's broken or hallucinated.
HARD CONTEXT
- Operator: Adam Boudjemaa <boudjemaa.adam@gmail.com> · GitHub: Aboudjem
- Hardware: MacBook M5 Max 14" 128 GB · macOS Tahoe (26.x) · arm64
- Brand: Neon Noir — #00D4FF · #FF006E · #7C3AED · #0A0A14
- Gist: https://gist.github.com/Aboudjem/fad46568b34a09bf453fd48a57123252
- Local clone: ~/m5-setup
- Files in gist: adam-mac-setup.md, Brewfile, setup.sh, verify.sh, 00-STATUS.md
LOCKED DECISIONS (anything contradicting these is by definition a BLOCKER):
- Clean install · no Migration Assistant · no project import
- Ghostty + Antidote + Starship + Atuin + mise + uv
- Brave / Chrome / Firefox · VS Code (Continue.dev) · Obsidian (no Notion) · Apple Music
- OrbStack · Ollama · whisper-cpp · Foundry (curl) · sui/supabase in core
- iogpu.wired_limit_mb = 122880
- Dropped: mstystudio, msty, rectangle, sleeve, wezterm, procs, bottom, httpie, spotify, notion, notion-calendar
RULES:
- Read-only — review surfaces problems, does NOT apply fixes
- Be ruthless: every doubt gets a finding, even minor ones
- Mnemosyne: any "common knowledge" claim without a primary source = automatic flag
- Git identity: Adam Boudjemaa <boudjemaa.adam@gmail.com>, no -c overrides
- FRESH machine: assume the reviewer is on a clean M5 with zero history
DO (read-only; you are a skeptic):
1. Mentally paste each of Prompts 1-7 into a fresh Claude Code session with
ZERO prior conversation history. For each prompt, predict:
- Does it carry all the context it needs, or does it lean on history?
- Are every referenced file path and tool name verified to exist?
- Are all install commands current (no deprecated brew names, no dead URLs)?
- Is the success criterion BINARY and measurable (not "looks right")?
2. Read every line of the four gist files (~/m5-setup/*):
- adam-mac-setup.md — every "see Step X" — does Step X actually exist?
- Brewfile — every cask/formula — fetch `brew info <name>` to confirm current
- setup.sh — every heredoc + every command — does it run cleanly on Tahoe 26.x?
- verify.sh — every check — right field number, right syntax, no shadowed builtins?
3. Flag every:
- Hallucinated command (referenced but doesn't exist)
- Deprecated package name (brew has renamed it)
- Missing dependency chain (Tool A needs Tool B installed first, not enforced)
- Marketing-fluff claim ("premium", "best", "fastest") without a primary source
- Implicit-context assumption (a next session won't know X)
4. Write /tmp/m5-review-$(date +%Y%m%d).md with sections:
## BLOCKER findings (must fix before shipping)
## NIT findings (should fix soon)
## FYI / annotations
## Synthesis — Total count, top 3 load-bearing, verdict (SHIP / PATCH / RESTART)
DO NOT:
- Apply any fix — this prompt is purely review
- Be polite or charitable — every doubt gets a finding
- Trust any claim sourced to "training data" or "common knowledge"
- Skip a file because it "looks fine" — read every line, including comments
SUCCESS:
- /tmp/m5-review-YYYYMMDD.md exists with all 4 sections populated
- Every finding cites file:line precisely
- Each finding rated BLOCKER / NIT / FYI
- End-of-turn: "N BLOCKER · M NIT · K FYI · Verdict: SHIP / PATCH / RESTART"
+ one-paragraph synthesis naming the top 3 load-bearing issues
Important
The walkthrough below is the explained reference. The 8 prompts above are the operational interface. If a prompt mentions "Step X" (e.g. "macOS defaults from setup.sh §9"), that step lives in the walkthrough — never assume the prompt itself contains it.
How to read this
🔍 Each command has an explanation of what it does to your system
📦 Each tool has: what it is + why you want it + what it replaces
⚙️ Each config file is annotated section-by-section
⚠️ Warnings explain the gotchas (drift, breaking changes, kernel panics)
Three patterns you'll see repeated:
brew install <name> → downloads + installs a CLI tool (lives in /opt/homebrew/bin)
brew install --cask <name> → installs a GUI app (lives in /Applications)
defaults write <domain> <key> -<type> <value> → writes a macOS system preference
Before you start — macOS settings explained
Important
Sign in to Apple ID. Turn iCloud Documents & Desktop sync OFF in System Settings → Apple ID → iCloud → Drive. This syncs your entire home folder to the cloud — slow, eats bandwidth, leaks data. FileVault ON for disk encryption.
The defaults command writes macOS preferences directly to plist files instead of clicking through System Settings. Run each block, explanation above each :
🔍 Show hidden files in Finder — macOS hides files starting with . (like .zshrc, .env). You need to see them as a dev.
🔍 Key repeat at maximum speed — when you hold a key, how fast does it repeat? Default is slow. KeyRepeat 2 = ~30ms between repeats. InitialKeyRepeat 15 = hold time before repeat starts (~225ms). Noticeably faster when navigating and editing code.
🔍 Screenshots to a dedicated folder — by default screenshots dump on Desktop. Make a folder for them, point macOS at it. Hazel will later auto-archive old ones.
🔍 Apply all changes — defaults writes to disk but the running apps don't know. killall restarts them so they re-read the prefs.
killall Finder Dock SystemUIServer
Step 1 — Xcode Command Line Tools (the foundation)
Important
This is the foundation everything else needs. Without it: no compiler, no git, no Python C extensions, no Homebrew.
🔍 What this command does — opens a system popup that downloads Apple's developer toolchain (~2 GB) : git, clang (C/C++ compiler), Python headers, SDK files. NOT the full Xcode IDE (that's 12 GB, you don't need it).
xcode-select --install
Click Install in the popup. Wait ~10 min. After, verify :
git --version # should print git version 2.39.x (Apple's lagged version — we'll override later)
xcode-select -p # should print /Library/Developer/CommandLineTools
Step 2 — Homebrew (the package manager)
🔍 What Homebrew is — apt for Mac. Without it, installing dev tools means downloading DMGs one by one. With it, you type brew install <name> and it handles download + install + PATH.
🔍 What this curl pipe does — fetches the official install script from GitHub and runs it. The script :
Detects Apple Silicon (M1/M2/M3/M4/M5) → installs to /opt/homebrew
Asks for your sudo password (creates the /opt/homebrew directory, needs root)
🔍 What those lines do — add Homebrew to your PATH so your shell can find brew. The first one writes it to ~/.zprofile (loaded on login). The second runs it now for this session.
Run them, close the terminal, reopen, then verify :
which brew # MUST output /opt/homebrew/bin/brew
brew --version
Warning
If which brew returns /usr/local/bin/brew, that's the Intel Mac path running through Rosetta translation. You'd waste 30% of every brew command's speed. Re-run the installer.
Step 3 — Terminal: Ghostty
🔍 Why a new terminal — macOS Terminal.app is fine but slow. Ghostty is GPU-rendered via Metal (your M5 has 40 GPU cores), boots in <100ms, supports all modern terminal features.
brew install --cask ghostty
📦 Ghostty v1.3+ → GPU terminal by Mitchell Hashimoto (the guy who made Vagrant + Terraform). Native AppKit, beautiful defaults. Replaces Terminal.app + iTerm2.
Warning
Known issue : multi-line paste in Claude Code breaks on Ghostty (open Anthropic bug #54700). Workaround in Step 9.
Open Ghostty once, press ⌘+, to see config options. We'll write a config file in Step 21.
Step 4 — Shell: zsh + Antidote + Starship + Atuin
macOS already runs zsh by default. We're adding three things : a plugin manager, a prompt, and a history sync.
🔍 The install command — three brew formulae in one line. Faster than 3 separate calls.
brew install antidote starship atuin
📦 Antidote v2.1.0 → zsh plugin manager. Reads a plain-text list of plugins. Much faster than oh-my-zsh (10% first-prompt lag vs 187%, measured by zsh-bench). Simpler than zinit (no DSL).
📦 Starship v1.25.1 → the part of your shell that shows the prompt (adam@mac ~/projects ❯). Adds git branch, language version, command duration. Renders in 5-15ms (vs Powerlevel10k's 100ms+).
📦 Atuin v18.16.1 → replaces bash/zsh history with an encrypted searchable DB. Press Ctrl+R, get fuzzy search across every command you've ever run. Optionally syncs across machines (your Mac + VPS).
We'll wire them into ~/.zshrc in Step 21. For now they're installed but not active.
Step 5 — Modern CLI tools (replace the 1970s Unix defaults)
🔍 The install command — 27 packages. Brew downloads and links them in parallel, takes ~3 minutes.
Colored, shows git status (M modified, ?? untracked), nicer than ls
bat
bat <file>
Prints files with syntax highlighting + line numbers
cat with brain
fd
fd <name>
Finds files by name
3× faster than find, smart defaults, no -name "*.py" syntax
ripgrep
rg <pattern>
Greps inside files
10× faster than grep, respects .gitignore automatically
fzf
Ctrl+R, Ctrl+T
Fuzzy finder
Interactive picker, pipes into anything
zoxide
z <dir>
Smart cd
Learns directories you visit. z proj jumps to your most-used "proj"
lazygit
lazygit
Git TUI
Stage/commit/branch with keyboard only
yazi
yazi
File manager TUI
Vim-keys, image preview, multi-pane
gh
gh pr create
GitHub CLI
Create PRs, view issues, clone repos from terminal
jq
jq '.foo'
JSON processor
Filter/transform JSON in pipelines (curl ... | jq '.items[]')
yq
yq '.foo'
Same but YAML
For Kubernetes/CI configs
tree
tree -L 2
Print directory tree
When you need a visual of a folder structure
htop / btop
htop, btop
Process monitors
top with colors, mouse, killable processes (btop = pretty version)
dust
dust
Disk usage
Visual tree of where disk space is going (vs du -sh *)
duf
duf
Free disk space
Replaces df, color tables per filesystem
direnv
auto
Auto-load .envrc
Walk into a folder with .envrc, it auto-loads env vars. Walk out, they vanish. Magic for per-project secrets
tldr
tldr <cmd>
Short man pages
"How do I tar a folder?" → 3 examples instead of 300 lines of man
gnupg
gpg
GPG keys / encryption
Formula name is gnupg, binary is gpg. Used for git commit signing
mkcert
mkcert example.local
Local HTTPS certs
When you need HTTPS in dev (cookies, CORS)
mas
mas install 12345
Mac App Store CLI
Install paid App Store apps from script
xh
xh GET ...
Rust version of httpie
10× faster startup
doggo
doggo example.com
DNS lookup
dig modernized (the older dog is dead since 2020)
wget
wget <url>
Download files
Resume support, better than curl for big files
watch
watch -n 2 ls
Repeat a command
Re-runs every N seconds, shows the output
imagemagick
magick convert
Image processing
Resize, convert formats, optimize from terminal
yt-dlp
yt-dlp <url>
YouTube downloader
Maintained fork of youtube-dl
ncdu
ncdu
Interactive disk usage
Walk through folders to see what's eating space
🔍 Activate fzf keybindings — fzf is installed but the shell hooks aren't active. This script wires Ctrl+R (history search), Ctrl+T (find file), Alt+C (jump to subdir).
$(brew --prefix)/opt/fzf/install
Accept all 3 defaults (auto-completion, keybindings, update shell config).
Step 6 — Dev runtimes via mise
🔍 What this installs — three things that make polyglot dev sane :
brew install mise uv git
📦 mise (2026.5.15) → polyglot version manager. Replaces nvm (Node), pyenv (Python), rustup (Rust), gvm (Go), asdf (everything). One tool, one config (~/.config/mise/config.toml). Per-project: drop a .mise.toml in your repo, mise auto-switches versions when you cd into it.
📦 uv (0.11.16) → Python package + virtualenv manager by Astral (the Ruff team). Replaces pip + venv + pipx + poetry. 10-100× faster. Rye was absorbed into uv in Feb 2026.
📦 git → Brew's git stays current (2.54+). Xcode CLT's git lags 6-12 months and breaks new GitHub features. After this brew install, git --version should jump from 2.39 to 2.54+.
🔍 Pin language versions globally — mise use --global writes to ~/.config/mise/config.toml. From now on, opening any new shell will have these versions on the PATH.
mise use --global node@lts python@3.13 rust@stable go@latest
🔍 What just happened — mise downloaded the LTS Node (currently 22.x), Python 3.13, the latest stable Rust toolchain, and the latest Go. It built shims in ~/.local/share/mise/shims/ that intercept node, python, rustc, go calls and route to the right version. You'll see them when you run mise activate zsh (we'll add that to ~/.zshrc in Step 21).
🔍 Verify :
mise current
# tools active:# node lts 22.11.0# python 3.13 3.13.0# rust stable 1.83.0# go latest 1.23.4
Step 7 — Git config
🔍 What git config --global does — writes to ~/.gitconfig. These are the settings every new repo inherits.
Stamps every commit with these. Required — git refuses to commit without
init.defaultBranch main
New repos start with main instead of master
pull.rebase false
git pull does a merge by default (not rebase). Some teams prefer rebase — your call
We'll write a full ~/.gitconfig (with SSH commit signing via 1Password and aliases) in Step 21.
🔍 SSH key for GitHub — generates a public/private key pair. The private key stays on your Mac (~/.ssh/id_ed25519). The public key (~/.ssh/id_ed25519.pub) goes to GitHub.
File location — press Enter (default ~/.ssh/id_ed25519)
Passphrase — press Enter twice for no passphrase (1Password agent will handle this later)
🔍 Copy public key to clipboard :
pbcopy <~/.ssh/id_ed25519.pub
Then : github.com/settings/keys → New SSH key → paste → Save. Also enable as a signing key (separate option below the SSH key list).
🔍 Test the SSH connection :
ssh -T git@github.com
# Hi Aboudjem! You've successfully authenticated, but GitHub does not provide shell access.
Step 8 — VS Code (the editor)
brew install --cask visual-studio-code
📦 VS Code v1.122+ → uses Microsoft's marketplace (no OpenVSX gaps), Settings Sync via GitHub/Microsoft account. AI lane via Continue.dev extension wired to local Ollama/MLX (config: ~/.continue/config.yaml), so coder models stay 100% local. Cursor was dropped: its cloud-routed model proxy + Chat-only custom model honoring makes it a poor local-LLM fit on this M5 stack.
🔍 code shell command is auto-installed by the cask (linked at /opt/homebrew/bin/code). code . opens the current folder. For a tunnel session: code-tunnel.
Step 9 — Claude Code (CLI agent) + paste fix
🔍 What this installs — claude-code (Anthropic's CLI agent) + vercel (deploys) + pnpm (faster npm). All globals. setup.sh §4 + Brewfile + verify.sh all treat the three as required.
📦 Claude Code (2.x) → Anthropic's CLI agent. cd into any project, type claude, ask anything. It reads files, edits them, runs commands, runs tests. Pairs with VS Code's Continue.dev (local Ollama/MLX for autocomplete + chat) and the opencode TUI. Stays on Anthropic cloud via the Claude Max subscription — never repointed to local Ollama (see §22.5).
🔍 First run — opens an OAuth URL in your browser. Sign in with your Anthropic account.
claude
Caution
Ghostty + Claude Code multi-line paste bug is OPEN (Anthropic #54700) as of May 2026. Pasting multi-line text mangles newlines. Workaround : override the TERM env var to xterm-256color (instead of the default xterm-ghostty).
Run this — appends the alias block to your ~/.zshrc in one command :
cat >>~/.zshrc <<'EOF'# === Claude Code Ghostty paste workaround (Anthropic #54700) ===if [ "$TERM_PROGRAM" = "ghostty" ]; then alias cc='TERM=xterm-256color claude'else alias cc='claude'fiEOFsource~/.zshrc # reload so `cc` works immediately
🔍 How cat >> file <<'EOF' ... EOF works — cat reads stdin and prints it. >> redirects to the file in append mode (doesn't overwrite existing content). <<'EOF' is a "heredoc" — everything until the next line that says EOF becomes the stdin. The single quotes around 'EOF' mean no variable substitution (so $TERM_PROGRAM stays literal).
🔍 How the alias works — $TERM_PROGRAM is set by Ghostty itself when it launches a shell. The if block makes cc an alias that prefixes the right TERM only when you're inside Ghostty. In iTerm2 or Terminal.app, cc just calls claude directly.
🔍 Image paste in Claude Code — screenshot a region with Cmd+Shift+Ctrl+4 (puts image bytes on clipboard, NOT a file path). Then in Claude paste with Ctrl+V not Cmd+V — the terminal app intercepts Cmd+V. Success rate ~60%. 100% reliable fallback : drag the image file from Finder into the Claude window.
🔍 Claude Code env vars — these tune the agent's behavior. All verified live in cli.js v2.1.152 source.
Run this — appends the env vars to ~/.zshrc :
cat >>~/.zshrc <<'EOF'# === Claude Code tuning (verified in cli.js v2.1.152) ===export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # kills telemetry + survey + non-API callsexport CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=15 # bump from default 10 — M5 can handleexport CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 # stops "Claude Code" tab title overwriteexport CLAUDE_CODE_EFFORT_LEVEL=xhigh # max test-time-compute on Opus 4.7export BASH_DEFAULT_TIMEOUT_MS=300000 # long-running bash tools don't auto-killEOFsource~/.zshrc # reload to apply
🔍 What each does :
DISABLE_NONESSENTIAL_TRAFFIC — Anthropic ships telemetry by default; this kills it for privacy + speed
MAX_TOOL_USE_CONCURRENCY=15 — when Claude calls multiple tools in parallel (e.g. read 20 files), it caps at 10 by default; M5 can comfortably do 15
DISABLE_TERMINAL_TITLE — by default Claude rewrites your tab title to "Claude Code"; this stops that
EFFORT_LEVEL=xhigh — for Opus 4.7, this is the maximum "think hard" knob, makes Claude spend more tokens on reasoning per response
BASH_DEFAULT_TIMEOUT_MS=300000 — bash tool calls auto-kill at 5 minutes (instead of the default 2 min)
📦 Brave (primary) → Chromium-based, same extensions and DevTools as Chrome, but with native ad blocker + tracker blocker + crypto wallet built in (no MetaMask extension needed). Slightly faster page loads, no Google profile tied to your browsing. Best fit for your stack — Polymarket, Web3 audits, daily browsing without ads.
📦 Chrome (backup) → universal DevTools reference + sites that detect/break Brave (Stripe dashboard sometimes, Google Meet edge cases). Open it when something rare doesn't work in Brave.
📦 Firefox → non-Chromium engine. Keep one around for cross-browser testing — if your site renders fine in Brave AND Firefox, it'll work everywhere except Safari (which you already have).
Tip
Optional : brew install --cask zen if you ever want to try the Arc-successor (Firefox fork with workspaces + vertical tabs). Cask is zen, NOT zen-browser. Most people end up keeping Brave as primary.
Step 10b — Brave extensions (curated for Adam's stack)
Note
Brave already blocks ads, trackers, fingerprinting, and includes Brave Wallet built-in — so the list below is only what ADDS value beyond Shields. Skip uBlock Origin (Brave Shields already handles it) and skip MetaMask (Rabby + Brave Wallet cover all chains).
Pin the ones you use daily (right-click extension icon → Pin)
Important
Audit each extension's permissions at install. Deny "read all site data" for anything that doesn't need it. Rabby + Tenderly legitimately need broad access (they overlay block explorers). ModHeader / JSON viewers should be scoped to specific origins. Avoid closed-source JSON or header extensions — pick the open-source ones from GitHub-linked publishers.
⌥+Space, type anything. Replaces Spotlight + Alfred + Maccy + Paste (4 tools in 1)
Ice (jordanbaird-ice) v0.11.12
Menubar declutter
Hides Slack/Dropbox/Zoom icons. Free, open-source. Bartender went paid sub + ownership controversy
Hazel
Folder rules engine
REMOVED 2026-05-30. Replaced by adhd-cleanup.sh + launchd (§21.9, §21.11). Hazel paste-format recipes preserved in §21.9 as a fallback if you want event-driven GUI rules.
Obsidian
Markdown notes
Files = .md on your disk, no proprietary format. Plugins for backlinks, graph view, Dataview SQL
Cold Turkey Blocker
Focus blocker
REMOVED 2026-05-30. v4.9 has a persistence bug on macOS 26.5 Tahoe — imports never survive a restart, even after full clean reinstall. Verified via SQLite forensics on /Library/Application Support/Cold Turkey/data-app.db. Replacement TBD.
Tip
After install : open Raycast → Settings → General → Hotkey: ⌥+Space. Then System Settings → Keyboard → Keyboard Shortcuts → Spotlight → uncheck ⌘+Space so they don't fight.
Step 12 — Solidity / Web3 stack (bounty-lab)
🔍 Foundry — NOT installed via brew — the brew formula exists but the canonical install uses the foundryup script for better update cadence.
anvil — local Ethereum testnet (like Ganache, faster)
chisel — Solidity REPL for experiments
🔍 Slither, Halmos, Wake via uv — these are Python tools. uv tool install puts them in ~/.local/bin as standalone binaries (each with its own isolated venv — no dependency conflicts with your project).
uv tool install slither-analyzer
uv tool install halmos
uv tool install eth-wake # PyPI package name is eth-wake, NOT wake
Tool
What it does
Slither
Static analysis, ~80 vuln detectors out of the box. slither contracts/MyContract.sol
Halmos
Symbolic execution / formal verification. Run on test functions with halmos --function testSomething
eth-wake
Test + audit framework by Ackee Blockchain. Property-based testing on Solidity
🔍 Aderyn — brew tap (Cyfrin maintains their own tap) :
brew install cyfrin/tap/aderyn
📦 Aderyn v0.6.8 → Rust-fast static analyzer. Alternative to Slither, sometimes catches things Slither misses. Author = the Cyfrin team (Patrick Collins).
🔍 Echidna fuzzer + Solana/Aptos/Sui :
brew install echidna
brew install solana
brew install aptos
brew install sui # now in homebrew-core (no tap needed)
📦 Echidna → Haskell-based smart contract fuzzer. Property-based testing — write invariants, Echidna tries to break them with thousands of random inputs.
📦 Solana / Aptos / Sui → CLIs for the non-EVM chains you might audit (Solana = Rust, Aptos+Sui = Move).
Note
sec3 X-Ray (Solana static analyzer) is Docker-only in 2026 — no native macOS binary. Run as : docker run --rm ghcr.io/sec3-product/x-ray:latest.
Step 13 — Cloud / deploy CLIs
🔍 Cloud providers :
brew install awscli # formula = awscli (NOT aws-cli — that's a 404)
brew install doctl # DigitalOcean CLI
📦 awscli → aws s3 ls, aws ec2 describe-instances, etc. After install, run aws configure to set up credentials.
📦 doctl → DigitalOcean. doctl auth init.
🔍 Web platforms (taps required) :
brew install supabase # now in homebrew-core (no tap needed)
brew install stripe/stripe-cli/stripe
📦 Supabase CLI → DB migrations, auth config, edge function deploy. supabase login, supabase init, supabase db push.
📦 Stripe CLI → stripe listen to forward webhooks to localhost during dev, stripe trigger payment_intent.succeeded to test event handlers.
📦 cloudflared → Cloudflare Tunnel. cloudflared tunnel --url localhost:3000 exposes your local dev server at a public HTTPS URL. Free, replaces ngrok (which went enterprise-restrictive).
📦 Tailscale → mesh VPN. Connect your Mac, VPS, and phone into a private network with magic DNS.
🔍 Node packages :
npm install -g vercel pnpm
📦 vercel → vercel deploy, vercel logs. Deploys Vespera, Prevue, etc.
📦 pnpm → drop-in for npm, ~2× faster install, content-addressable storage (multiple projects share node_modules on disk).
Step 14 — AI / MLX local stack
🔍 Core LLM tools :
brew install ollama whisper-cpp ffmpeg llama.cpp
📦 Ollama → Local LLM runtime. ollama run huihui_ai/qwen3-abliterated:30b starts an interactive chat.
Caution
Do NOT point the claude CLI at localhost:11434. The claude binary
(@anthropic-ai/claude-code) is paid via your Claude Max subscription and must
stay on Anthropic cloud. Setting ANTHROPIC_BASE_URL=http://localhost:11434
hijacks the subscription endpoint and silently swaps the cloud model for a local
Qwen — you lose Max value AND get degraded quality, with no warning.
For local-model coding, use a separate tool that natively speaks to Ollama:
opencode (primary), crush (Charmbracelet, fallback), Continue.dev (VS Code),
Cline (VS Code), or Aider. See §22.5 for the local-AI lane setup.
📦 whisper-cpp v1.8.4 → C++ port of OpenAI Whisper. Speech-to-text via Metal GPU.
Caution
Never pip install openai-whisper on Python 3.14. The Python torch MPS backend is broken (no wheels), so it falls back to CPU silently. A 52-min audio file = 60+ min CPU vs 3-5 min with whisper-cpp Metal.
📦 llama.cpp → GGUF model inference, Metal acceleration. Compete with Ollama; usually you only need one.
📦 ffmpeg v8.1.1 → audio/video converter. Required by whisper-cpp (needs 16kHz mono WAV) and by Python AI libs transitively.
🔍 Start Ollama as a background service — so it boots automatically and runs in the background :
brew services start ollama
🔍 What brew services does — uses macOS's launchd to run a daemon. The service file is at /opt/homebrew/Cellar/ollama/.../homebrew.mxcl.ollama.plist. To stop : brew services stop ollama. To list all : brew services list.
🔍 GUI apps :
brew install --cask lm-studio draw-things comfyui
App
What
LM Studio v0.4.14
Best daily-driver chat GUI for local LLMs. Browse models, download, chat
Draw Things
Image gen GUI (Stable Diffusion, FLUX, Z-Image)
ComfyUI v0.9.3
Node-based image/video workflows. Steep learning curve, max power
uv init — created pyproject.toml and .venv/ in current dir
uv add ... — installed 7 packages into the venv, pinned in pyproject.toml
Why per-project not global : these libs evolve fast. Pinning per-project = reproducible. Global install = breakage when one project needs mlx 0.31 and another needs 0.35.
Package
What it does
mlx v0.31.2
Apple's tensor library, like PyTorch but Metal-native
Adds DPO/ORPO/GRPO training on top of mlx-lm (preference learning)
mlx-vlm v0.5.0
Vision-language models (Qwen-VL, etc.)
mlx-audio v0.4.3
TTS + voice clone
mlx-embeddings v0.1.0
Embedding models
mflux v0.17.5
FLUX image generation on MLX (filipstrand)
Note
vllm-mlx v0.3.0 (PyPI, repo waybarrios/vllm-mlx) is the chosen MLX OpenAI-compatible serving backend for the local-AI lane — see §22.5. Launch with vllm-mlx serve <model> --port … (NOT python -m vllm_mlx.server). Keep mlx-lm for finetuning/convert; Ollama remains the GGUF convenience backend.
🔍 Download whisper model — beware HF LFS truncation :
--fail — exit non-zero on HTTP errors (prevents writing an error HTML page as if it were the model)
--retry 3 — retry up to 3 times on network failures
-o <path> — output to this file
⚠️The ls -lh at the end is critical — HuggingFace LFS sometimes truncates downloads silently (curl exits 0, you get a partial file that looks valid). Verify size = ~1.6 GB. If it's smaller, delete and re-download.
🔍 Pull base LLM models (overnight, ~131 GB total) — Adam runs the uncensored/abliterated equivalents of the qwen3 set:
🔍 What ollama pull does — downloads the model weights to ~/.ollama/models/, splits across blobs to dedupe shared layers. The 122B model alone is ~81 GB — go to sleep, it'll be ready in the morning.
Step 15 — Docker: OrbStack
brew install --cask orbstack
📦 OrbStack → fast Docker for Mac. Uses 10× less RAM than Docker Desktop, boots in 2 seconds, native ARM, k8s built-in. docker CLI works exactly the same — apps don't know which runtime is behind.
Caution
Don't install Docker Desktop. Strictly worse than OrbStack in 2026. And in a corp setting, Docker Desktop now requires a Pro license for companies >250 employees — OrbStack is free.
Windows-style ⌥+Tab with window previews. Cmd+Tab only switches apps — AltTab switches individual windows
Stats v2.12
Menubar system monitor (CPU/RAM/GPU/network/fan). Free, open source
AppCleaner
Drag any app onto it → removes app + all preference/cache files. Better than dragging to Trash
The Unarchiver
Opens .rar, .7z, .tar.xz, things macOS native can't
IINA
Modern video player, all codecs. Replaces QuickTime + VLC
VoiceInk
MLX-based dictation. Press a hotkey, talk, get text. Runs Whisper-Large-v3-Turbo on M5 GPU
Shottr
REMOVED 2026-05-30 — CleanShot expanded shortcuts cover all use cases (window/area/fullscreen/OCR/recording).
Note
Highlights (PDF annot → Obsidian export) is NOT on brew. Download from highlightsapp.net or App Store.
Music — Adam uses Apple Music (native macOS app, no install). If you ever switch to Spotify: brew install --cask spotify.
Step 19 — M5-specific tuning
🔍 GPU memory cap — by default macOS reserves only ~75% of unified RAM for the GPU. For MLX workloads on a 128 GB machine, you want 120 GB. This sysctl raises the cap.
sudo sysctl iogpu.wired_limit_mb=122880
🔍 What this does — iogpu.wired_limit_mb is a kernel parameter that caps how much RAM the IOGPU subsystem can wire (lock) for GPU use. 122880 MB = 120 GB. Leaving 8 GB for macOS itself.
Caution
Don't push to 124000+ — kernel panic at the next big batch. 122880 is the sweet spot validated empirically by the MLX community.
🔍 Make it permanent (survives reboot) :
# Modern macOS (Tahoe 26.x) does NOT auto-load /etc/sysctl.conf at boot.# The canonical persistence path is a LaunchDaemon — written by setup.sh §13 to# /Library/LaunchDaemons/com.adam.iogpu-wired-limit.plist# (verify presence with: ls -la /Library/LaunchDaemons/com.adam.iogpu-wired-limit.plist)
🔍 What tee -a does — tee writes stdin to both stdout AND the file. -a means append. sudo is needed because /etc/sysctl.conf is owned by root.
🔍 MLX zsh functions — wrap MLX_METAL_FAST_SYNCH=1 + caffeinate so you can enable "power mode" before training. Run this :
Now mlx_max toggles power mode for long training runs, mlx_revert restores normal.
Step 20 — Cask renames you'd otherwise miss
Warning
Verified live on formulae.brew.sh May 2026. These are the gotchas where the obvious name 404s :
❌ Wrong
✅ Correct
Why
brew install --cask zen-browser
brew install --cask zen (optional)
Cask is just zen — but Brave is now the primary pick
brew install --cask tailscale
brew install --cask tailscale-app
Renamed
brew install --cask msty
brew install --cask mstystudio
Renamed
brew install aws-cli
brew install awscli
Formula has no hyphen
brew install rustup-init
brew install rustup
Renamed
brew install gpg
brew install gnupg
Formula = gnupg, binary = gpg
pip install wake
uv tool install eth-wake
PyPI name disambiguation
brew install --cask wezterm
Stuck Feb 2024 — drop
Abandoned
brew install dog
Use doggo or stock dig
Dead since 2020
brew install --cask pinokio
Manual DMG from desktop.pinokio.co
No cask
brew install --cask highlights
Direct download highlightsapp.net
No cask
Hazel IS on brew now (cask hazel v6.1.2) — you don't need to download from noodlesoft.com anymore (still needs $42 license at first launch).
Step 21 — Config files (paste-ready, with section commentary)
21.1 — ~/.zshrc (full template)
Save the file with code ~/.zshrc (after Step 8) or nano ~/.zshrc. This is the file zsh runs at every shell startup.
# ============================================================# ~/.zshrc — M5 Max, May 2026# ============================================================# ----- PATH: where the shell looks for binaries -----# Homebrew puts binaries in /opt/homebrew/bin (Apple Silicon)export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"# rustup's binaries live in a separate prefixexport PATH="$(brew --prefix rustup)/bin:$PATH"# uv puts its installed tools (slither, halmos, etc.) hereexport PATH="$HOME/.local/bin:$PATH"# ----- Antidote: zsh plugin manager -----# Loads the plugins listed below, lazily, on shell startup.# Each line in the heredoc is "owner/repo".source$(brew --prefix)/opt/antidote/share/antidote/antidote.zsh
antidote load << 'EOF'zsh-users/zsh-autosuggestionszsh-users/zsh-syntax-highlightingzsh-users/zsh-completionszdharma-continuum/fast-syntax-highlightingEOF# ----- Prompt + interactive tools -----# starship renders the prompt (the part before the cursor)eval"$(starship init zsh)"# zoxide lets `z foo` jump to your most-used "foo" directoryeval"$(zoxide init zsh)"# atuin replaces Ctrl+R with a fuzzy DB-backed history searcheval"$(atuin init zsh)"# mise auto-switches language versions per projecteval"$(mise activate zsh)"# direnv auto-loads/unloads .envrc files when you cdeval"$(direnv hook zsh)"# fzf keybindings (Ctrl+R history, Ctrl+T file, Alt+C dir)
[ -f~/.fzf.zsh ] &&source~/.fzf.zsh
# ----- Claude Code (Ghostty paste workaround for issue #54700) -----# Ghostty advertises kitty-protocol terminfo. Claude Code's paste# tokenizer mishandles it on multi-line paste. Override TERM only# when running Claude inside Ghostty.if [ "$TERM_PROGRAM"="ghostty" ];thenalias cc='TERM=xterm-256color claude'elsealias cc='claude'fi# Tuning (all env vars verified in cli.js v2.1.152 source)export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=15
export CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1
export CLAUDE_CODE_EFFORT_LEVEL=xhigh
export BASH_DEFAULT_TIMEOUT_MS=300000
# NOTE: do NOT set ANTHROPIC_BASE_URL — the `claude` CLI must stay on Anthropic cloud# (Claude Max subscription). Local-model coding uses a SEPARATE tool (opencode primary,# Crush fallback) wired to Ollama / the MLX backend via that tool's own config. See §22.5 for setup.# ----- HF token via 1Password (never plaintext) -----# Requires 1Password app + CLI signed in# export HF_TOKEN="$(op item get 'HuggingFace' --field credential 2>/dev/null)"# ----- Aliases: replace the old Unix commands with modern equivalents -----alias ls='eza --icons --git'alias ll='eza -la --icons --git'alias lt='eza --tree --level=2 --icons'alias cat='bat --paging=never'alias grep='rg'alias find='fd'alias top='btop'alias du='dust'alias df='duf'# ----- Git shortcuts -----alias g='git'alias lg='lazygit'alias gst='git status'alias gco='git checkout'alias gp='git push'alias gpl='git pull'# ----- Project shortcuts -----alias dev='cd ~/1_PROJECTS'alias bl='cd ~/projects/bounty-lab'alias kb='cd ~/2_KNOWLEDGE'# ----- MLX max-power functions (M5 Max) -----# Toggle "give MLX everything" mode before training.mlx_max() {
export MLX_METAL_FAST_SYNCH=1
defaults write NSGlobalDomain NSAppSleepDisabled -bool YES
caffeinate -di &echo"MLX max: fast sync ON, sleep OFF, caffeinate"
}
mlx_revert() {
unset MLX_METAL_FAST_SYNCH
defaults delete NSGlobalDomain NSAppSleepDisabled
pkill caffeinate 2>/dev/null
echo"MLX max reverted"
}
# ----- fzf theme: Neon Noir -----# Colors match your design system (#00D4FF cyan, #FF006E magenta, #7C3AED violet)export FZF_DEFAULT_OPTS=" --color=bg+:#15151F,bg:#0A0A14,spinner:#FF006E,hl:#00D4FF --color=fg:#F5F5F7,header:#00D4FF,info:#7C3AED,pointer:#FF006E --color=marker:#34D399,fg+:#F5F5F7,prompt:#00D4FF,hl+:#FF006E"# ----- End of ~/.zshrc -----
After saving : source ~/.zshrc to apply. New prompt + plugins should activate.
21.2 — ~/.config/starship.toml
🔍 What this file does — controls how your prompt looks. The format string defines the segments order. Each [segment] block configures one piece.
"$schema" = "https://starship.rs/config-schema.json"# Order of segments in the promptformat = """[](#7C3AED)\$os\$username\[](bg:#0A0A14 fg:#7C3AED)\$directory\[](fg:#0A0A14 bg:#0A0A14)\$git_branch\$git_status\[](fg:#0A0A14 bg:#0A0A14)\$nodejs\$python\$rust\$golang\[](fg:#0A0A14 bg:#15151F)\$time\[ ](fg:#15151F)\$line_break$character"""palette = "neon_noir"
[palettes.neon_noir]
cyan = "#00D4FF"magenta = "#FF006E"violet = "#7C3AED"green = "#34D399"dark = "#0A0A14"surface = "#15151F"
[character]
success_symbol = "[❯](bold cyan)"error_symbol = "[❯](bold magenta)"
[directory]
style = "bold cyan"truncation_length = 3# show last 3 path segmentstruncate_to_repo = true# truncate to git root if inside a repo
[git_branch]
style = "bold magenta"symbol = ""
[git_status]
style = "bold magenta"
[time]
disabled = falseformat = '[ $time ]($style)'style = "fg:#71717A"time_format = "%H:%M"
[cmd_duration]
min_time = 500# only show duration if cmd took >500msformat = "[took $duration]($style) "style = "fg:#FBBF24"
21.3 — ~/.config/ghostty/config
🔍 Ghostty's config file format — key = value pairs, no quotes around strings, comments with #. setup.sh writes this file verbatim. Theme is cyberdream (closest to Neon Noir brand) with a custom Neon Noir palette fallback at ~/.config/ghostty/themes/neon-noir (Section 21.4b).
Note
Font dependency — this config requires JetBrainsMono Nerd Font (brew install --cask font-jetbrains-mono-nerd-font, included in Brewfile + setup.sh §7). Ghostty falls back gracefully if missing, but the icon glyphs won't render in eza/starship.
🔍 Why this exists — if cyberdream is ever removed from Ghostty's bundled themes (or if you want to switch to a pure brand palette), set theme = neon-noir instead. setup.sh writes this file alongside the main config.
🔍 What this pins — your global default language versions. Any project without its own .mise.toml uses these.
[tools]
node = "lts"python = "3.13"rust = "stable"go = "latest"
[settings]
experimental = true# Only auto-activate .mise.toml in these trusted directoriestrusted_config_paths = ["~/projects", "~/1_PROJECTS"]
21.6 — ~/.gitconfig
🔍 What's special here — SSH commit signing via 1Password. Instead of fiddling with GPG, you sign commits with your SSH key, signed by the 1Password ssh-agent. GitHub verifies it.
[user]name = Adam Boudjemaa
email = boudjemaa.adam@gmail.com
signingkey = ~/.ssh/id_ed25519.pub
[gpg]format = ssh
[gpg "ssh"]program = /Applications/1Password.app/Contents/MacOS/op-ssh-sign
[commit]gpgsign = true # auto-sign every commit[init]defaultBranch = main
[pull]rebase = false # merge by default; switch to true if you prefer linear history[push]autoSetupRemote = true # `git push` works for new branches without --set-upstreamdefault = current
[fetch]prune = true # remove local refs to deleted remote branches[merge]conflictstyle = zdiff3 # show common ancestor in conflict markers (clearer)[diff]algorithm = histogram # smarter than default myerscolorMoved = default # highlight moved blocks[color]ui = auto
# Useful aliases — type `git lg` for a pretty graph, `git st` for status, etc.[alias]st = status
co = checkout
br = branch
cm = commit -m
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
last = log -1 HEAD
unstage = reset HEAD --
amend = commit --amend --no-edit
sw = switch
ll = log --oneline -20
21.7 — ~/.config/atuin/config.toml
🔍 What Atuin replaces — your shell history. By default zsh keeps the last N commands in ~/.zsh_history (plain text). Atuin stores them in SQLite, encrypts, optionally syncs across machines.
# Atuin shell history — encrypted DB + sync# Docs: https://docs.atuin.sh/# When you press Enter on a history item, preview it first (don't auto-execute)# Press Enter again to run. Safer.enter_accept = false# Search across ALL your hosts (not just this machine)filter_mode = "global"filter_mode_shell_up_key_arrow = "session"style = "compact"inline_height = 20show_preview = trueshow_help = true# Sync every 10 min if you use atuin.sh server (or self-host)auto_sync = truesync_frequency = "10m"
[stats]
common_prefix = ["sudo", "time"]
21.8 — ~/.claude/settings.json
🔍 What this controls — Claude Code's default model, env vars, permission policy, file-checkpointing for /rewind, parallel-tool throughput. Live template at template-claude-settings.json in this gist.
model: "opus" — family alias, auto-tracks the latest Opus release. When Anthropic ships Opus 5.0, your sessions pick it up automatically. Same trick works for "sonnet" / "haiku".
effortLevel: "xhigh" — deep reasoning by default. The SAFE half of "ultracode" without the auto-orchestration runaway risk.
env.CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY: 32 — let Claude fire up to 32 parallel tool calls in one turn. M5 Max (18 cores, 128 GB) handles this easily.
permissions.allow — fast-path tools that never need confirmation
permissions.deny — hard blocks even with bypass mode
permissions.ask — speed bumps for destructive ops (only effective when bypass mode is OFF)
fileCheckpointingEnabled — snapshot files before edits so /rewind can restore. Bounded by the §21.12 disk-guard.
skipDangerousModePermissionPrompt — accept the bypass-mode dialog up-front (paired with the claude zsh wrapper in §21.1)
disableDeepLinkRegistration — don't register the claude-cli:// protocol with macOS
🔍 What's deliberately NOT here: "ultracode": true, "autoCompactWindow", "showThinkingSummaries", "fastMode". These are either session-scoped (silently dropped from settings.json) or buggy / cost-multipliers. Ultracode is forced via the claude zsh wrapper instead (see template-zshrc-claude-wrappers.zsh).
21.9 — ADHD cleanup (Hazel-equivalent via launchd)
🔍 Hazel was uninstalled on 2026-05-30 in favor of a fully scripted equivalent. The launchd version mirrors what Hazel would do for the 13 rules below, runs every 10 minutes, and adds a content-classification tier (OCR + Ollama) on top — see §21.11. Hazel itself is excellent if you want event-driven rules + macOS notifications; rebuild via these recipes if you ever go back.
Recipes (paste into Hazel UI if you choose to use it):
DMGs auto-trash — Folder ~/Downloads. If Kind is Application bundle (dmg) AND Date Added before 1 day ago → Move to Trash.
Invoice classify — Folder ~/Downloads. If Name containsinvoice OR facture AND Extension is pdf → Move to ~/Documents/3_admin/factures/.
Screenshot archive — Folder ~/Pictures/Screenshots. If Date Added before 30 days ago → Move to ~/Pictures/Screenshots/archive/.
Audit PDFs to KB — Folder ~/Downloads. If Name matchesaudit* AND Extension is pdf → Move to ~/2_KNOWLEDGE/audits/incoming/.
Empty Trash weekly — Folder ~/.Trash. If Date Trashed before 7 days ago → Delete forever.
The scripted version implements 13 rules across 5 folders (Downloads / Desktop / Screenshots / 0_inbox / .Trash) — see adhd-cleanup.sh + com.adam.adhd-cleanup.plist.
21.10 — launchd plist (weekly tidy)
🔍 What launchd is — macOS's native cron + service manager. To run something on schedule, you drop a .plist (XML config) in ~/Library/LaunchAgents/.
Save as ~/Library/LaunchAgents/com.adam.tidy.plist :
🔍 What this runs every Sunday at 3 AM — brew cleanup (purges old formula versions), rm -rf ~/Library/Caches/* (system caches), ~/.npm/_cacache (npm cache), docker system prune (dangling containers/images). Logs go to /tmp/tidy.log.
21.11 — Personal launchd-managed scripts (live templates in this gist)
🔍 What's here — three scripts wired to user LaunchAgents. All files live at the root of this gist. On a fresh restore, after setup.sh runs, copy them to their canonical locations and launchctl bootstrap each plist.
Gist file
Destination
Schedule
Purpose
adhd-cleanup.sh
~/.local-bin/adhd-cleanup.sh (chmod 755)
every 10 min via com.adam.adhd-cleanup.plist
Sweeps Downloads/Desktop/Screenshots/0_inbox/.Trash per 13 name+age rules, then runs Tier C content classification on unmatched PDFs/images/text
classify-file.py
~/.local-bin/classify-file.py (chmod 755)
invoked by adhd-cleanup.sh
OCR (pdftotext for text PDFs, ocrmac for images / scanned PDFs) → Ollama qwen3-coder-abliterated:30b with format=json → returns {category, name, confidence} → caller auto-routes
launchctl list | grep com.adam # expect adhd-cleanup + claude-file-history-guard (+ any others you maintain)
DRY_RUN=1 zsh ~/.local-bin/adhd-cleanup.sh && tail -5 ~/.local-ai/logs/adhd-cleanup.log
~/.local-bin/classify-file.py ~/Downloads/some.pdf 2>&1| head -3 # smoke test
zsh ~/.local-bin/claude-file-history-guard.sh && tail -5 ~/.local-ai/logs/claude-file-history-guard.log
🔍 Templated settings (also in this gist):
template-claude-settings.json → ~/.claude/settings.json (see §21.8 for the rendered keys)
template-vscode-settings.json → ~/Library/Application Support/Code/User/settings.json (Solidity Foundry config + Todo Tree ripgrep path fix)
template-zshrc-claude-wrappers.zsh — paste this block into ~/.zshrc to force ultracode + --dangerously-skip-permissions on every claude / cc launch (escape hatches: \claude or command claude)
Step 22 — Verify everything
# Foundation
brew doctor # "Your system is ready to brew." = green
git --version # should be 2.54+ (brew's, not 2.39 Xcode)
mise current # node/python/rust/go versions# AI stack
ollama list # list installed models
whisper-cli --version
python -c "import mlx.core as mx; print('MLX Metal:', mx.metal.is_available())"# → MLX Metal: True# Solidity
forge --version
slither --version
halmos --version
aderyn --version
# Modern CLI
eza --version
rg --version
fzf --version
echo"✅ all green if you see versions above"
🔍 Day-1 smoke test — Ollama is up — sanity check the local LLM runtime via its NATIVE API (the Anthropic-compat shim is reserved for tools that opt-in; claude MUST NOT be one of them):
curl http://localhost:11434/api/tags # → JSON list of installed models
curl http://localhost:11434/api/generate \
-d '{"model":"huihui_ai/qwen3-coder-abliterated:30b-a3b-instruct-q8_0","prompt":"hello","stream":false}'
🔍 What this proves — Ollama is running, has Qwen3-Coder pulled, and returns a valid response. If the second call returns JSON with a "response" field, everything works.
Caution
Do NOT set ANTHROPIC_BASE_URL to point claude at Ollama. The official claude CLI is paid via your Claude Max subscription and must stay on Anthropic cloud. Setting that env var hijacks the subscription endpoint and silently degrades quality. Verify the var is empty: echo "$ANTHROPIC_BASE_URL" should print nothing.
🔍 Drive Ollama from opencode (primary local-AI CLI) — see §22.5 for full setup:
# opencode reads ./opencode.json or ~/.config/opencode/opencode.json# Provider block points at Ollama's OpenAI-compat endpoint (NOT the /v1/messages shim)# Then run opencode interactively or:
opencode -p "list 3 Solidity reentrancy patterns" --model ollama/huihui_ai/qwen3-coder-abliterated:30b-a3b-instruct-q8_0
🔍 What this does — downloads Qwen3-Coder-7B from HuggingFace (~14 GB bf16), converts to MLX Q4 format (compressed to ~4.5 GB), saves to ~/models/. If this completes, your full finetune pipeline will work.
Step 22.5 — Local-AI lane (post-base setup)
Caution
The claude CLI stays on Anthropic cloud — always. Your Claude Max subscription pays for that endpoint. NEVER set ANTHROPIC_BASE_URL in your shell or any config. Local-model coding uses a separate tool, wired to Ollama via its own provider config.
The local-AI lane is its own world: Ollama runs the language models, and a companion tool gives you a Claude-Code-like UX against those local models. Adam's pick set after research (see scout reports at /tmp/m5-local-tool-picks.md + /tmp/m5-multimodal-local.md):
Lane
Tool
What it does
Wire-up
LLM perf backend (NEW)
vllm-mlx OpenAI server :1234
Headless MLX server → M5 Neural Accelerators give up to 4× prefill vs M4 (needs MLX ≥0.30; have 0.31.2). Continuous batching + prefix cache. Serves an abliterated Qwen3-Coder MoE in MLX 4-bit.
Metal FLUX/SD3.5; DT gRPC for automation, Comfy node graphs
enable Server-Offload in DT; :8002/prompt for Comfy REST
Important
Port map (no collisions — set --port explicitly on everything):Ollama 11434 · MLX coder (vllm-mlx) 1234 · vllm-mlx vision 8000 · mlx-audio TTS 8001 · Open WebUI 8080 · ComfyUI 8002 (desktop cask; CLI uses 8188) · DT shim 7861 · Draw Things 7860 · SearXNG 8888
Note
As-deployed (2026-05-29)::1234 now serves the heavy Eldadalbajob/Huihui-Qwen3-Coder-Next-abliterated-mlx-4Bit (44.9 GB, served as qwen3-coder-mlx, mmap-loaded). The small mlx-community/Josiefied-Qwen3-1.7B-abliterated-v1-4bit (~1 GB; ~354 decode / ~7,600 prefill tok/s) is the fast fallback. coder-up.sh downloads-then-serves whichever REPO= is set; coder-swap.sh auto-swaps :1234 to the heavy model once its download completes. Persistent chains in ~/.local-ai/ (coder-up.sh, coder-swap.sh, bench-1234.sh, vision-up.sh); logs in ~/.local-ai/logs/. NOTE: launch mlx_audio.server with --log-dir (it crashes creating ./logs if CWD is read-only, e.g. under a LaunchAgent).
This machine has only Python 3.13. mlx-openai-server needs <3.13 → use vllm-mlx (works on 3.13) as the MLX backend. uv venvs have no pip — install with uv pip install --python <venv>/bin/python … and use the venv's hf/vllm-mlx binaries directly.
# claude stays on cloud — guardrail checkecho"$ANTHROPIC_BASE_URL"# MUST print nothing
claude --version # 2.x, calls Anthropic on first use# Local coding CLIs hit a LOCAL model (selector syntax: provider/model)
opencode run "list 3 Solidity reentrancy patterns" -m mlx/qwen3-coder-mlx # MLX backend (tools work)
opencode run "list 3 Solidity reentrancy patterns" -m ollama/huihui_ai/qwen3-abliterated:30b
# Open WebUI up (wired to both :11434 and :1234)
curl -s http://localhost:8080/health || open http://localhost:8080
# MLX coder backend
curl -s http://localhost:1234/v1/models
curl -s http://localhost:1234/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"qwen3-coder-mlx","messages":[{"role":"user","content":"reverse a string in python"}],"max_tokens":80}'# Vision (Qwen3-VL via vllm-mlx :8000) — base64-encode a local image into the data URL
curl -s http://localhost:8000/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"mlx-community/Qwen3-VL-30B-A3B-Instruct-8bit","messages":[{"role":"user","content":[{"type":"text","text":"What is this? One word."},{"type":"image_url","image_url":{"url":"data:image/jpeg;base64,'"$(base64 -i /tmp/test.jpg)"'"}}]}],"max_tokens":20}'# TTS via mlx-audio (one-shot flag is --output_path, not --output)
mlx_audio.tts.generate --model mlx-community/Kokoro-82M-bf16 --text "hello from the M5" --voice af_heart --lang_code a --output_path /tmp/tts_out
afplay /tmp/tts_out.wav
# STT round-trip via parakeet-mlx
parakeet-mlx /tmp/tts_out.wav --output-format txt
# OCR~/.venvs/ocrmac/bin/python -c "from ocrmac import ocrmac; print(ocrmac.OCR('/tmp/shot.png').recognize()[:3])"# MAX-GPU proof: run a big MLX generation and watch the GPU
sudo powermetrics --samplers gpu_power -n 3 -i 1000 # GPU active residency + power
pmset -g | grep powermode # expect powermode 2 on AC
Important
The two lanes are independent. When Adam is online, both work in parallel — use claude for hard problems where you want Anthropic's best, opencode/Open WebUI for privacy, offline work, or burst-rate-limited tasks. Models stay warm in Ollama because both local tools share the same Ollama backend.
Note
Why not just use Open WebUI for everything? It's a web UI, not an agentic terminal coding tool. It can't drive multi-turn file edits the way opencode/claude do. Pair them: opencode for code-and-edit loops, Open WebUI for the everything-else surface (image, voice, search, RAG).
M5 max-utilization tuning
To actually exploit the M5 Max (40-core GPU, per-core Neural Accelerators, ~600 GB/s):
# High Power Mode (AC only) — sustains GPU clocks under long MLX loads
sudo pmset -c powermode 2 # set for AC profile; engages when plugged in
pmset -g | grep powermode # live value shows 2 on AC# MLX must be ≥0.30 for the M5 Neural-Accelerator path (up to 4× prefill vs M4; needs macOS ≥26.2)
python -c "import mlx.core as mx; print(mx.__version__)"# have 0.31.2 ✓# Ollama: keep models warm + raise context (tiny default silently truncates → broken tool calls)# set in ~/Library/LaunchAgents/homebrew.mxcl.ollama.plist EnvironmentVariables:# OLLAMA_KEEP_ALIVE=-1 · OLLAMA_CONTEXT_LENGTH=16384 (alongside OLLAMA_FLASH_ATTENTION=1, OLLAMA_KV_CACHE_TYPE=q8_0)
launchctl bootout gui/$(id -u)/homebrew.mxcl.ollama; launchctl bootstrap gui/$(id -u)~/Library/LaunchAgents/homebrew.mxcl.ollama.plist
MLX_METAL_FAST_SYNCH is bug-prone (kernel-load failures / deadlocks) — do NOT blanket-enable. It's gated behind the opt-in mlx_max shell function (with caffeinate + sleep-off); call it only when deliberately benchmarking.
GPU cap stays iogpu.wired_limit_mb=122880 (120 GB). May lower to 118784 (116 GB) if running everything at once and memory pressure goes red (avoids the IOGPU kernel panic). Never 124000+.
Decode is bandwidth-bound (MoE models win), prefill is compute-bound (where the M5 4× shows up) — prefer MLX MoE 4-bit for coding.
Verified gotchas (Phase 17)
HF Xet stalls on unauthenticated pulls. Big MLX repos are Xet-backed; without an HF_TOKEN the transfer wedges (repeated xet-read-token, ~0 bytes). Fix: export HF_HUB_DISABLE_XET=1 (classic HTTPS/LFS) or set an HF_TOKEN. hf_xet must be installed in the venv either way.
vllm-mlx wraps downloads in a 300s-per-attempt timeout → multi-GB shards never finish over a slow link. Pre-download with hf download <repo> (no timeout, resumable), thenvllm-mlx serve (loads from cache).
Python 3.13 only on this box → mlx-openai-server (needs <3.13) is out; use vllm-mlx. uv venvs have no pip (use uv pip install --python …).
mlx-audio & vllm-mlx both default to :8000 → always pass --port (TTS=8001, vision=8000).
mlx-audio one-shot flag is --output_path, not --output; Kokoro repo id mlx-community/Kokoro-82M-bf16.
opencode org renamed sst→anomalyco; brew tap is anomalyco/tap/opencode (curl installer + ~/.config/opencode/opencode.json path unchanged).
openclaude → Crush: openclaude has High-sev CVE GHSA-m6rx-7pvw-2f73 and is derived from leaked Claude Code source.
Ollama abliterated coder GGUF lacks a tool template (advertises only completion) → opencode/Crush agent mode can't drive it; use the MLX coder (tools work) or qwen3-abliterated:30b.
Nexus — the control layer (2026-05-29)
"Nexus" wraps the whole local-AI stack in a one-click macOS experience. All control scripts live in ~/.local-ai/; logs in ~/.local-ai/logs/.
App (single, in /Applications):
Nexus.app — menu-bar app (Swift/NSStatusItem, LSUIElement, bundle id local.adam.nexus, v2.0). Menu: Open Web UI / Open SearXNG / Start All & Open Web UI / Start All / Stop All / Restart All / Free RAM (unload models) / View Logs / Quit. Source ~/.local-ai/nexusbar/App.swift (swiftc -O -parse-as-library -target arm64-apple-macos26.0 -framework AppKit, ad-hoc codesigned). Consolidated 2026-05-30 (former Nexus.app start launcher + Nexus Stop.app + NexusBar.app merged into this).
Auto-start at login: LaunchAgents ~/Library/LaunchAgents/{local.adam.nexus,com.adam.nexus}.plist. local.adam.nexus opens the menu-bar app; com.adam.nexus runs start-all.sh to boot the services. Load: launchctl bootstrap gui/$(id -u) <plist> (disable: launchctl bootout gui/$(id -u)/<label>). NOTE: in zsh ! cmd1 && cmd2 negates cmd1 → the && short-circuits; run each bootstrap on its own line.
Open WebUI features (all local, env in webui-up.sh; some configs persist to webui.db so env can be overridden — verify via API):
Web search ON by default: SearXNG (docker, :8888) with BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL=true. Set the per-user setting webSearch: "always" (Settings → Interface → "Always on Web Search", or DB user.settings) so every chat auto-searches — the frontend reads $settings.webSearch === 'always'.
Audio: TTS = mlx-audio Kokoro (af_heart); STT = Open WebUI's local faster-whisper (mlx-audio's parakeet route is broken). mlx-audio must run with HF_HUB_OFFLINE=1.
Image gen: Open WebUI → ~/.local-ai/dt-a1111-shim.py (stdlib proxy :7861) → Draw Things :7860. DT's API omits sd_model_checkpoint (→ OWUI KeyError 400) and 404s sd-models; the shim injects it + ensures txt2img responses carry info. AUTOMATIC1111_BASE_URL=http://localhost:7861. One-time: Draw Things → Advanced → API Server ON, HTTP, port 7860; keep DT open.
Local Claude-Code-like TUI = opencode (~/.config/opencode/opencode.json): wired to all local coders + a vision MCP server (~/.local-ai/vision-mcp.py, run from ~/.venvs/mcp-vision) that calls Qwen3-VL :8000 — so opencode reads images offline (its native image-attach to custom providers is broken, anomalyco/opencode#20802). Skills in ~/.config/opencode/skills/<name>/SKILL.md, subagents in ~/.config/opencode/agent/<name>.md. crush = secondary TUI (best native image attach). In-editor offline AI = Continue.dev (VS Code, ~/.continue/config.yaml): autocomplete via Ollama abliterated coder, chat/edit via the MLX coder on :1234, embeddings via nomic-embed-text. Cursor was evaluated and rejected for this stack (cloud-routed model proxy + Chat-only custom model honoring).
M5 memory: Ollama OLLAMA_KEEP_ALIVE=30m + OLLAMA_MAX_LOADED_MODELS=1 (was -1, which pinned the 122B+30B = ~106 GB forever and forced swap). Don't run the 122B and the 38 GB Qwen3-VL together.
setup.sh installs every formula and cask, but it CANNOT grant TCC permissions, sign you in, enter licenses, run first-launch wizards, or install editor/launcher extensions. Work through this once on a fresh Mac, aftersetup.sh finishes. Items marked (scriptable) can be automated and may move into setup.sh later.
Already covered elsewhere — do NOT repeat here: Apple ID / iCloud-off / FileVault (top of doc), VS Code Settings Sync (Step 8), Claude Code OAuth (Step 9), 1Password OAuth + Brave extensions (Step 10b), Hazel license (Step 11), Continue.dev wiring (Step 22.5). The Nexus architecture (ports, scripts, Open WebUI features) is in the Nexus section above; subsection (E) is only the one-time human actions it assumes.
(A) macOS permissions to grant
TCC (privacy) grants cannot be scripted. Open System Settings → Privacy & Security, pick the category, and add the app with + from /Applications if it isn't listed. Apps that need a permission do nothing until you grant it.
App
Permission
Where (System Settings → Privacy & Security → …)
AltTab
Accessibility (required — focus/switch)
Accessibility
Raycast
Accessibility
Accessibility
VoiceInk
Accessibility (paste back transcript)
Accessibility
CleanShot X
Accessibility
Accessibility
Zoom
Accessibility (remote control only)
Accessibility
CleanShot X
Screen Recording (required)
Screen & System Audio Recording
Zoom
Screen Recording (share screen)
Screen & System Audio Recording
AltTab
Screen Recording (window previews)
Screen & System Audio Recording
Raycast
Input Monitoring (global hotkey)
Input Monitoring
VoiceInk
Input Monitoring (push-to-talk hotkey)
Input Monitoring
VoiceInk
Microphone (required — dictation)
Microphone
Zoom
Microphone + Camera
Microphone / Camera
WhatsApp / Telegram
Microphone + Camera (calls)
Microphone / Camera
Hazel
Full Disk Access (required) — then Quit & Reopen Hazel
Full Disk Access
Hazel → Finder
Automation (move/tag files) — approve the prompt
Automation
Ghostty
Files & Folders — approve per-folder prompts on first access
Tailscale — app reports "Logged out": menu-bar icon → Log in → join your tailnet (also flip Run at login in (D)).
Telegram — phone number + 2FA. WhatsApp — Settings → Linked Devices → scan QR. Zoom — Google SSO (then grant perms in (A)). Postman — sign in for sync, or use offline/Scratchpad mode and import collections.
Licenses + first-run config
CleanShot X — enter license. Disable ALL 5 native screenshot shortcuts (System Settings → Keyboard → Shortcuts → Screenshots → uncheck the 5 entries). Then in CleanShot Settings → Shortcuts, bind: ⌘⇧3 → Capture Fullscreen, ⌘⇧4 → Capture Area, ⌘⇧5 → Record Video, ⌘⇧T → Capture Text (OCR), ⌘⇧G → Record GIF, ⌘⇧8 → Capture Previous Area. Save to ~/Pictures/Screenshots; enable OCR + scrolling capture; turn ON displayRecordingTime, doNotDisturbWhileRecording. CleanShot fully replaces native screenshots + recording.
VoiceInk — AI Models → Local → Import Local Model = the already-present ~/.cache/whisper-cpp/ggml-large-v3-turbo.bin (do NOT re-download) → Set as Default; set the recording hotkey; optional Enhancement → provider Ollama (auto-connects localhost:11434, already running); enter license.
Ice — set the menu-bar divider; choose Hidden vs Always-Hidden icons. (Tahoe note: Ice 0.11.12 is partly broken on macOS 26; if hiding misbehaves use a 0.11.13-dev beta.)
Stats — pick modules (CPU / GPU / RAM / Network / Sensors). The Ghostty config in Step 21 references a stats block — verify it matches.
Cold Turkey Blocker — SKIP. Removed 2026-05-30 due to persistence bug on macOS 26.5 Tahoe (imports never survive restart, even on fresh install — confirmed via SQLite forensics). Replacement TBD.
LM Studio — no models: download a GGUF in-app, or just rely on Ollama (Step 14).
ComfyUI — first launch builds the support-dir tree; drop a checkpoint (SDXL / FLUX .safetensors) into models/checkpoints; for the local-AI lane enable listen on :8002.
(C) Extensions & plugins
VS Code — sign in (top-right) for Settings Sync (Microsoft/GitHub OAuth). Fresh install has zero extensions. Install via CLI (scriptable) — code is auto-linked at /opt/homebrew/bin/code:
# Solidity / web3
code --install-extension NomicFoundation.hardhat-solidity # official Hardhat LSP, reads foundry.toml
code --install-extension tintinweb.solidity-visual-auditor # works on VS Code (MS Marketplace)
code --install-extension tintinweb.vscode-solidity-flattener
# web / TS / React
code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode
code --install-extension bradlc.vscode-tailwindcss
code --install-extension prisma.prisma
code --install-extension christian-kohler.path-intellisense
code --install-extension usernamehw.errorlens
code --install-extension eamodio.gitlens
code --install-extension yoavbls.pretty-ts-errors
code --install-extension mikestead.dotenv
# general
code --install-extension streetsidesoftware.code-spell-checker
code --install-extension gruntfuggly.todo-tree
code --install-extension editorconfig.editorconfig
# fully-offline in-editor AI → Continue.dev wired to local stack
code --install-extension Continue.continue # config: ~/.continue/config.yaml
Both tintinweb Solidity extensions install cleanly on VS Code (MS Marketplace). solidity-visual-auditor pulls in tintinweb.vscode-ethover, tintinweb.graphviz-interactive-preview, tintinweb.vscode-inline-bookmarks, tintinweb.vscode-solidity-language, and jebbs.plantuml as dependencies — ~21 total ext after install. Continue.dev's config.yaml is pre-wired to the MLX coder (port 1234), Ollama abliterated coders (11434), and nomic-embed-text — see Step 22.5.
Then set Foundry formatting in VS Code settings.json (~/Library/Application Support/Code/User/settings.json) and verify with code --list-extensions:
Enable built-ins (no Store install): Clipboard History (30-day), Window Management, Snippets, Calculator, File Search, Emoji Picker.
Store extensions (⌘↵ to install): GitHub, Brew, Speedtest, Color Picker, Coffee, Kill Process, Visual Studio Code recent projects, DevDocs, 1Password.
Finish replacing Spotlight: System Settings → Keyboard → Keyboard Shortcuts → Spotlight → uncheck ⌘Space. Confirm Raycast is on ⌥Space.
Obsidian — no vault exists. Create one on local disk (e.g. ~/2_KNOWLEDGE/vault; iCloud Drive is OFF). Settings → Community plugins → disable Restricted mode → Browse → install Dataview, Templater, QuickAdd, Tasks, Obsidian Git (optional: Advanced Tables, Excalidraw, Linter, Calendar, Homepage). Keep under ~30 plugins. Pairs with the Web Clipper extension in Step 10b.
Chrome — Chrome is the backup browser; do NOT mirror Brave's Step 10b list. Only: Google profile sign-in, 1Password (from the desktop app's Browsers pane), optional React Developer Tools. Ad/tracker blocking + wallet stay Brave's job.
(D) Login items & menu-bar auto-start
Three mechanisms: (1) classic Login Item (System Settings → General → Login Items & Extensions, inspect via osascript -e 'tell application "System Events" to get the name of every login item'); (2) in-app SMAppService toggle (inspect via sudo sfltool dumpbtm); (3) a LaunchAgent plist in ~/Library/LaunchAgents with RunAtLoad.
Ice — right-click menu bar → Ice Settings → General → Launch at login ON.
Stats — Settings → Start at login ON.
VoiceInk — Settings → Launch at login ON (also grant Microphone + Accessibility/Input Monitoring per (A)).
Tailscale — menu → Settings → Run Tailscale at login ON (it's running now only because launched manually).
Touch ID for sudo (scriptable):sudo ships /etc/pam.d/sudo_local.template but no active sudo_local. Enable Touch-ID-backed sudo:
sudo sed 's/^#auth/auth/' /etc/pam.d/sudo_local.template | sudo tee /etc/pam.d/sudo_local
# optional: add `auth sufficient pam_watchid.so` for Apple Watch unlock
(E) Local-AI (Nexus) manual steps
One-time human actions the Nexus lane assumes (architecture is in the Nexus section above).
Install opencode — MISSING from setup.sh/Brewfile (which opencode = not found): curl -fsSL https://opencode.ai/install | bash (or brew install anomalyco/tap/opencode). Config is already documented at ~/.config/opencode/opencode.json; launch with opencode. (scriptable)
Draw Things API Server (required for image gen, off by default, not scriptable): Draw Things → Settings → Advanced → API Server ON, HTTP, port 7860; keep DT running. Verify curl -s http://localhost:7860/sdapi/v1/sd-models. ⚠️Known bug (upstream drawthingsai/draw-things-community#59, open since 2026-03): the toggle does NOT persist between app launches — every relaunch reverts API Server to OFF. AppleScript UI auto-toggle won't help (DT is pure SwiftUI with zero accessibility labels). Workaround: accept the manual re-toggle each session, or use Open WebUI's native ComfyUI integration via :8002 and bypass DT entirely.
Open WebUI first-run (browser only): open http://localhost:8080 — the first account becomes admin. Then Settings → Interface → Always on Web Search ON (auto-queries SearXNG :8888). Verify curl -s http://localhost:8080/health.
Start now:Nexus.app menu → Start All & Open Web UI, or ~/.local-ai/start-all.sh (--no-open skips the browser; stop-all.sh frees RAM). Auto-start at login (scriptable) — run each on its own line (in zsh, ! a && b short-circuits):
One-time grants: the DT API toggle (E.2), Open WebUI admin signup (E.3), and OrbStack's privileged-helper prompt on first launch (approve once). No cloud logins; ANTHROPIC_BASE_URL stays unset (Step 22.5 guardrail).
Cheat sheet — keyboard shortcuts
Shortcut
Action
⌥+Space
Raycast launcher
⌘+⇧+3
CleanShot fullscreen
⌘+⇧+4
CleanShot region
⌘+⇧+5
CleanShot record video
⌘+⇧+T
CleanShot capture text (OCR)
⌘+⇧+G
CleanShot record GIF
⌘+⇧+8
CleanShot capture previous area
Cmd+Shift+Ctrl+4
Region → clipboard (for Claude image paste)
⌥+Tab
AltTab window switcher with previews
Ctrl+R
Atuin fuzzy history search
Ctrl+T
fzf file finder
Alt+C
fzf cd subdir
z foo
zoxide jump to most-visited "foo" dir
cc
Claude Code (Ghostty paste-safe)
code .
Open current folder in VS Code
lg
Lazygit TUI
mlx_max / mlx_revert
Toggle MLX max-power mode
`⌘+``
Ghostty Quake-style Quick Terminal (any app)
⌘+⇧+P
Ghostty Command Palette
Cost summary
Disk — ~30 GB tools + ~80 GB models = ~110 GB after full install
Active install time — ~90 min (downloads = overnight)
Recurring monthly — Claude Pro $20 + Raycast Pro $8 = ~$28/mo (VS Code free, Continue.dev free)
One-time — CleanShot $30 (+ optionally Hazel $42 if you want event-driven GUI rules instead of the launchd script in §21.9/§21.11). Cold Turkey removed 2026-05-30 (Tahoe persistence bug, see Step 11).
That's it. Every command above is annotated with what it does to your system. ~30 brew formulae + ~25 casks + 7 Python tools + 4 Ollama models + 8 config files. Production-ready M5 Max.
— Adam (fanout-verified, mnemosyne-checked, fully explained, 2026-05-27)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-d "{\"model\":\"$MODEL_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"Write a one-line Python function that reverses a string.\"}],\"max_tokens\":80}" \
| tee -a "$LOG"
echo "" | tee -a "$LOG"
# --- Decode benchmark: short prompt, 256 tokens ---
echo "[$(date +%T)] DECODE bench (short prompt, 256 new tokens):" | tee -a "$LOG"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
echo "=== 1. Server-side web search endpoint (the real test) ==="
# Required body = SearchForm: {"queries": [ ... ]} (queries is an ARRAY, required)
curl -sS -X POST "$BASE/api/v1/retrieval/process/web/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries": ["what is the Apple M5 Max chip"]}' \
-w "\nHTTP %{http_code}\n"
echo "=== 2. (optional) End-to-end via chat completions with web_search feature ==="
curl -sS -X POST "$BASE/api/chat/completions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"qwen3-coder-mlx","messages":[{"role":"user","content":"What is todays date in London?"}],"stream":false,"features":{"web_search":true}}' \
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Handoff for a peer Claude session on another machine. Captures what this session did,
the current verified state, and the gotchas worth knowing before you touch anything.
MCPs (user scope): filesystem, context7 (http), sequential-thinking, github — all ✓ Connected. brave-search skipped (no BRAVE_API_KEY).
Machine config touched this session
~/.zshrc: has cc alias + CLAUDE_CODE_* env vars (pre-existing); ADDED export GITHUB_PERSONAL_ACCESS_TOKEN="$(gh auth token)" (resolves from gh keyring at shell start — no plaintext token on disk) and mlx_max/mlx_revert helpers.
Configs written from setup.sh templates: ~/.config/{starship.toml, ghostty/config + themes/neon-noir, atuin/config.toml}, ~/.ssh/config github stanza.
Gist commits pushed this session (over SSH, fast-forward, no force)
82bf238 — model pin claude-opus-4-7-20260416 → claude-opus-4-7 (setup.sh + adam-mac-setup.md). The dated snapshot does not exist (claude rejects it).
cd0a45d — verify.sh MLX check false-positive → probe the per-project venv instead of global python.
(Drifts I found for the ollama :latest matcher and GPU-LaunchDaemon check were already fixed upstream before I got there — the gist is actively maintained from multiple machines.)
Software removed (each with explicit per-item consent)
procs, bottom, httpie (formulae) · mstystudio, notion, notion-calendar, slack, signal (casks).
brew cleanup freed ~5.6 GB. Note: the gist's current spec (verify.sh DROPPED_CASKS) treats
notion/notion-calendar/slack/signal as dropped — that's why they were removed.
GOTCHAS — read before acting
Model IDs: use bare claude-opus-4-7 (Opus 4.7) / claude-sonnet-4-6 / claude-haiku-4-5. Dated suffixes like -20260416 do NOT resolve.
gh is flaky in automated/sandboxed shells here — keychain access intermittently fails with "failed to change group ID: operation not permitted" (worsened by the $(gh auth token) zshrc line running gh at every shell init). For gist writes, use git over SSH (git push git@gist.github.com:<id>.git), NOT gh gist edit. Network commands need the sandbox disabled.
The gist is multi-machine maintained — ALWAYS git fetch + rebase/reset onto remote before editing; my first push was rejected because the remote was 19 commits ahead. Never force-push.
ANTHROPIC_BASE_URL must stay unset — claude CLI stays on Anthropic cloud (Claude Max). Local models (Ollama @ localhost:11434) drive opencode/Cursor only.
whisper-cpp ONLY (never pip install openai-whisper). iogpu cap = 122880 (NEVER 124000).
Foundry stays curl-installed (~/.foundry/bin); don't "migrate to brew" — docs intentionally match the curl reality.
Local-only artifacts (on the M5, NOT in this gist — won't reach you)
This handoff is the portable summary. Full detail lives in /tmp on the M5:
m5-audit-20260527.md (initial audit, 108/18/9), m5-install-fix.log (every install cmd+exit),
m5-verify-20260527.md (8/8 smoke tests), m5-debug-20260527-2315.md (model-pin root cause),
plus timestamped verify-*.log runs (1818 = 18 FAIL → 2343 = 0 FAIL/0 WARN).
Open / optional (nothing blocking)
Ghostty GUI behaviors await manual confirmation in a live tab (cmd+` quick terminal, cmd+shift+p command palette, dynamic tab title) — config is correct, just can't be exercised headlessly.
git signing uses plain ssh-keygen; setup.sh's spec prefers 1Password op-ssh-sign — switch by setting gpg.ssh.program once 1Password SSH agent is configured.
GUI app logins still manual: 1Password, Cursor, and license apps (Hazel, CleanShot, Cold Turkey).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters