iTerm2 + Claude Code

A Disciplined Claude Code Workstation for iTerm2

Four sessions, four locked roles, one window. Opus reviews, Sonnet builds, and the reviewer runs read-only — it cannot write files, whatever you ask it to do.

macOS · iTerm2 · Claude Code v2.1.278 · ~45 min · no background services
by Prav Durgani — freelance data & workflow automation, London

Part I — Install it

Seven steps, strictly sequential. Do them in order.

01

Prerequisites

Three tools to install before anything else. All three are Homebrew packages — if you already have them, each check-then-install is a single command.

1.1

Homebrew

Terminal
# Check if Homebrew is already installed:
command -v brew >/dev/null && echo "Homebrew already installed — skip" || \
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to your PATH (Apple Silicon only — skip if already in ~/.zshrc):
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc
source ~/.zshrc
1.2

Node.js (required for Claude Code)

brew install node
1.3

iTerm2

brew install --cask iterm2

Open iTerm2 once to complete its initial setup, then continue inside it. iTerm2 is required because $ITERM_PROFILE is what makes per-pane role detection work.

This repo

Later steps copy files out of it — the shell snippet, the hooks, the skills and the slash command. Clone it now and stay in that directory for the rest of Part I:

git clone https://github.com/pravindurgani/claude-code-multipane-iterm2.git
cd claude-code-multipane-iterm2
02

Install and authenticate Claude Code

Claude Code is Anthropic’s official CLI. Install it globally via npm, then authenticate. Browser OAuth is the recommended path — no API key required.

2.1

Install and authenticate

npm install -g @anthropic-ai/claude-code

Authenticate with browser OAuth (recommended):

claude auth login # opens a browser window — sign in with your Anthropic account

Sign in with your Anthropic account (Pro or Max plan). No API key needed with this method.

🔑
API key alternative (if you don’t have a Pro/Max subscription) Add export ANTHROPIC_API_KEY="sk-ant-your-key-here" to your ~/.zshrc so it persists across sessions. Get a key at console.anthropic.com → API Keys.
2.2

Verify

claude --version
03

Create four iTerm2 profiles

Create four named profiles, each with a distinct background tint, tab colour, and badge so you can instantly tell which pane you’re in.

Each pane runs a separate Claude Code session with a dedicated role. The AUDIT pane uses Opus for deep review. Everything else uses Sonnet to keep costs down. A cc alias launches the right configuration automatically.

AUDIT Opus
Modelopus
Efforthigh
Permissionplan (read-only)
Review completed work. Never generates code. Enforced read-only via --permission-mode plan. Scope reviews with: "Focus ONLY on src/auth.py. Do not read other files."
IMPL Sonnet
Modelsonnet
Efforthigh
PermissionacceptEdits
Write and edit code. Auto-accepts file edits. Run tests after every change.
PROMPT Sonnet
Modelsonnet
Effortmedium
Permissiondefault
Refine prompts, templates, and content. Separate from code changes.
PLAN Sonnet
Modelsonnet
Effortlow
Permissiondefault
Discuss architecture, plan features, review docs. No file writes.
📝
What this is NOT This is not an agent orchestrator. There are no background processes, no task queues, no dashboards. You run four focused sessions, you drive each one, you decide when work moves between panes. If you want 50 agents running unattended, other tools exist for that. This setup is for developers who want control, not automation.
💾
Hardware note: This 4-pane setup runs comfortably on 16GB+ machines. On systems with less than 16GB RAM, consider running 2–3 panes instead of all four, or closing inactive panes between tasks. Monitor with top -l 1 | grep PhysMem if you notice slowdowns.
⚠️
macOS + iTerm2 required This guide requires macOS with iTerm2 3.3+. The $ITERM_PROFILE environment variable that drives role detection is iTerm2-specific. A WSL2 + Windows Terminal equivalent ($WT_PROFILE_ID as the role signal) is possible but not documented here.
3.1

Create and name four profiles

Open iTerm2 → Settings → Profiles (⌘,). Click the + button four times. Double-click each "New Profile" name to rename it:

Profile NameBackground HexTab Colour HexRole
CC-AUDIT#0d0b18#a855f7Evaluation / Auditing
CC-IMPL#080f0b#22c55eImplementation
CC-PROMPT#080e10#06b6d4Prompt engineering
CC-PLAN#0d0b00#f59e0bPlanning / Architecture
iTerm2 Settings showing 4 named profiles in the sidebar
All 4 profiles created and named in the sidebar
3.2

Colors tab — background & tab colour

For each profile, click the Colors sub-tab:

  • Click the Background swatch in the "Defaults" row. In the macOS colour picker, switch to Hex mode (sliders icon → dropdown at bottom) and enter the hex value from the table above.
  • Scroll down to "Tab:", tick "Use custom tab color", and set the accent colour. Keep everything else from your base theme.
