CI status

If Claude Code is where you work, cpb is how you run more than one of it.

One ~/.claude holds one setup: one set of settings and hooks, one memory, one login, one environment, all with full access to your machine. cpb gives you as many Claude Codes as you need, each behind its own command, each isolated as far as you choose, and each described in a file you can apply anywhere.

quick start
$ cpb CREATE PLAYBOOK work                           # a playbook, and a `work` command that opens it
$ cpb CREATE PLAYBOOK side ISOLATED LOGIN            # a second account, running beside the first
$ cpb CREATE PLAYBOOK sre SANDBOX                    # every launch inside a microVM
$ work                                               # Claude Code, bound to that playbook
verified against v3.27.0, testbed, throwaway HOME

Four layers

Isolation, as far as you choose

Each playbook gets its own config, and can go further: its own environment, its own login, or its own process boundary. You decide per playbook.

Config home

Its own CLAUDE.md, settings.json, hooks, memory, history, sessions, plugins, MCP servers and skills. Your ~/.claude never moves.

every playbook, always

Environment

Its own variables, set or blocked at launch: another model backend, another token, another proxy. Your shell stays as it is.

USE ENV · SET VAR · BLOCK VAR

Login

Its own Anthropic account, sharing nothing with ~/.claude.

ISOLATED LOGIN

Process

A microVM with its own kernel, filesystem and network. It sees only your working directory and its own config; your machine's login never enters it. By default a host-side proxy injects backend API keys, so the sandbox never holds them.

SANDBOX · --sandbox

Try it

Try things without touching your daily setup

A scratch playbook, used and dropped. ~/.claude is never touched.

real run, v3.27.0
$ cpb CREATE PLAYBOOK scratch
Created playbook "scratch" at /home/you/.claude-playbooks/scratch
Command:  scratch  (launcher at /home/you/bin/scratch)

Run it now:
  'scratch'
$ cpb SHOW PLAYBOOK scratch
Name:           scratch
Version:        (none)
Path:           /home/you/.claude-playbooks/scratch
Source:         (none)
Launcher:       (none)
Env sets:       (none)
Variables:      (none)
Sandbox:        no
Pilot profile:  imported (CLAUDE.md imports ~/.pilot-profile/)
$ cpb DROP PLAYBOOK scratch --yes
Removed command "scratch"
Deleted playbook "scratch".
verified against v3.27.0, testbed, throwaway HOME

A throwaway session in a throwaway folder works the same way: cpb start /tmp/x --delete.

Environment & models

Route one playbook to another model

An env set is a named group of variables, attached to one playbook or to all of them (DEFAULTS). EXPLAIN shows what a launch sets and which layer set it — and a custom /model list travels with the playbook too.

real run, v3.27.0
$ cpb CREATE ENV router SET ANTHROPIC_BASE_URL=http://localhost:4000/v1
Created ENV router
  set       ANTHROPIC_BASE_URL
$ cpb CREATE PLAYBOOK glm NO PILOT PROFILE ISOLATED LOGIN
Created playbook "glm" at /home/you/.claude-playbooks/glm
Login isolated: it shares no login with ~/.claude; run /login once in it.
$ cpb ALTER PLAYBOOK glm USE ENV router SET VAR ANTHROPIC_MODEL=glm-5.3
Altered PLAYBOOK glm
  env sets  router
  set       ANTHROPIC_MODEL
$ cpb ALTER PLAYBOOK glm BLOCK VAR ANTHROPIC_API_KEY
Altered PLAYBOOK glm
  blocked   ANTHROPIC_API_KEY
$ cpb ALTER PLAYBOOK glm ADD MODEL 'glm-5.3' LABEL 'GLM 5.3' DESCRIPTION 'via the router'
Altered PLAYBOOK glm
  model     glm-5.3
$ cpb ALTER PLAYBOOK glm ADD MODEL 'glm-5.3-flash' LABEL 'GLM 5.3 Flash' BEHAVES AS 'claude-sonnet-5'
Altered PLAYBOOK glm
  model     glm-5.3-flash
$ cpb ALTER PLAYBOOK glm SET MODEL PICKER ONLY
Altered PLAYBOOK glm
  picker    only
$ cpb EXPLAIN PLAYBOOK glm
VARIABLE            VALUE                     FROM
--------            -----                     ----
ANTHROPIC_API_KEY   (blocked)                 PLAYBOOK glm
ANTHROPIC_BASE_URL  http://localhost:4000/v1  ENV router
ANTHROPIC_MODEL     glm-5.3                   PLAYBOOK glm

Secret helper: (none)
Plugins: (none)
Agent: (none)
Model: glm-5.3 (ANTHROPIC_MODEL); a launch's --model and /model still win
Model picker: only: glm-5.3 (GLM 5.3), glm-5.3-flash (GLM 5.3 Flash)
Login: isolated: no link to ~/.claude's login and no machine token; /login once in it
verified against v3.27.0, testbed, throwaway HOME

