pi

pi-cc-plugin

OtherClaude Codeby RazvanRotaru

Summary

Slash commands that delegate to pi-subagents: /pi:agent, /pi:status, /pi:result, /pi:cancel, /pi:setup.

Install to Claude Code

/plugin install pi@pi-cc-plugin

Run in Claude Code. Add the marketplace first with /plugin marketplace add RazvanRotaru/pi-cc-plugin if you haven't already.

README.md

pi-cc-plugin

> Run subagents on any model from inside Claude Code.

pi-cc-plugin is a Claude Code plugin that delegates work to pi-subagents (nicobailon's fork of pi-coding-agent). Pi already speaks Claude, OpenAI, Google, OpenRouter, DeepSeek, Groq, xAI, Mistral, Cerebras, Hugging Face, Bedrock, and friends — this plugin gives Claude Code a clean slash-command surface to dispatch into that model zoo, while keeping the orchestrator session itself snappy and non-blocking.

┌─ Claude Code (orchestrator, Claude) ──────────────────────────────┐
│                                                                    │
│  /pi:agent worker "fix the auth bug" --model openrouter/...        │
│       │                                                            │
│       ▼                                                            │
│   pi-broker  ──►  pi --mode rpc  ──►  pi-subagents  ──►  child pi  │
│       │                                    │                │      │
│       ▼                                    ▼                ▼      │
│   .pi-cc-plugin/state.json    /tmp/.../async-subagent-runs/<id>/   │
│   (job ledger)                  status.json + events.jsonl + log   │
│                                                                    │
│       ▲                                    │                       │
│       └────  /pi:status reads both ◄───────┘                       │
│              (events stream forwarded raw to the orchestrator)     │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘
  • Foreground by default. /pi:agent waits for the subagent to finish

and prints its output, just like any normal tool call. Add --bg for long jobs you want to fire and forget — and pick up later via

/pi:status and /pi:result.

  • Live progress with --verbose. Stream step transitions to the

terminal as they happen. Off by default to keep the orchestrator's context tight.

  • Heterogeneous models. Use --model openrouter/google/gemini-3-pro-preview,

--model anthropic/claude-opus-4-7, anything pi knows about.

  • Worktree isolation. --worktree runs the subagent in its own

git worktree for safe edits when several dispatches share a repo.

  • MCP tools per agent or per dispatch. Static via the agent file,

dynamic via --mcp foo/bar.

  • Auto-reconcile. state.json syncs against pi's view on every

read — finished jobs always show as completed.

  • Raw event stream in /pi:status. Pi-subagents' events.jsonl

is forwarded verbatim — step transitions, child tool calls, errors — so the orchestrator sees what's actually happening, not just running.

---

Quick start

1. Install pi (Node ≥ 20)

# Need Node 20+ in the shell. nvm is the easiest path:
nvm install --lts && nvm use --lts

# Pi itself:
npm i -g @mariozechner/pi-coding-agent

# pi-subagents (the extension that gives pi the `subagent` tool):
pi install npm:pi-subagents

2. Configure a provider

OpenRouter is the lowest-friction path (one key → all models):

export OPENROUTER_API_KEY=sk-or-...
echo 'export OPENROUTER_API_KEY=sk-or-...' >> ~/.bashrc

Or, for OAuth providers (Claude Pro/Max, ChatGPT Plus, Gemini CLI):

pi    # then /login → pick a provider

See Provider matrix below for direct API-key env vars.

3. Install this plugin

/plugin marketplace add /path/to/pi-cc-plugin
/plugin install pi@pi-cc-plugin
/reload-plugins

4. Wire up your project

/pi:setup --yes

That checks pi/pi-subagents are installed, validates auth, copies six default specialist seeds into .pi/agents/, and gitignores

.pi-cc-plugin/.

5. First run

/pi:agent scout "how many .mjs files are in this repo?"

The broker waits for scout to finish and prints its output. For long jobs you'd rather background, add --bg:

/pi:agent worker "rewrite the auth module" --bg

Output (within ~1s):

Started job-001 (pi-run-id 0f214ec0-…) — agent=worker
Use /pi:status job-001 to inspect.

Then /pi:status job-001 shows live progress; /pi:result job-001 prints the final output once worker is done.

---

Slash command reference

| Command | What it does | |---|---| | /pi:setup [--yes] | Verify pi + pi-subagents + provider auth, scaffold .pi/agents/, gitignore .pi-cc-plugin/. Idempotent. | | /pi:agent <agent> <task…> [flags] | Dispatch one task to one pi agent. Need to fan out? Call /pi:agent multiple times in one assistant turn — Claude Code runs tool calls in parallel. | | /pi:status [id] | Without id: list every tracked job. With id: inspect one. Auto-reconciles state.json against pi and forwards the raw event stream (subagent.run.started, subagent.step.started, child tool calls, subagent.run.completed, …) so the orchestrator sees more than just running. | | /pi:result <id> | Print the final markdown output. | | /pi:cancel <id> | SIGTERM pi-subagents' parent + every detached worker carrying the runId; SIGKILL after 5s. |

Flags

| Flag | Where | Effect | |---|---|---| | --wait (default) | run | Wait for the subagent to finish; print its output when done. Redundant — same as omitting both flags. | | --bg | run | Detach. Returns the run id immediately; pick up via /pi:status and /pi:result. Use this for long runs or when you want to keep the orchestrator session free. In Claude Code you can also Ctrl+B Ctrl+B a running /pi:agent to background it manually. | | --verbose | run | While waiting in foreground, stream step-transition lines (· agent: running· agent: complete) to stdout. Off by default to keep the parent agent's context small. | | --model <id> | run | Override the agent's default model (e.g. openrouter/google/gemini-3-pro-preview). Always use provider/model form to avoid registry resolution surprises. | | --fork | run | Run the subagent in a forked context (pi-subagents context: "fork"). | | --worktree | run | Run the subagent in an isolated git worktree. Requires a clean working tree. Dispatches via the tool-call path (adds ~3-5s of latency). | | --mcp <list> | run | Attach MCP tools to this dispatch (comma-separated server/tool). See MCP tools. | | --cwd <path> | run | Run pi in a different working directory. | | --yes | setup | Auto-apply scaffold/gitignore fixes. |

Identifiers

Every job has two IDs:

  • internal_id — short, stable: job-001, job-002. For humans.
  • pi run id — the long uuid pi gives back. Canonical.

/pi:status, /pi:result, /pi:cancel accept either, plus any unambiguous prefix of the pi run id.

What /pi:status shows you

Two files on disk back every status query, and both get reflected in the output:

                    ┌──────────────── /pi:status job-001 ────────────────┐
                    │                                                     │
       state.json   │   .pi-cc-plugin/state.json   ← broker ledger        │
       per-job ─────┤        (id, agent, task, started, completed)        │
                    │                                                     │
                    │   /tmp/pi-subagents-uid-<uid>/async-subagent-runs/  │
       pi-subagents │     <runId>/                                        │
       per-run ─────┤        ├─ status.json     ← per-step state          │
                    │        └─ events.jsonl    ← raw event stream        │
                    │                                                     │
                    └─────────────────────────────────────────────────────┘

Sample output (single-job inspect):

**job-001** · single · running
  pi-run-id: `8f3a2c1b-9e7d-4a6b-8c1d-2e3f4a5b6c7d`
  agents: worker
  task: summarize the auth module
  started: 2026-04-29T19:00:12.503Z
  steps:
    - worker: running
  events:
    {"type":"subagent.run.started","ts":1777834812,"runId":"8f3a..."}
    {"type":"subagent.step.started","ts":1777834813,"runId":"8f3a...","stepIndex":0,"agent":"worker"}
    {"type":"tool_call","name":"read","args":{"path":"src/auth.ts"},"subagentSource":"child","subagentAgent":"worker"}
    {"type":"tool_call","name":"grep","args":{"pattern":"export"},"subagentSource":"child","subagentAgent":"worker"}

The events block is raw pass-through — no summarization, no filtering. The orchestrator (Claude) parses it and decides what to surface to you. Common event types pi-subagents emits:

| Type | Source | Carries | |---|---|---| | subagent.run.started / .completed | pi-subagents | runId, success | | subagent.step.started | pi-subagents | stepIndex, agent | | subagent.parallel.started / .completed | pi-subagents | step group + agent list | | subagent.control | pi-subagents | steering / cancel notices | | tool_call, message_*, usage, … | child pi (re-emitted) | tagged subagentSource: "child" | | subagent.child.stdout / .stderr | child pi (raw lines) | unstructured fallback |

---

Agent configuration

Agents live in .pi/agents/<name>.md (project) or

~/.pi/agent/agents/<name>.md (user). Project wins. Pi-subagents also ships a built-in set (scout, worker, planner, reviewer,

context-builder, researcher, delegate, oracle,

oracle-executor) that any user/project agent can override by name.

Frontmatter shape

---
name: scout                                    # required, must match filename
description: Fast codebase recon              # required
model: openrouter/moonshotai/kimi-k2.6        # any pi-known model
fallbackModels: anthropic/claude-haiku-4-5    # optional ordered fallbacks
thinking: medium                               # off | minimal | low | medium | high | xhigh
tools: read, bash, grep, find                  # builtin tool allowlist + mcp:* entries
skills: clean-code                             # comma-separated, injected into prompt
maxSubagentDepth: 1                            # how many layers of nested delegation
inheritProjectContext: true                    # keep AGENTS.md/CLAUDE.md in the prompt
inheritSkills: false                           # surface pi's full skills catalog?
systemPromptMode: replace                      # replace | append (vs pi's base prompt)
output: context.md                             # default file the agent writes
defaultReads: brief.md                         # files the agent reads on start
---

You are a scout. Your job is …

Attaching MCP tools (the most common ask)

Pi-subagents reads mcp:server/tool entries from the same tools: line and forwards them to the child pi via MCP_DIRECT_TOOLS. So the canonical place to wire MCP tools is the agent's tools: line:

---
name: implementer
description: Write production code that makes the test suite green
model: openrouter/anthropic/claude-sonnet-4-7
tools: read, write, edit, bash, grep, find,
  mcp:filesystem/read_file, mcp:filesystem/write_file,
  mcp:test-runner/run_suite, mcp:linter/check
skills: clean-code
---

Any /pi:agent implementer … then has those MCP tools available without extra flags.

If you want a per-dispatch MCP override (one-off experimentation, or attaching tools to a builtin like scout without committing changes to your seed), use the --mcp flag. The broker writes a temporary agent file with the extra tools, dispatches under that name, and cleans up after asyncId capture:

/pi:agent scout "find auth bugs" --mcp memory/store,memory/read --bg

Source resolution order for --mcp: project seed (.pi/agents/) → user seed (~/.pi/agent/agents/) → pi-subagents builtin. Settings overrides (.pi/settings.json#subagents.agentOverrides) are merged into the temp file, so model/thinking/skills overrides keep applying.

Scaffolding

/pi:setup --yes copies six seeds into .pi/agents/:

| Seed | Role | |---|---| | architect | Shape contracts, surface integration points, write a brief | | test-writer | Author the failing test suite that pins desired behavior | | test-reviewer | Adversarial review of the test suite | | implementer | Make the suite green | | code-reviewer | Adversarial review of the implementer's diff | | ci-triage | Bisect a CI failure; classify flake/regression/infra |

Edit them freely — the plugin never overwrites once they exist.

---

Recipes

Cheap recon → expensive deep dive

Two /pi:agent calls. The orchestrator (Claude) reads scout's result, decides whether to keep going, then dispatches the deeper agent with that context embedded in the brief:

/pi:agent scout "map the affected modules"
# (foreground: scout's output is printed; orchestrator reads it)
/pi:agent oracle "Given scout's summary above, critique the plan and surface risks"

This replaces the old /pi:chain form. Sequential agents almost always benefit from the orchestrator validating the previous step's output before deciding what to do next.

Fan out with isolation

Multiple /pi:agent --bg calls from the same assistant turn — Claude Code runs tool calls in parallel, and --bg lets all three start without any one of them blocking the others:

/pi:agent scout "audit frontend" --worktree --bg
/pi:agent scout "audit backend"  --worktree --bg
/pi:agent scout "audit infra"    --worktree --bg

Each scout gets its own worktree under /tmp/pi-worktree-<runId>-<n>, torn down at run end. Requires a clean git working tree. Pick the results up later via /pi:result job-001, /pi:result job-002, etc.

Lightweight test harness

/pi:agent test-writer "pin the contract for src/auth.ts"
# orchestrator reads the test-writer brief from stdout, then:
/pi:agent implementer "make the suite at tests/auth.test.mjs green"
# review the diff, then:
/pi:agent code-reviewer "adversarial review of the implementer diff"

Foreground by default keeps the orchestrator in the loop between steps.

Attach MCP tools just for this dispatch

/pi:agent worker "implement the planned migration using the test-runner MCP" --mcp test-runner/run_suite,test-runner/diff

---

How it works

Dispatch (/pi:agent)

┌─ Claude Code session ────────────────────────────────────────────┐
│                                                                   │
│  /pi:agent worker "task"                                          │
│       │                                                           │
│       ▼                                                           │
│  plugins/pi/scripts/pi-broker.mjs run worker "task" [--bg|--wait] │
│       │                                                           │
│       ▼                                                           │
│  args.mjs (parse) → pi-cli.mjs (spawn pi --mode rpc) ──┐          │
│                                                         │          │
│  ┌──────────────────────────────────────────────────────┴───┐      │
│  │ pi (Node ≥20) loads pi-subagents extension              │      │
│  │   handles /run slash command                            │      │
│  │   OR direct subagent tool_call (--worktree path)        │      │
│  │   spawns child pi → writes status.json + events.jsonl   │      │
│  │   emits subagent-slash-result {asyncId, asyncDir}       │      │
│  └──────────────────────────────────────────────────────┬───┘      │
│                                                         │          │
│       ◀─ broker captures asyncId, closes stdin ──────── ┘          │
│       │                                                           │
│       ▼                                                           │
│  state.json patch (job-NNN ↔ runId ↔ asyncDir)                    │
│       │                                                           │
│       ├──── --wait ────► poll status.json until terminal,         │
│       │                  print final output, return               │
│       └──── --bg   ────► return immediately with job id           │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

Status / result (/pi:status, /pi:result)

┌─ Claude Code session ─────────────────────────────────────────┐
│                                                                │
│  /pi:status [job-id]                                           │
│       │                                                        │
│       ▼                                                        │
│  pi-broker.mjs status [id]                                     │
│       │                                                        │
│       │   reconcile.mjs                                        │
│       │      ├── read .pi-cc-plugin/state.json                 │
│       │      └── read /tmp/.../async-subagent-runs/<id>/       │
│       │           ├── status.json    → state, steps, errors    │
│       │           └── events.jsonl   → raw event stream        │
│       ▼                                                        │
│  render.mjs → markdown block per job                           │
│       │      header · pi-run-id · steps · errors · events:     │
│       ▼                                                        │
│  Claude reads, decides what to surface                         │
│                                                                │
└────────────────────────────────────────────────────────────────┘

State on disk

| Path | Purpose | Owner | |---|---|---| | ./.pi-cc-plugin/state.json | Broker's job ledger: internal_id, pi-run-id, agents, task, status, pi_status_dir | pi-cc-plugin | | /tmp/pi-subagents-uid-<uid>/async-subagent-runs/<runId>/status.json | Per-run state: state, per-step status / model / error, timing | pi-subagents | | /tmp/pi-subagents-uid-<uid>/async-subagent-runs/<runId>/events.jsonl | One JSON event per line: subagent.* lifecycle events + child pi tool calls (re-emitted with subagentSource: "child") | pi-subagents | | /tmp/pi-subagents-uid-<uid>/async-subagent-runs/<runId>/output-N.log | Per-step stdout (concatenated for /pi:result) | pi-subagents | | /tmp/pi-subagents-uid-<uid>/async-subagent-runs/<runId>/subagent-log-<runId>.md | Human-readable transcript | pi-subagents | | ~/.pi/agent/auth.json | Provider credentials (0600) | pi | | ~/.pi/agent/extensions/, <npm-global>/pi-subagents/ | Extension code | pi |

The plugin never writes pi's state; it only reads. The broker's

state.json auto-reconciles with pi's status.json on every

/pi:status and /pi:result. events.jsonl is read in full each call (readPiEvents accepts an optional byte cursor for incremental tails — currently unused since /pi:status is happy to dump the full stream and let the orchestrator interpret).

Provider matrix

Pi recognizes these env vars in addition to OAuth and auth.json:

| Provider | Env var | |---|---| | Anthropic | ANTHROPIC_API_KEY | | OpenAI | OPENAI_API_KEY | | OpenRouter | OPENROUTER_API_KEY | | Google Gemini | GEMINI_API_KEY | | DeepSeek | DEEPSEEK_API_KEY | | Groq | GROQ_API_KEY | | xAI | XAI_API_KEY | | Mistral | MISTRAL_API_KEY | | Cerebras | CEREBRAS_API_KEY | | AWS Bedrock | (uses standard AWS creds) | | Vercel AI Gateway | AI_GATEWAY_API_KEY | | Hugging Face | HF_TOKEN |

Full list lives in pi's docs/providers.md.

Listing model prices

This plugin ships a companion /pi-prices slash command (and a

pi-prices shell binary at ~/.local/bin/) that dumps pi's bundled pricing table — no network calls:

pi-prices                    # all models
pi-prices openrouter         # filter by substring
pi-prices --json             # machine-readable

---

Troubleshooting

| Symptom | Cause | Fix | |---|---|---| | pi-broker: pi requires Node >= 20 | System node is 18 (Claude Code's subprocess env) and no nvm Node 20+ found | nvm install --lts && nvm use --lts, restart Claude Code | | pi-broker: pi exited (code N) before emitting subagent-slash-result | pi crashed at startup; usually missing extension or auth | Run /pi:setup --yes; check pi list \| grep pi-subagents; verify auth | | pi-broker: timed out after Nms waiting for subagent-slash-result | Pi is loading slowly (first run) or stuck on auth/rate-limit | Bump PI_BROKER_DISPATCH_TIMEOUT_MS=120000, or attach a debugger to pi | | step (error) in /pi:status despite state: complete | Pi-subagents marks the run complete even when a step's API call fails (e.g. invalid model id, missing auth) | Read step-errors: block; the broker auto-suggests a hint | | Unknown agent: <name> | Project seed missing a name: line, or wrong path | Project seeds must be at .pi/agents/<name>.md with name: <name> in frontmatter | | worktree isolation requires a clean git working tree | Local tree dirty | git stash or commit, retry | | --mcp couldn't find a source for agent "<name>" | No project, user, or builtin source for that agent | Scaffold one (/pi:setup) or pick a different agent |

---

Configuration knobs (env vars)

| Var | Default | Purpose | |---|---|---| | PI_BROKER_PI_BIN | (auto-resolved) | Override pi launcher (script or binary) | | PI_BROKER_PI_ARGS | (none) | Comma-separated args prepended after PI_BROKER_PI_BIN | | PI_BROKER_DISPATCH_TIMEOUT_MS | 60000 | How long to wait for pi to ack the slash command | | PI_BROKER_POLL_INTERVAL_MS | 1500 | Foreground poll interval against pi-subagents' status.json | | PI_BROKER_SIGTERM_GRACE_MS | 5000 | Cancel grace before SIGKILL | | PI_BROKER_NO_NODE_VERSION_CHECK | 0 | Skip the Node-≥20 fail-fast (for fixtures) | | PI_BROKER_DEBUG | 0 | Print stack traces on broker errors |

These are subprocess vars — set them where you launch Claude Code.

---

Development

npm install
npm test                            # 129+ offline tests against the fake-pi fixture
npm run lint                        # biome
PI_SMOKE=1 npm run test:smoke       # only when a real pi is installed

Tests use a fake-pi fixture (tests/fixtures/fake-pi.mjs) that mimics pi's RPC protocol — JSONL prompts on stdin, subagent-slash-result custom messages, status.json shape, the works. No real pi needed for the offline suite. Real-pi smoke tests live in docs/DOGFOOD.md and are runnable manually.

Code layout:

plugins/pi/
  scripts/
    pi-broker.mjs                  # entry point invoked by every slash command
    lib/
      args.mjs                     # parse the slash grammar
      pi-spawn.mjs                 # locate pi (npm-global, nvm, /usr/local) + pick Node ≥20
      pi-cli.mjs                   # JSON-RPC client; slash and tool_call dispatch paths
      pi-status-reader.mjs         # read pi's status.json / output-N.log
      reconcile.mjs                # sync state.json from pi on every read
      ephemeral-agents.mjs         # write per-dispatch agent files for --mcp
      process-tree.mjs             # find + kill detached subagent workers
      tracked-jobs.mjs             # state.json CRUD
      state.mjs                    # atomic JSON I/O with mkdir-based locking
      gitignore.mjs                # ensure .pi-cc-plugin/ is ignored
      render.mjs                   # markdown for /pi:status and /pi:result
      setup-checks.mjs             # /pi:setup checks
      actions/{run,status,result,cancel,setup}.mjs
  agents-seed/                     # six default specialists
  commands/                        # one .md per slash command
  skills/{pi-cc-usage,pi-cc-harness}/SKILL.md
docs/
  PI_INVOCATION.md                 # verified contract with real pi
  DOGFOOD.md                       # manual QA script

Run /pi:setup --yes against a freshly-cloned repo to confirm everything works end-to-end before dogfooding model dispatches.

---

Status

Stable — broker dispatch (/pi:agent) with foreground polling and optional --verbose step streaming, --bg for detached runs, status, result, cancel, worktree (via tool_call path), --mcp, auto-reconcile,

raw events.jsonl forwarding through /pi:status so the orchestrator gets per-step transitions and child tool calls (not just

running).

Removed/pi:chain and /pi:parallel. Use multiple /pi:agent calls from the orchestrator instead: parallel falls out naturally (Claude Code runs tool calls concurrently), and sequencing benefits from the orchestrator validating each step before deciding the next.

Out of scope (v1)/pi:steer (pi-subagents has no steering API), pi-intercom bridge, integrated MCP server (state lives elsewhere).

---

Related

the underlying multi-provider CLI.

that provides the subagent tool + the /run slash command inside pi. (pi-subagents also exposes /chain and /parallel; this plugin no longer surfaces them — see Status above.)

License: MIT.

Related plugins

Browse all →