iTerm2 Colors tab showing tab color checkbox and badge color
Scrolled down in Colors tab — Tab colour and Badge colour visible at the bottom
3.3

General and Text tabs — title, badge, and font

Click the General sub-tab for each profile:

  • Title: Click the dropdown. Under "Foreground Job", uncheck "Job Name" so only "Session Name" stays ticked. This stops -zsh appearing after the role name.
  • Badge: Type the short role name. This overlays a faint watermark on the pane background.
CC-AUDIT → AUDIT
CC-IMPL → IMPL
CC-PROMPT → PROMPT
CC-PLAN → PLAN

Then click the Text sub-tab and set the font to JetBrains Mono 13pt (or Menlo 13pt).

iTerm2 General tab showing title, badge, and command settings
General tab — badge set to "AUDIT", command and initial directory configured
04

Startup command and initial directory

Each profile needs a startup command (to cd into the project directory and open an interactive shell) and an initial directory setting (as a fallback for window arrangement restores).

4.1

Set the startup command

In each profile → General sub-tab → change the Command dropdown from "Login Shell" to "Custom Shell" and enter:

# Same command for all 4 profiles — only the directory changes per project.
# Role detection uses $ITERM_PROFILE (set automatically by iTerm2),
# so the startup command doesn't need to set PANE_ROLE.
/bin/zsh -c 'cd ~/Desktop/your-project; exec zsh'

Replace your-project with your project directory name.

💡
Why exec zsh at the end? It replaces the subshell with an interactive zsh session. Without it, the pane would close the moment you exit Claude Code or any other command.
4.2

Set the initial directory

Still in the General sub-tab, find Initial directory below the Command field. Change from "Home directory" to "Directory:" and enter the full path:

/Users/yourname/Desktop/your-project
⚠️
Why set both Command and Initial Directory? Arrangement restore re-runs the Initial directory but not always the startup command. Setting both ensures you always land in the right folder regardless of how the pane was opened.
05

Shell snippet

Append zshrc-snippet.sh (from this repo) to your ~/.zshrc. It auto-detects which profile you’re in and configures coloured prompts, locked tab titles, and a cc alias to launch Claude Code with the right flags.

5.1

Append to ~/.zshrc

cat zshrc-snippet.sh >> ~/.zshrc
source ~/.zshrc

The snippet provides:

  • $PANE_ROLE and a coloured prompt per profile
  • a title-lock so the pane title stays fixed (won’t flip to -zsh)
  • the cc alias — launches Claude with the correct model, effort and permissions for the pane you are in
  • gate and ship aliases (IMPL pane only)
  • Ollama env vars and llm-* aliases (uncomment your tier’s block — Part III)

It keys off $ITERM_PROFILE and appends to precmd_functions so it won’t clobber pyenv, fnm or conda.

5.2

What the snippet contains

~/.zshrc — append at bottom
# ── Multi-pane Claude Code workflow ──
# Uses $ITERM_PROFILE (auto-set by iTerm2 on every
# session, including arrangement restores).
case "$ITERM_PROFILE" in
CC-AUDIT)
PANE_ROLE="AUDIT"
PROMPT="%F{magenta}[AUDIT]%f %~ %# " ;;
CC-IMPL)
PANE_ROLE="IMPL"
PROMPT="%F{green}[IMPL]%f %~ %# " ;;
CC-PROMPT)
PANE_ROLE="PROMPT"
PROMPT="%F{cyan}[PROMPT]%f %~ %# " ;;
CC-PLAN)
PANE_ROLE="PLAN"
PROMPT="%F{yellow}[PLAN]%f %~ %# " ;;
esac
if [[ -n "$PANE_ROLE" ]]; then
echo -ne "\033]0;${PANE_ROLE}\007"
_pane_title_precmd() {
echo -ne "\033]0;${PANE_ROLE}\007"
}
precmd_functions+=(_pane_title_precmd)
fi
# ── Claude Code launch alias (role-aware) ──
# Type "cc" to launch with the right flags.
case "$ITERM_PROFILE" in
CC-AUDIT) alias cc='claude --model opus --effort high --permission-mode plan' ;;
CC-IMPL) alias cc='claude --model sonnet --effort high --permission-mode acceptEdits' ;;
CC-PROMPT) alias cc='claude --model sonnet --effort medium' ;;
CC-PLAN) alias cc='claude --model sonnet --effort low' ;;
esac
✓
Why this works reliably
  • $ITERM_PROFILE is set automatically by iTerm2 on every session — including arrangement restores. No dependency on startup commands.
  • precmd_functions+=() adds to the hook array rather than overwriting it, so existing hooks from pyenv, fnm, conda, etc. are preserved.
  • Normal terminals (Default profile) are completely unaffected.
