The CLI does the work. The settings, hooks, skills, and habits make it 10× faster than the same model on the web.
This doc is what we'd hand a new pro joining the team — a tight install, the exact settings.json we use,
and the workflow rules that turn Claude Code into an editor that ships code instead of a chatbot that suggests it.
Mark — this is how we work. Read top-to-bottom once, then come back to the section you need. Setup steps land you in 30 minutes; the workflow section is where the leverage is.
| Habit | What it looks like |
|---|---|
| Plan before you build. | Decide → align → execute. Never code before the plan is named. |
| Verify your own work. | A green build is not evidence; a passing test against the actual user flow is. Same-session toasts don't count. |
| Multiple choice over open-ended. | Force decisions with a 2–4 option picker, not "what do you want?". Faster turns. |
| Cheap models for grunt work. | Dispatch greps, lints, JSON parsing, retries to Haiku/Llama. Keep the expensive brain for judgment. |
| No auto-publish. | Robert reviews before anything posts. Same applies to Mark. The agent never pushes to main, never sends a Slack DM, never deletes branches without a checkpoint. |
| Terse responses. | One sentence per update. Skip the recap — Mark can read the diff. |
If you find yourself writing a 6-paragraph "here's what I did" message, stop. We've coded that out. Read the diff, send a one-liner.
Don't skip this. Powerlevel10k, iTerm2, modern CLI tools, fzf, zoxide. Full instructions in the dev-terminal skill — Robert sent it separately. Do that before anything else. A 1990s terminal is a tax on every command.
# Install
npm install -g @anthropic-ai/claude-code
# Verify
claude --version
# First run — authenticates via browser
claude
The CLI lives at claude. Run it in any project root. The first run pops a browser to OAuth into Anthropic. Copy the API key into your shell rc file as a fallback:
# ~/.zshrc
export ANTHROPIC_API_KEY="sk-ant-..."
Default model: claude-opus-4-7 for everything that matters. Switch to claude-sonnet-4-6 for higher-throughput work, claude-haiku-4-5 for cheap grunt work. Toggle model at runtime with /model.
settings.json we useTwo files. Both live in ~/.claude/.
~/.claude/settings.json — global defaults, machine-wide. Checked into your dotfiles if you have them.
{
"model": "claude-opus-4-7",
"permissions": {
"allow": [
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(pnpm install:*)",
"Bash(pnpm dev:*)",
"Bash(pnpm build:*)",
"Bash(pnpm test:*)",
"Bash(pnpm lint:*)",
"Bash(pnpm typecheck:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(rg:*)",
"Bash(fd:*)",
"Bash(gh pr view:*)",
"Bash(gh pr list:*)",
"Bash(gh repo view:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push --force:*)",
"Bash(git reset --hard:*)"
]
},
"env": {
"EDITOR": "code --wait",
"PAGER": "delta"
}
}
git status, git diff, ls, rg — you'll never approve them again. The skill fewer-permission-prompts (a slash command) auto-generates this list from your transcripts.pnpm dev/build/test/typecheck/lint — the agent should be running these constantly. Approving each one is friction.rm -rf, force-push, hard-reset always prompt. Even if the agent is convinced it's right.Bash(*) allow. Tempting. Don't. The friction of approving an unfamiliar command is the safety net that lets you trust the rest.~/.claude/settings.local.json — per-project overrides (or the same shape, just narrower). You can also put a settings.json at the repo root in .claude/settings.json so the project ships its own permissions. Useful for "in this repo, allow X".
The line at the bottom of your terminal showing current model, branch, project, time. Two routes:
Quick: /statusline slash command — Claude writes one for you based on what you ask for.
Curated: drop this into ~/.claude/statusline.sh, mark it executable, and reference it in settings.json:
#!/usr/bin/env bash
# ~/.claude/statusline.sh
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name // "unknown"')
cwd=$(echo "$input" | jq -r '.workspace.current_dir' | sed "s|$HOME|~|")
branch=$(cd "$(echo "$input" | jq -r '.workspace.current_dir')" 2>/dev/null && git branch --show-current 2>/dev/null)
printf "\033[2m%s\033[0m \033[1m%s\033[0m %s%s" \
"$model" "$cwd" \
"$([ -n "$branch" ] && echo "$branch")" \
""
Then in settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
Now every prompt shows: opus-4-7 ~/Projects/orbiter-sandbox feat/interview-flow.
agent-browser (the verification primitive)This is non-negotiable for frontend work. The agent cannot trust its own toasts. It needs a real browser.
# Install
npm install -g @anthropic-ai/agent-browser
# Verify
agent-browser --version
# Headed Chrome (Google OAuth requires headed)
agent-browser --headed open http://localhost:3001
agent-browser set viewport 1440 810
The pattern Mark — when you ask Claude to verify a UI change:
Open the running dev server, click the thing, screenshot the result, OCR the relevant area, confirm the text matches what you expected. Only then is it done.
This is what "verify your own work" means in practice. Without agent-browser, the agent ships UI changes that look right in a code review and break the moment a human clicks them.
Skills are markdown files at ~/.claude/skills/<skill-name>/SKILL.md. The agent reads them when relevant. They're how you encode "always do X this way" without retraining a model.
ls ~/.claude/skills/ # Robert ships ~200 of these
To use a skill in a session, name it: "use the dev-terminal skill" or in a slash command: /dev-terminal. The agent loads it.
To create one:
~/.claude/skills/my-skill/
├── SKILL.md # the actual instructions, with YAML frontmatter
└── (any other reference files the skill points to)
Frontmatter shape:
---
name: my-skill
description: One-line summary the agent uses to decide if this skill applies.
---
# My Skill
Body...
The trigger pattern: the description field is what the agent matches against your request. Be specific — "Use this skill when the user asks for X, Y, or Z". The looser the description, the more often it loads when irrelevant.
We'll bundle a starter set in your ~/.claude/skills/ once you're set up — mark-onboarding, dev-terminal, crayon-sdk, xano-mcp-workflow, xanoscript-builder, and a couple Orbiter-specific ones.
Every repo gets a CLAUDE.md at the root. The CLI auto-loads it on startup.
# CLAUDE.md
## Build & Development Commands
```bash
pnpm install
pnpm dev # port 3001
pnpm test
pnpm typecheck
```
## Architecture Overview
- Next.js 14 App Router
- Xano backend (workspace 3, API group 1270)
- AlloyDB ScaNN for vector search
- Crayon SDK for UI components
## Code Patterns
### API calls
Always use `/api/<route>/route.ts` BFFs. Never call Xano directly from a component.
### When in doubt
Check `docs/architecture.md` and `docs/anything-engine.md` first.
## Git Workflow
- Always create PRs targeting `main` (M-Pederson/orbiter-sandbox is single-branch)
- Pre-commit runs Biome + typecheck
Two purposes: telling the agent the conventions, and telling future-you what you decided. Treat it as the canonical handbook.
The orbiter-sandbox already has one. Read it. Add to it as patterns crystallize.
Model Context Protocol servers expose your data to the CLI as tools. We use three:
| MCP | What it gives you | Auth |
|---|---|---|
orbiter-docs | mcp__orbiter-docs__search_orbiter_io_docs to grep Mark's Mintlify corpus from inside Claude Code | Already configured in Robert's setup |
xano-mcp | mcp__xano-mcp__execute / info / list_all_tools — full Xano admin surface (read endpoints, edit XanoScript, deploy) | XANO_META_TOKEN env var |
charlotte-mcp | Email + calendar + tasks — Mark probably won't need this one yet | OAuth, optional |
To add an MCP, edit ~/.claude/mcp.json. Robert will hand you a templated config when you're ready — that's its own skill (xano-mcp-workflow).
A ~/.claude/keybindings.json file lets you bind multi-key chords. The pros use them. Two we recommend:
{
"keybindings": [
{
"key": "cmd+shift+v",
"action": "toggle-verbose",
"description": "Toggle verbose mode (show all tool calls)"
},
{
"key": "cmd+shift+m",
"action": "next-model",
"description": "Cycle through opus → sonnet → haiku"
}
]
}
Run /keybindings-help if you want to see the full list of bindable actions.
Hooks are shell commands that run on lifecycle events: PostToolUse, Stop, UserPromptSubmit, etc. Use them to enforce invariants you don't want the agent to forget.
Examples we run:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd $CLAUDE_PROJECT_DIR && pnpm typecheck 2>&1 | tail -5"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "echo 'session ending — verify build before claiming done'"
}
]
}
]
}
}
The PostToolUse hook above means: every time the agent edits a file, run typecheck and feed the last 5 lines back to the agent. If TypeScript breaks, the agent sees it immediately and fixes before continuing. No more "I shipped it!" → "wait, it doesn't compile."
Don't go overboard. Each hook adds latency to every tool call. The two above are the highest-ROI starting set.
This section is more important than any of the setup. Tools are commodity. Workflow is leverage.
AskUserQuestion with 2–4 concrete named options. Never "what do you want?".Skipping step 2 is the #1 reason work goes sideways. The agent commits to an interpretation that's 80% right and you lose 20 minutes redirecting.
Robert's hard rule: always give multiple choice / interview-style options to push decisions forward fast.
# bad
What direction do you want me to take this in?
# good
[picker — Header: "Direction"]
- Use the existing classifier endpoint (less work, but less control)
- Add a new dispatcher (more flexible, ~30 min)
- Punt for now and unblock the demo
The picker is faster than typing a reply. It also forces the agent to commit to real options instead of vague gestures.
Don't waste the expensive brain on file listings, JSON parsing, retry loops, "is this URL 200?", "extract failing tests from this log."
If you have access to snappy-dispatch (Robert will share if/when relevant): one-line call, cheap model executes, you get the result back. Sub-penny per dispatch, 2–4 second latency.
For Mark today: just call out when a task is grunt work. The agent will switch to Haiku for that step automatically if you tell it to.
This rule is the difference between shipping and thinking you shipped.
Same-session toasts are not evidence. When the agent reports "done", check the diff yourself, run the actual flow yourself (or via agent-browser), and only then mark it done.
Two patterns:
run_in_background: true on Bash — for builds, dev servers, long-running watchers. The CLI gives you a task ID; you read output later. The agent doesn't sit and wait.
Sub-agents via the Task tool — spawn a fresh-context worker for a self-contained job (refactor, write a doc, run a multi-step search). The sub-agent has its own context window, returns one summary at the end.
Use sub-agents when:
Don't use sub-agents for tasks that need your taste, judgment, or knowledge of the conversation history. Those stay with the main agent.
Two options when Claude Code itself is unavailable:
snappy-shell — boots pi (a different agent runtime) with the full skill kernel loaded. Same skills, different brain. Cascade: OpenAI → Gemini Pro → Gemini Flash → Anthropic Sonnet, falling through on quota errors. Useful for "I want a second opinion" or "Anthropic is down."
claude-private — a zero-telemetry fork of Claude Code that talks to OpenRouter. Same UX, different auth. Useful when you want to route through OpenRouter for cost or compliance reasons.
Both come with skills. Robert will share the install path when you're ready.
This is Robert's house style: no agent reports "done" without a verification certificate. Same-session toasts don't count. The actor cannot be the auditor.
In practice: when the agent says "committed and pushed", the next thing it does is git log -1 and gh pr view <id> to prove it. If the proof step fails, the original claim was wrong.
You'll absorb this rhythm by example. The first few sessions you'll catch yourself trusting a toast. Stop, verify, and the muscle memory builds.
Robert's rule: if you remove code, remove it cleanly. No // removed for X reason, no rename-to-_var-because-unused, no shimmed re-exports. If it's gone, it's gone — the diff is the record.
# 0. Open a fresh terminal window
cd ~/Desktop/projects/orbiter-sandbox # or wherever you cloned it
# 1. Boot the dev server in background
pnpm install
claude # opens the CLI inside this repo
# 2. First prompt — let the agent read the lay of the land
> Read CLAUDE.md and docs/architecture.md, then tell me in 5 bullets what
> this repo does and where I'd start to add a new outcome class.
# 3. Watch what it does
# It should: read the two files, summarize, propose a starting point.
# Notice: it doesn't just answer, it cites file paths and line numbers.
# 4. Ask for a small change
> Edit prompts/find_investors/interview.md so the deck-or-describe
> opener says "Show me your deck OR describe the round in 2 sentences"
> instead of the current wording. Run pnpm sync-prompts and confirm.
# 5. Verify
> Open localhost:3001/chat, type "I want investors", screenshot the first
> assistant message, confirm the new wording is showing.
If you got through that without major friction, you're set up. If not, the troubleshooting section below covers the usual suspects.
Build a new prompt. Sync it. Test it. Commit it.
> Add a new prompt file at prompts/find_investors/synthesize-v2.md.
> It should be the same as synthesize.md but enforce a 90-word max on the
> WHY paragraph. Add a registry entry mapping it to a new var slot
> $synthesize_v2 in endpoint 8401. Sync. Test in /chat by running a real
> investor query and confirming the WHY paragraph fits the new limit.
> Commit on a new branch, push, open a PR.
The agent should:
synthesize.md, registry.json, the relevant Xano endpointAskUserQuestion)agent-browser, run a query, screenshot, OCR, count wordsgit push, gh pr create, paste the URLThat's the pro flow. Tooling does the manual labor. You stay in the judgment seat.
When a task gets big, spawn sub-agents. Try this:
> I want to add interview prompts for find_journalists, find_event_attendees,
> and find_warm_intros. They should follow the same shape as
> find_investors/interview.md (deck-or-describe → ready-flag → JSON contract).
>
> Spawn three Task sub-agents in parallel — one per class. Each writes its
> interview.md, adds it to the registry, runs sync, and reports back with
> a verification screenshot from /chat.
Three sub-agents work in parallel. You get three "done" reports back, each with a screenshot. Total wall time: ~the time of the slowest one, not 3× one.
If something's off, you redirect just that sub-agent — the others stay clean.
claude doesn't recognize my changes to ~/.claude/settings.json.
Restart the CLI. Settings are read at session start. There's no live-reload.
Permissions prompt for a command I thought I allowed.
Pattern matching is exact. Bash(git push:*) allows git push, git push origin, etc. — but not Bash(git push). Always use :* suffix.
Agent claims it ran a command but the output is empty.
Usually a long-running shell that timed out at 2 minutes. Re-run with run_in_background: true. The agent should know this — if it doesn't, point it at this section.
agent-browser saved-state expired (Google OAuth re-prompt).
Re-run the full login flow, save state again:
agent-browser --headed open http://localhost:3001
# log in manually
agent-browser state save ~/.claude/workos-auth-state.json
The agent is going off on a tangent.
Interrupt. Two ways:
Esc once — stops the current generation, lets you redirectEsc Esc (double) — clears the current turn entirelyUse the picker to push it back on track: "Pick one of: A, B, C."
The agent is being verbose.
You can correct it inline ("just give me the answer, no recap"). Or save it to memory: /remember the user wants terse responses, no trailing summaries.
TypeScript compiles but the build broke.
The PostToolUse hook is too narrow. Add pnpm build to the matcher list, or add a separate hook for Edit|Write that runs full pnpm check.
Hook is adding 30 seconds to every edit.
Hooks run synchronously. If the typecheck takes 30s, that's 30s per edit. Either narrow the matcher (e.g., only .ts/.tsx paths) or move the heavy check to a Stop hook that runs once at session end.
Once you're set up:
mark-onboarding skill we ship next to this doc — read it, edit it, sync it.~/.claude/skills/. When you find yourself explaining the same context twice, that's a skill waiting to be written.We'll layer on the more advanced stuff (cross-machine ops, autonomous loops, ralph experiments, dispatch swarms) as you ask for it. Day 1 is just: install, settings, terminal, browser, skills, MCP. The rest compounds.
~/.claude/settings.json<repo>/.claude/settings.json or ~/.claude/settings.local.json~/.claude/skills/<name>/SKILL.md~/.claude/mcp.json~/.claude/statusline.sh~/.claude/keybindings.jsonhooks field in settings.json<repo>/CLAUDE.mdEach of those files is plain text. Edit, save, restart the CLI, you're live.