ISOLATED LOGIN keeps your Anthropic login away from that provider, and NO PILOT PROFILE keeps a local profile import (such as ~/.pilot-profile/) out of the playbook's CLAUDE.md — cpb warns once if a playbook that already imports one is later routed to a non-Anthropic host.

Process isolation

Let an agent loose without letting it near your machine

With --sandbox, Claude Code runs in a microVM that sees your working directory and the playbook's own directory — not your home, ~/.claude, your shell's environment, or your other playbooks.

sbx (Docker Sandboxes)
$ cpb run --sandbox work                                         # this folder is the workdir
$ cpb run --sandbox --sandbox-fresh --clone --workdir ~/untrusted-repo work   # a private clone; your tree is untouched
$ cpb run --sandbox --mount ~/shared-libs:ro work                # one more directory, read-only
$ cpb run --sandbox-host me@buildbox work                        # the same launch, sandboxed on another machine

By default your backend API keys stay on the host: the sandbox sees a placeholder that a host-side proxy swaps for the real key, for that endpoint only. If a key can't be registered with the proxy, the launch refuses rather than fall back to sending it in plain (v3.26.0); [sandbox] secrets = "env" is the only way to pass one in as a variable instead. A SANDBOX playbook is sandboxed on every launch.

On Linux with Docker, --sandbox=openshell runs the session in NVIDIA OpenShell instead of sbx — experimental, Linux with Docker only, opt-in (v3.27.0).

The DDL grammar

Put the whole setup in a file

Every statement above can live in a .cpb file: a recipe, since its ALTER PLAYBOOK names no playbook. APPLY checks all of it before writing anything, --dry-run shows every change, and applying twice changes nothing.

reviewer.cpb
-- reviewer.cpb: a recipe (it names no playbook)
ALTER PLAYBOOK
  ADD MCP SERVER files COMMAND 'npx' ARGS '-y' '@modelcontextprotocol/server-filesystem' '/srv/notes'
  ALLOW TOOL 'Bash(gh pr *)'  DENY TOOL 'Bash(git push *)'
  ADD SKILL review FROM '/home/you/skills-src/review'
  SET MODEL 'claude-opus-5-5';
real run, v3.27.0
$ cpb APPLY reviewer.cpb TO reviewer --dry-run
reviewer.cpb:2       created   CREATE PLAYBOOK reviewer
reviewer.cpb:2       changed   ALTER PLAYBOOK reviewer  (would run: claude mcp add-json files --scope user; link skills/review to /home/you/skills-src/review)
Would apply reviewer.cpb: 1 created, 1 changed, 0 unchanged, 0 dropped
$ cpb APPLY reviewer.cpb TO reviewer
Applied reviewer.cpb: 1 created, 1 changed, 0 unchanged, 0 dropped
$ cpb SHOW CREATE PLAYBOOK reviewer --skip-secrets
CREATE PLAYBOOK IF NOT EXISTS reviewer;

ALTER PLAYBOOK reviewer
  ALLOW TOOL 'Bash(gh pr *)'
  DENY TOOL 'Bash(git push *)'
  SET MODEL 'claude-opus-5-5';

ALTER PLAYBOOK reviewer
  ADD MCP SERVER files COMMAND 'npx' ARGS '-y' '@modelcontextprotocol/server-filesystem' '/srv/notes'
  ADD SKILL review FROM '/home/you/skills-src/review';
verified against v3.27.0, testbed, throwaway HOME

Secrets are references, resolved at launch by your helper; SHOW CREATE never prints a value. A recipe applies to any playbook or to ~/.claude itself. cpb SHOW CREATE ALL > playbook.cpb exports the whole machine; cpb APPLY playbook.cpb rebuilds it on the next one.

Configuration

MCP servers, with secrets by reference

ADD MCP SERVER builds the config and runs Claude Code's own claude mcp add-json for the playbook. A credential must come FROM '<ref>': Claude's config gets only a placeholder, and a configured secret helper resolves it into the session at launch.

real run, v3.27.0
$ cpb ALTER PLAYBOOK researcher ADD MCP SERVER sentry URL 'https://mcp.sentry.dev/mcp' HEADER 'Authorization' FROM 'keychain:pilot/sentry-auth'
Altered PLAYBOOK researcher
  MCP server sentry
$ cpb EXPLAIN PLAYBOOK researcher
VARIABLE                                 VALUE                              FROM
--------                                 -----                              ----
CPB_MCP_SENTRY_H_AUTHORIZATION_32770652  <from keychain:pilot/sentry-auth>  PLAYBOOK researcher

Secret helper: (none)
Plugins: (none)
Agent: (none)
MCP servers: files, sentry
$ cpb SHOW CREATE PLAYBOOK researcher --skip-secrets
CREATE PLAYBOOK IF NOT EXISTS researcher
  NO ALIAS;