5.3

Verify

Open a new pane using the CC-IMPL profile, then:

echo $PANE_ROLE # should print: IMPL
echo $ITERM_PROFILE # should print: CC-IMPL

If $PANE_ROLE is empty, the pane was not opened with a CC-* profile — open it via Profiles → CC-IMPL in the menu bar.

06

Window layout and saved arrangement

Create a 2×2 pane layout, save it, and configure iTerm2 to restore it automatically on launch.

Target layout
AUDIT
adversarial review
opus · high · plan
PROMPT
prompt engineering
sonnet · medium
IMPL
code writing
sonnet · high · acceptEdits
PLAN
architecture & docs
sonnet · low
6.1

Create the 2×2 split

  1. Open a new window with the CC-AUDIT profile.
  2. ⌘D — split right. Right-click the new pane → Edit Session → change profile to CC-PROMPT.
  3. Click back on the left pane (AUDIT). ⌘⇧D — split down. Change the new bottom-left pane to CC-IMPL.
  4. Click on the right pane (PROMPT). ⌘⇧D — split down. Change the new bottom-right pane to CC-PLAN.
4-pane iTerm2 layout with coloured prompts and badges
Final result — 4 panes with coloured prompts, badges, and distinct background tints
6.2

Save as default arrangement

  1. Window → Save Window Arrangement — give it a name (e.g. your project name).
  2. Go to iTerm2 → Settings → General → Startup and set the window restoration policy to "Open Default Window Arrangement".
  3. Back in the menu: Window → Save Window Arrangement once more, selecting the same name — this saves it as the default arrangement the startup policy will use.
Window menu showing Save Window Arrangement option
Window → Arrangements → Save Window Arrangement
iTerm2 General Startup settings showing Open Default Window Arrangement
Settings → General → Startup → "Open Default Window Arrangement"
✓
Result: iTerm2 now opens the four-pane layout automatically on launch, with the correct profiles, directories, coloured prompts, and badges.
🖥
Dual monitor variant AUDIT + PLAN on monitor 1, IMPL + PROMPT on monitor 2. Switch between macOS Spaces with ctrl+1/2/3/4.
07

Launch with cc

Type cc in each pane. That’s it. The alias expands per pane.

Panecc expands toWhat you see
AUDIT claude --model opus --effort high --permission-mode plan Opus · plan mode on
IMPL claude --model sonnet --effort high --permission-mode acceptEdits Sonnet · accept edits on
PROMPT claude --model sonnet --effort medium Sonnet · medium effort
PLAN claude --model sonnet --effort low Sonnet · low effort
All 4 panes with Claude Code running showing different models and effort levels
All 4 panes with Claude Code running — Opus in AUDIT (plan mode), Sonnet in the rest
⚠️
Why Opus only in AUDIT Opus costs about 2.5× what Sonnet costs per token. Reserve it for the review pass, where a second opinion from a stronger model earns the money, and leave the other three panes on Sonnet.
💡
Override anytime: The cc alias is a convenience, not a lock-in. You can always type the full command with different flags when you need to (e.g. claude --model opus --effort max).

Note: On some systems, cc is aliased to the C compiler. If you work with C/C++, rename the alias to cl or claude-go in your ~/.zshrc.
Ref

Verified CLI flags

FlagPurpose
--model opusUse Opus (aliases: opus, sonnet, haiku, or a full ID like claude-fable-5)
--effort highThinking budget: low · medium · high · xhigh · max. PLAN uses low — architectural discussion doesn’t need deep reasoning chains.
--permission-mode planRead-only — Claude can’t write files. Full set: plan, acceptEdits, auto, manual, dontAsk, bypassPermissions
--permission-mode acceptEditsAuto-accept file edits without asking
--append-system-prompt "..."Add custom instructions on top of defaults
--continueResume most recent conversation
--resumeResume a specific session by ID
⚠️
On --dangerously-skip-permissions: --permission-mode acceptEdits is more targeted — it auto-accepts file edits while still asking before shell commands. Only use --dangerously-skip-permissions in fully sandboxed environments.
⚠️
Version note: Flags verified against Claude Code v2.1.278 (September 2026). CLI tools update frequently — run claude --help if a flag isn’t recognised.

Part II — Work in it

08

How work moves between panes

Work goes round a fixed loop, and you carry it across pane boundaries by hand.

↺

The change cycle

PLAN → Discuss approach. No file writes. Get architecture sign-off.
IMPL → Implement. Run tests immediately after (must pass).
AUDIT → Review the changed files (read-only). Feed findings back to IMPL.
PROMPT → (When prompt/content files change) Separate from code changes.
⚡

Carrying findings across

When AUDIT identifies an issue, copy its output and paste it into IMPL with:

The AUDIT pane identified: [paste findings here].
Fix this while preserving existing patterns.
Do not touch unrelated files.
💡

Context hygiene

  • Run /clear in the AUDIT pane before every review pass. Otherwise AUDIT reviews the versions cached in its context, not what IMPL just wrote.
  • Re-anchor on long sessions: "Before starting, re-read CLAUDE.md and confirm the project invariants. Then…"
  • Scope AUDIT explicitly: "Focus ONLY on src/auth.py and src/middleware.py. Do not read any other files unless I explicitly ask."
  • /compact frees context without losing all history — use it instead of /clear when the history still matters.
🎯
The golden rule: When a conversation goes stale — Claude starts repeating itself or loses track of files — type /clear and start fresh with a tightly-scoped prompt. Short, focused sessions always beat long sprawling ones.
09

The gate and ship commands

Nothing reaches AUDIT until the tests pass.

AliasWhat it runsWhen to use
gatepytest suite (tests/); exits non-zero on failureBefore handing off to AUDIT
shipgate + interactive git add -p + git commitWhen tests pass and work is commit-ready
1

Using them

# After making changes:
gate
# → ... pytest output ...
# → ✅ GATE PASSED
# When ready to commit:
ship
# → runs gate, then prompts for staged hunks + commit message

gate and ship are defined only when $ITERM_PROFILE == CC-IMPL. Running them in other panes is a harmless no-op.

2

The loop

IMPL → implement → gate (must pass) → AUDIT → findings → IMPL → fix → gate → AUDIT

Start pytest-only; add lint, type checking and a docker build to gate once you know they are load-bearing.

✓
Why this matters Sending failing code to AUDIT wastes Opus tokens on defects the test suite already catches. The gate alias makes this impossible to forget.
10

The SESSION_LOG pattern

A SESSION_LOG gives the next session somewhere to resume from.

1

Keep SESSION_LOG.md in the project root

Append a new entry at the end of every session; never overwrite old entries. Minimum viable format:

### YYYY-MM-DD — one-line task summary
- **Done**: what was completed this session
- **Decisions**: any architectural choices made
- **Next**: open items or follow-ups for the next session

Real example:

### 2026-03-22 — Add rate-limit retry to API client
- **Done**: Implemented exponential backoff in api_client.py. All 24 tests pass.
- **Decisions**: Max 3 retries, 2s base delay. Errors logged with context, not raised.
- **Next**: AUDIT review of api_client.py. Then wire retry into pipeline scheduler.
2

Instruct Claude to read it on session start

In your CLAUDE.md (see section 11), add a Session Continuity instruction: "At the start of every session, read the last 60 lines of SESSION_LOG.md and surface the most recent 'Next:' items before doing anything else."

11

Splitting rules: CLAUDE.md and AGENTS.md

Split global rules from project rules so neither leaks into the other.

1

Two files, two scopes

ScopePut it inCommitted?
Your personal conventions, every project~/.claude/CLAUDE.mdNo — personal
Shared project rules, every agent reads themAGENTS.mdYes
Claude-only additions for this projectCLAUDE.md importing @AGENTS.mdYes

Load order is: managed policy, then user ~/.claude/CLAUDE.md, then project ./CLAUDE.md, then ./CLAUDE.local.md. They concatenate — later files add to earlier ones, they do not override them.

@path imports pull one rules file into another. Absolute, ~ and relative paths all work, and imports nest up to four levels deep.

💡
Keep global rules lean Your ~/.claude/CLAUDE.md is loaded into every session across every project. Move project-specific detail into the repo’s .claude/CLAUDE.md.
2

Sharing one rule set with other agents

AGENTS.md is an open standard (agents.md) used by over 60,000 open-source projects and supported by a couple of dozen agents, among them Codex, Cursor, Copilot, Gemini CLI, Zed and Aider. From v2.1.277 Claude Code reads it too.

Claude Code reads AGENTS.md only when it finds no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md in that directory or any directory above it. Your personal ~/.claude/CLAUDE.md does not count — it keeps loading alongside.

So the portable pattern is: put the shared rules in AGENTS.md, and make CLAUDE.md a one-line import.

echo '@AGENTS.md' > CLAUDE.md

Every agent then reads the same rules, and you can still append Claude-only rules underneath the import line.

3

Use the included templates

Two starter templates ship with this repo: CLAUDE.md.template (copy to ~/.claude/CLAUDE.md, do not commit) and REFERENCE.md.template (copy to .claude/REFERENCE.md and commit — the AUDIT pane uses it for sprint context and known issues).