ALTER PLAYBOOK researcher
  ADD MCP SERVER files COMMAND 'npx' ARGS '-y' '@modelcontextprotocol/server-filesystem' '/srv/notes'
  ADD MCP SERVER sentry URL 'https://mcp.sentry.dev/mcp' HEADER 'Authorization' FROM 'keychain:pilot/sentry-auth';
verified against v3.27.0, testbed, throwaway HOME

The value never appears in a file, in argv, or in any SHOW/EXPLAIN output. Claude's own config gets only ${CPB_MCP_SENTRY_H_AUTHORIZATION_…}, which the secret helper expands at launch.

Sessions & SQL

See everything that is running

cpb sessions lists every live Claude Code session, in every playbook. SELECT queries the same state as tables — built in for plain column lists, or through clickhouse local for WHERE, ORDER BY and functions.

real run, v3.27.0, with example 18's stand-in session
$ cpb sessions
PLAYBOOK  PID     TTY  KIND         STATUS  AGE  ACTIVE  MODEL            SESSION                               CWD
--------  ---     ---  ----         ------  ---  ------  -----            -------                               ---
worker    246892  -    interactive  idle    0s   0s      claude-opus-5-5  0e5c1a2b-0000-4000-8000-000000000001  /home/you/project
$ cpb "SELECT name, envs, sandbox FROM PLAYBOOKS"
NAME        ENVS    SANDBOX
----        ----    -------
glm         router  false
researcher  -       false
reviewer    -       false
side        -       false
sre         -       true
work        -       false
$ cpb "SELECT playbook, key FROM VARS WHERE effective ORDER BY playbook, key"
PLAYBOOK    KEY
--------    ---
glm         ANTHROPIC_API_KEY
glm         ANTHROPIC_BASE_URL
glm         ANTHROPIC_MODEL
researcher  CPB_MCP_SENTRY_H_AUTHORIZATION_32770652
verified against v3.27.0, testbed, throwaway HOME (SELECT …WHERE via clickhouse local)

cpb RESUME starts this folder's latest session, in its own playbook, and never resumes one that's already live — two processes on one session id would corrupt it.

cpb tui (v3.25.0)

Browse playbooks, sessions and env sets on one screen

A read-only terminal UI over the grammar: every screen names the cpb … --json statement it reads, on its last line. No screen ever holds a secret value. The blocks below are the real screens, from cpb's own test goldens.

cpb tui — Playbooks (80×24)
 cpb  [1 Playbooks]  2 Sessions   3 Env sets   4 Defaults   5 Log        ? help
────────────────────────────────────────────────────────────────────────────────
  NAME           LAUNCHER  ENV SETS  LOGIN     SESSIONS  MODEL
▸ kommander-dev  kd        -         shared    1         claude-opus-5-5
  router         k9        9router   isolated  1         glm-5.3

────────────────────────────────────────────────────────────────────────────────
 2 playbooks · 2 live sessions · read 0s ago
 enter open  s sessions  c SHOW CREATE  e export  y copy  / filter  q quit
reads: cpb SHOW PLAYBOOKS --json · cpb SHOW SESSIONS --json
cpb tui — a playbook's Overview (80×24)
 cpb   1 Playbooks   2 Sessions   3 Env sets   4 Defaults   5 Log        ? help
────────────────────────────────────────────────────────────────────────────────
 router  (launcher k9 · ~/.claude-playbooks/router)
 [Overview] Env  Vars  Plugins  MCP  Skills  Status line  Model  Sessions

  Version         -
  Source          (none)
  Login           isolated
  Pilot profile   not imported
  Env sets        9router
  Tools           -
  Live sessions   1 · newest 2d ago in ~/DEV/claude-playbooks

────────────────────────────────────────────────────────────────────────────────
 2 playbooks · 2 live sessions · read 0s ago
 ←→/1-9 tab  c SHOW CREATE  e export .cpb  y copy  r refresh  esc back  q quit
reads: cpb SHOW PLAYBOOK router --json · cpb SHOW SESSIONS --json

cpb tui in a terminal opens it; a script reads the same state with the statement named on the screen's last line.

Built to be relied on

Stable since v3.24.0

Breaking changes wait for a major version. Every change is tested the same way, on the exact commit a release tags.

20 examples applied in CI on every change — dry run, apply, re-apply (no-op), then each README's own checks
upgrade-tested the previous release's binary applies its own examples; this build takes over the same state and must read it identically
arena regression a full gentar run on the exact commit each release is tagged from

CI status

Get it

Install

install
$ curl -fsSL https://raw.githubusercontent.com/ramazanpolat/claude-playbooks/main/install.sh | sh
$ cpb --version
claude-playbook version v3.27.0
verified against v3.27.0, testbed

Linux and macOS, amd64/arm64 (no native Windows — WSL works). devbox, Nix, npx and source builds →