# Global rules — do NOT commit
cp CLAUDE.md.template ~/.claude/CLAUDE.md
# Project reference — commit this
cp REFERENCE.md.template .claude/REFERENCE.md
⚠️
The common mistake Rules files past roughly 200 lines dilute adherence — the model reads everything and weights nothing. Prose is a suggestion. Anything that must be enforced belongs in a hook (Part III), not in a rules file.
12

Adapting it to another project

This setup is project-agnostic. To use it with a different codebase:

1

Rename profiles

CC-AUDIT → MYPROJECT-AUDIT, and so on for IMPL, PROMPT and PLAN.

2

Update Initial directory in each profile

Point each profile’s General tab at the new project path.

3

Update ~/.zshrc

Add new case entries matching the new profile names, in both case blocks:

# Example with a project-specific prefix
MYPROJECT-AUDIT)
PANE_ROLE="AUDIT"
PROMPT="%F{magenta}[AUDIT]%f %~ %# " ;;
# ... same pattern for MYPROJECT-IMPL, MYPROJECT-PROMPT, MYPROJECT-PLAN
4

Add project-specific slash commands

Drop them in .claude/commands/ so they travel with the repo.

5

Save a project-specific arrangement

Name it after the project (Window → Save Window Arrangement). To switch between projects later, use Window → Arrangements → [arrangement name] — iTerm2 opens that project’s saved 4-pane layout in a new window.

Part III — Optional add-ons

Each of these is independent. Skip any of them and Parts I and II still work.

13

Safety hooks

Hooks enforce what prompt instructions only request. Claude Code runs them before and after tool use, and at session start.

TierEventPurpose
PreToolUseBefore the tool executesBlock dangerous actions before they happen
PostToolUseAfter the tool returnsObserve outcomes; trip circuit-breaker on repeat failures
SessionStartWhen a new session opensReset counters; validate session state
1

Copy the hook scripts to ~/.claude/hooks/

mkdir -p ~/.claude/hooks
cp hooks/protect-env.py ~/.claude/hooks/
cp hooks/protect-git-push.py ~/.claude/hooks/
cp hooks/circuit-breaker.py ~/.claude/hooks/
cp hooks/session-start-reset.py ~/.claude/hooks/
cp hooks/version-check.py ~/.claude/hooks/
2

Merge hooks/settings.json.example into ~/.claude/settings.json

Merge the "hooks" block from hooks/settings.json.example into the top level of ~/.claude/settings.json. All "command" values use $HOME — Claude Code does not expand ~.

💡
Why $HOME instead of ~? Claude Code does not expand ~ in hook command strings. Use $HOME, which the shell expands at execution time, or replace it with your full absolute path.

The example also sets CLAUDE_AUTOCOMPACT_PCT_OVERRIDE to "50", auto-compacting the conversation at 50% of the context window; "0" disables it.

3

What each hook does

HookTierBlocks when
protect-env.pyPreToolUseEdit/Write/MultiEdit targets any .env file
protect-git-push.pyPreToolUseBash command matches git … push (any flag order)
circuit-breaker.pyPostToolUse3 consecutive tool failures in a session
session-start-reset.pySessionStart(resets failure counter — never blocks)
version-check.pySessionStart(never blocks — prints update checklist when Claude Code version changes)
⚠️
Platform note: the circuit-breaker hook uses fcntl, so the hooks run on macOS and Linux only.
14

MCP server, slash commands and skills

A GitHub MCP integration, the /reflect command, and three contextual skills.

1

GitHub MCP server

Claude Code registers user- and local-scoped MCP servers via claude mcp add, and reads project-scoped servers from a committed .mcp.json at the repo root. The included .mcp.json.example is reference JSON if you need claude mcp add-json instead.

brew install github-mcp-server
claude mcp add github -s user \
-e GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_readonly_token \
-- $(which github-mcp-server) stdio

Generate a read-only PAT at github.com/settings/tokens with scope public_repo (or repo for private-repo access); recommended expiry 90 days. Verify with claude mcp list | grep github. -s user registers the server for all projects — omit it to restrict to the current one.

2

/reflect slash command

commands/reflect.md defines a command you run at the end of an IMPL or AUDIT session: it reads the recent git log and diff, then outputs a table of suggested CLAUDE.md additions. It never edits the file — you decide what to incorporate.

mkdir -p ~/.claude/commands
cp commands/reflect.md ~/.claude/commands/reflect.md
3

Contextual skills

The skills/ directory holds three skills that load automatically when the task matches their trigger description.

SkillWhen it activatesWhat it adds
code-reviewCode review tasks (AUDIT pane)Project-specific review conventions
security-auditSecurity review tasks (AUDIT pane)Security checklist and vulnerability patterns
testingWriting/reviewing tests (IMPL pane)pytest conventions matching the project
cp -r skills/ ~/.claude/skills/
15

Local models with Ollama

Private inference on your own machine, with no API calls. Nothing else in this guide requires Ollama — only Sigil (section 16) does, for its fact classifier.

TierRAMWhat you get
🌺 Base16 GB+Claude Code + small local models (fast/code/embed)
🔵 Mid32 GB+Above + reasoning model + larger fast/code models
🔴 Full64 GB+Above + vision model + 32B code model (~20 GB)
brew install ollama
brew services start ollama

Pull the block matching your RAM. Times assume a 100 Mbps connection.

🌺

16 GB+ (~15 min, ~10 GB)

ollama pull qwen3:8b # ~5 GB — fast daily driver
ollama pull qwen2.5-coder:7b # ~4.7 GB — code specialist
ollama pull nomic-embed-text # ~274 MB — embeddings
🔵

32 GB+ (~40 min, ~23 GB total) — pull these instead of the 8b/7b versions

ollama pull qwen3:14b # ~9 GB — replaces qwen3:8b
ollama pull qwen2.5-coder:14b # ~9 GB — replaces qwen2.5-coder:7b
ollama pull nomic-embed-text # ~274 MB
ollama pull deepseek-r1:8b # ~5 GB — structured reasoning
🔴

64 GB+ (~90 min, ~55 GB total) — use these instead of the 14b versions

ollama pull qwen3:14b # ~9 GB
ollama pull qwen2.5-coder:32b # ~20 GB — replaces qwen2.5-coder:14b
ollama pull nomic-embed-text # ~274 MB
ollama pull deepseek-r1:8b # ~5 GB
ollama pull gemma3:27b # ~16 GB — vision + heavy reasoning
⚠️
RAM note on 16 GB: The reasoning model (deepseek-r1) competes with Claude Code’s working set. Omitted intentionally — upgrade tier to enable.

Uncomment your tier’s block in the Ollama section at the bottom of zshrc-snippet.sh to get llm-fast, llm-code, llm-reason (🔵 32 GB+), llm-embed and the llm-smart router.

16

Persistent memory with Sigil

Every pane is a separate session, and every session starts with amnesia.

Sigil is an open-source, local-first memory system by Anmol Srivastava. It runs hooks inside every Claude Code session that inject relevant stored facts before Claude sees your prompt, and capture memorable ones afterwards.

Why this setup in particular benefits: the SESSION_LOG pattern carries context forward in time within one project. Sigil carries it sideways — across all four panes and across project boundaries, so you stop re-explaining "we use pnpm, not npm" in every pane, every session.

# Install (clones to ~/.sigil/app, adds to PATH, starts the daemon)
curl -fsSL https://raw.githubusercontent.com/Anmol-Srv/sigil/master/install.sh | sh
# Configure — interactive wizard with live connection tests
sigil init
⚠️
The one setting that matters: point the LLM provider at a local Ollama model. Claude Code runs UserPromptSubmit hooks synchronously on every prompt you type, with a roughly 10-second budget. A provider that shells out to claude -p takes about 16 seconds per call — every prompt then shows UserPromptSubmit hook timed out after 10s and you get no memory injection at all. Fact classification is a routing job, not a reasoning job.

Everything else — database choice, embedding provider, namespaces, the sigil facts / remember / search / ingest commands, MCP wiring for other clients — is covered in the upstream README at github.com/Anmol-Srv/sigil. Never store secrets in it: the database is local but not encrypted at rest.

17

iTerm2 triggers

Auto-highlight keywords in terminal output.

Profile → Advanced → Triggers → +

RegexActionColour
\b(CRITICAL|ERROR|FAIL(ED)?)\bHighlight TextRed background
\b(PASS(ED)?|SUCCESS)\bHighlight TextGreen background
\b(WARNING|WARN|TODO)\bHighlight TextYellow background

Part IV — Reference

18

Agent teams or four panes?

Claude Code can already spawn teammates into split panes — it solves a different problem.

Agent Teams is a built-in, experimental feature, off by default. Enable it with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. iTerm2 split panes additionally need the it2 CLI and the iTerm2 Python API enabled.

Teammates run inside one lead session, and that is where the difference bites:

🎯
The verdict Use agent teams for short bursts of parallel exploration inside a single task — several teammates reading different subsystems at once. Use this four-pane setup when you want durable, separately-permissioned sessions that outlive a task, and a reviewer that genuinely cannot write files. They are complementary, not competing; nothing stops you spawning a team inside the IMPL pane. If Anthropic later adds per-teammate permission modes, agent teams will cover much of what this setup exists for.
19

Common questions

Q1

Can I run this with tmux instead of iTerm2?

Partly — you would have to replace the role-detection mechanism. The snippet branches on $ITERM_PROFILE, which iTerm2 sets for you on every session, including arrangement restores. In tmux you would set your own per-pane environment variable and branch on that, and rebuild the layout and colours in tmux config.

Q2

Does the reviewer really not write files?

Yes. The AUDIT pane launches with --permission-mode plan, which is enforced by Claude Code itself, not by an instruction in a prompt. It can read, search and report; it cannot edit.

Q3

How much does running Opus in one pane cost?

About 2.5× Sonnet per token — Opus 5 is $5/$25 per million against Sonnet 5's $2/$10, checked September 2026 — and only on review passes. AUDIT is idle while you implement, and a review reads far fewer tokens than an implementation session writes. The other three panes stay on Sonnet.

Q4

Can I use fewer than four panes?

Yes — IMPL plus AUDIT is the pair that earns its keep. PLAN and PROMPT are conveniences; drop them and use /clear in IMPL when you switch modes. Keep the separation between the pane that writes and the pane that reviews.

Q5

Does this work on Linux or Windows?

Claude Code does; this layout does not. The profiles, arrangements and $ITERM_PROFILE detection are macOS + iTerm2. The circuit-breaker hook uses fcntl, so they run on macOS and Linux but not Windows.

Q6

Do I need Ollama?

No. Nothing in Parts I or II touches it. It is only a prerequisite if you install Sigil and want its classifier to stay inside the hook time budget.

20

Troubleshooting

Common failure modes and how to fix them.

T1

$ITERM_PROFILE is empty, cc launches with wrong model

Symptom: echo $ITERM_PROFILE returns nothing; the cc alias falls through with no model flags.

Cause: iTerm2 older than 3.3, or the pane was opened before the profile was applied.

Fix: update iTerm2 to 3.3+ (Help → Check For Updates), then reopen the pane via Profiles → [your profile name] → Open in current tab. Confirm with echo $ITERM_PROFILE — it should print CC-AUDIT, etc.

T2

cc launches the C compiler instead of Claude

Symptom: which cc shows /usr/bin/cc — cc is the C compiler on many systems.

Fix: rename the alias in ~/.zshrc, replacing all 4 alias cc= occurrences with alias cl= (or any name), then source ~/.zshrc.

# In ~/.zshrc — change all 4 occurrences:
CC-AUDIT) alias cl='claude --model opus --effort high --permission-mode plan' ;;
CC-IMPL) alias cl='claude --model sonnet --effort high --permission-mode acceptEdits' ;;
# ... then:
source ~/.zshrc
T3

Hooks not firing

Symptom: .env edits or git push commands are not blocked.

# Check 1 — files exist
ls ~/.claude/hooks/
# Expected: protect-env.py protect-git-push.py circuit-breaker.py session-start-reset.py version-check.py
# Check 2 — python3 available
which python3 # must return a path; if missing: brew install python3
# Check 3 — settings.json is valid
python3 -m json.tool ~/.claude/settings.json # prints formatted JSON on success
# Errors on a line starting with # ? You pasted the example file wholesale.
# settings.json must be pure JSON — strip the comment lines.
T4

Circuit-breaker stuck after tool failures

Fix A: press Ctrl+C, then cc — session-start-reset.py resets the counter at session start.

Fix B: delete the state file:

rm -f ./circuit-breaker-state.json
# Then /clear inside Claude Code to reset conversation context.
T5

gate fails with "pytest not found" or 0 tests collected

  • Activate your venv first: source venv/bin/activate (or .venv/bin/activate).
  • Verify: which pytest should point inside your venv. If not: pip install pytest.
  • If there are no test files yet, add a placeholder: touch tests/test_placeholder.py.
T6

Need to push to git, the hook is blocking it

protect-git-push.py blocks Claude from pushing autonomously. Hooks only intercept tool calls inside a session, so push from a regular shell:

# Open a new tab (not inside a Claude session) and run:
git push
⚠️
This is by design. Review the diff yourself before pushing — that’s the point of the hook.
T7

Claude CLI update broke the cc alias

Symptom: after npm update -g @anthropic-ai/claude-code, cc errors with an unknown flag.

  1. Run claude --help to see current supported flags.
  2. Update the alias block in ~/.zshrc to match, then source ~/.zshrc.
  3. Run claude --version to confirm your installed version. This guide was verified against v2.1.278 (September 2026).
💡
Tip: The version-check.py hook detects version changes automatically and prints this checklist at session start.
T8

Ollama: model not found or server not running

Symptom: ollama run qwen3:8b hangs or returns "model not found."

# Check 1 — server is running
curl http://localhost:11434/ # should return "Ollama is running"
# Check 2 — model is pulled
ollama list # lists all downloaded models
# If server is not running:
ollama serve &
# If model is missing:
ollama pull qwen3:8b # (or whichever model)
21

Quick reference

Everything on one screen — copy, paste, go.

┌──────────────────────────────────────────────────────────┐
│ LAUNCH │
├──────────────────────────────────────────────────────────┤
│ Type "cc" in any pane — alias handles the rest. │
│ │
│ AUDIT: opus · high effort · plan (read-only) │
│ IMPL: sonnet · high effort · acceptEdits │
│ PROMPT: sonnet · medium effort │
│ PLAN: sonnet · low effort │
├──────────────────────────────────────────────────────────┤
│ LOCAL AI │
├──────────────────────────────────────────────────────────┤
│ llm-fast "..." → qwen3 (general) │
│ llm-code "..." → qwen2.5-coder (code) │
│ llm-reason "..." → deepseek-r1 (reasoning, 32GB+) │
│ llm-smart "..." [fast|code|reason|embed] → router │
│ ollama list → show downloaded models │
├──────────────────────────────────────────────────────────┤
│ NAVIGATION │
├──────────────────────────────────────────────────────────┤
│ ⌘⌥ arrows = switch panes ⌘⇧↵ = zoom pane │
│ ⌘D = split right ⌘⇧D = split down │
│ Esc = stop generation /clear = reset context │
├──────────────────────────────────────────────────────────┤
│ WORKFLOW │
├──────────────────────────────────────────────────────────┤
│ PLAN → discuss approach (no writes) │
│ IMPL → implement + gate (must pass) → ship to commit │
│ AUDIT → review changed files (read-only) │
│ /clear AUDIT before review (state sync) │
│ PROMPT → prompt/content changes (separate from code) │
│ │
│ gate = run pytest suite ship = gate + git add -p │
└──────────────────────────────────────────────────────────┘
1

Pane roles

PaneModelEffortPermissionPurpose
AUDITOpushighplanCode review (read-only)
IMPLSonnethighacceptEditsWrite code + run tests
PROMPTSonnetmediumdefaultPrompt engineering
PLANSonnetlowdefaultArchitecture + planning
2

Shell aliases

AliasPaneWhat it does
ccAllLaunch Claude Code with role-correct flags
gateIMPLRun full test suite — must pass before AUDIT handoff
shipIMPLgate + git add -p + git commit
3

Keyboard shortcuts

iTerm2 navigation

⌘⌥←/→
Switch panes (left/right)
⌘⌥↑/↓
Switch panes (up/down)
⌘D
Split right
⌘⇧D
Split down
⌘⇧⏎
Zoom current pane (toggle)
⌘1-4
Switch tab
⌘F
Find in output
⌘M
Set mark (bookmark position)
⌘⇧↑
Jump to previous mark
⌘K
Clear terminal buffer
⌘⌥E
Broadcast input to all panes (careful)

Claude Code (inside a session)

Esc
Interrupt generation
CtrlC
Cancel, return to prompt
/clear
Reset conversation context
/compact
Compress context (keeps summary)
/model
Switch model mid-session
/effort
Switch effort mid-session
/help
List commands
4

Daily workflow

Morning boot: open iTerm2 (arrangement auto-restores) → cc in each pane → /clear in AUDIT → PLAN reviews git log --oneline -10 → IMPL smoke test with pytest tests/ -x --tb=short.

End of session: IMPL runs the full suite → ship to commit → /compact any long contexts → re-save the arrangement if the layout changed.

# Morning
1. Open iTerm2 (arrangement auto-restores)
2. Type cc in each pane
 
# Working
3. PLAN → discuss approach (no writes)
4. IMPL → implement → gate (must pass)
5. AUDIT → review changed files → findings back to IMPL
   /clear AUDIT before reviewing (state sync)
 
# End of day
6. IMPL → ship (gate + stage + commit)
7. Update SESSION_LOG.md
8. /compact in long sessions
5

Safety hooks

HookEventBlocks
protect-env.pyPreToolUseAny edit to .env files
protect-git-push.pyPreToolUsegit push commands
circuit-breaker.pyPostToolUse3 consecutive tool failures
session-start-reset.pySessionStart(resets counter)
version-check.pySessionStart(never blocks — prints update checklist on version change)

Appendix

22

Retired: pane handoff

An automated AUDIT ↔ IMPL pane handoff add-on was retired on 2026-09-21. Its files are archived under archive/handoff-2026-06/. Persistent memory plus a shared AGENTS.md replaced it. See archive/handoff-2026-06/README.md if you want to restore it.