<!-- GENERATED by scripts/build-docs.mjs from the live Commander tree (src/cli/program.ts). Do not edit by hand. -->

# pai worker

> Run subagents on configured worker providers: run, ps, follow, replay,

pane, log, status-line, providers, roles, goal, config, on, off, install.
run takes --spec <file> (or --spec -) to read the prompt from a file/stdin
instead of an inline -p '<prompt>', which breaks on shell quoting.

## Synopsis

```
pai worker <subcommand> [options]
```

## Subcommands

| Command | Description |
|---------|-------------|
| [`pai worker run [args...]`](#pai-worker-run-args) | Run one claude-code worker through the configured provider. |
| [`pai worker ps`](#pai-worker-ps) | List workers of this session (RUNNING + FINISHED); --all for every worker |
| [`pai worker follow [id]`](#pai-worker-follow-id) | Live transcript of one worker, or of this session's running workers |
| [`pai worker replay <id>`](#pai-worker-replay-id) | Print the transcript of one finished or running worker |
| [`pai worker watch`](#pai-worker-watch) | ps refreshed every 2 seconds (plain `watch`, colors kept) |
| [`pai worker pane [id]`](#pai-worker-pane-id) | Open the follow pane for a worker (or one shared pane for this session) |
| [`pai worker log [what]`](#pai-worker-log-what) | all = ledger, tail = last ledger lines, <id> = raw event stream, none = list |
| [`pai worker say <id> <text>`](#pai-worker-say-id-text) | Send one message to a running worker (forwarded to its open stdin) |
| [`pai worker goal <id> <text>`](#pai-worker-goal-id-text) | Relabel a running worker (its ps / pane goal) without sending it a message |
| [`pai worker handoff <json>`](#pai-worker-handoff-json) | From inside a worker: append a handoff to the parent's inbox and (when it runs) say it to the parent. |
| [`pai worker merge <id>`](#pai-worker-merge-id) | Merge a worker's worktree branch (worker/<id>) into the original checkout, then remove the worktree and delete the branch |
| [`pai worker wait <ids...>`](#pai-worker-wait-ids) | Poll workers until they finish; prints each result as one JSON line, exit 1 on failure or timeout |
| [`pai worker discard <id>`](#pai-worker-discard-id) | Drop a worker's worktree and branch, keeping nothing |
| [`pai worker kill <id>`](#pai-worker-kill-id) | Send SIGTERM to a running worker process |
| [`pai worker controls <id> <who>`](#pai-worker-controls-id-who) | Hand the desktop controls (clickr) to a worker or take them back. |
| [`pai worker resume <id> <text>`](#pai-worker-resume-id-text) | Continue a finished worker on the same provider: claude --resume <session> |
| [`pai worker proxy [stop]`](#pai-worker-proxy-stop) | The local Anthropic↔OpenAI proxy (loopback only); started on demand by `run`, |
| [`pai worker mcp [list]`](#pai-worker-mcp-list) | MCP servers workers may load via --mcp / roles, and the configured sets |
| [`pai worker status-line [term] [cwd] [session]`](#pai-worker-status-line-term-cwd-session) | One-line worker summary for a status bar (empty when none in scope). |
| [`pai worker on`](#pai-worker-on) | Route Agent-tool subagents to workers (default when a provider exists) |
| [`pai worker off`](#pai-worker-off) | Stop routing: Agent tool runs on Anthropic again |
| [`pai worker fallback [action] [provider]`](#pai-worker-fallback-action-provider) | Machine-wide fallback: every NEW Claude Code process runs on a worker |
| [`pai worker install`](#pai-worker-install) | Migrate: Agent hook in settings.json, ~/.local/bin glm* shims, old script cleanup |
| [`pai worker providers`](#pai-worker-providers) | Providers: list (default), add, remove, use, enable, disable, test |
| [`pai worker classes`](#pai-worker-classes) | Classes: which provider serves draft / implement / review / … |
| [`pai worker model [what] [model]`](#pai-worker-model-what-model) | Model ids per provider: no args lists them, |
| [`pai worker capability [name] [providers]`](#pai-worker-capability-name-providers) | Which provider(s) serve a capability (e.g. image), independent of any one |
| [`pai worker config`](#pai-worker-config) | workers.yaml itself: path, init, migrate, check, inline-keys |

### pai worker run [args...]

Run one claude-code worker through the configured provider.

Unknown options are passed to claude verbatim (e.g. -p, --allowedTools);
--output-format/--verbose are handled here.
--label "<goal>" is optional — it is the row shown in ps / follow /
the status line; when absent it is derived from the prompt's first line
(--chain/--agent derive their own instead).
--spec <file> (or --spec -) reads the prompt from a file/stdin instead of
an inline -p '<prompt>', which breaks on shell quoting; mutually
exclusive with -p.
Grant MCP tools by naming mcp__server__tool in --allowedTools (the server loads automatically);
--chain draft,implement[,review] runs a spec-first pipeline;
--agent <name> runs an agent definition from ~/.claude/agents.

**Arguments**

| Argument | Kind | Description |
|----------|------|-------------|
| `[args...]` | variadic | claude arguments, e.g. -p '<task>' --allowedTools 'Read,Edit,Bash' |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--provider <name>` | Provider to run on (default: active, else routing order) |  |
| `--class <name>` | Use the provider of this class (draft, implement, review, research, spotcheck, simple, complex, image) |  |
| `--role <name>` | Alias of --class (roles were renamed to classes) |  |
| `--capability <name>` | Run on whichever configured provider serves this capability (see `pai worker capability`), not the active provider or --class's own one — e.g. --capability image runs an `engine: image` provider directly, POSTing a prompt and writing the PNG instead of spawning claude |  |
| `--for <name>` | Alias of --capability |  |
| `--out <file>` | Where an image-capability run writes its PNG (default: <logDir>/<worker-id>.png) |  |
| `--size <WxH>` | Image size for an image-capability run, e.g. 1024x1024 (default: 1024x1024) |  |
| `--timeout-ms <n>` | Timeout for an image-capability run's HTTP request (default: 120000) |  |
| `--chain <stages>` | Comma-separated stage classes, e.g. draft,implement or draft,implement,review |  |
| `--agent <name>` | Run the agent definition ~/.claude/agents/<name>.md on a worker |  |
| `--model <model>` | Override the model for this run. Headless (-p) workers default to the --class model; an interactive launch (no -p) with no --model uses the harness default model from settings.json. |  |
| `--label <text>` | Short task label shown in ps / follow / status line (default: first line of the prompt) |  |
| `--spec <path>` | Read the prompt from this file (or - for stdin) instead of -p; mutually exclusive with -p |  |
| `--cwd <dir>` | Directory the worker runs in (default: this process's cwd) |  |
| `--mcp <names>` | MCP servers/sets this worker may use (comma-separated; see `pai worker mcp`) |  |
| `--no-pane` | Do not open a follow pane for this worker |  |
| `--worktree` | Run in a git worktree on branch worker/<id> (default for implement/complex/plan in a git repo) |  |
| `--no-worktree` | Run in place, no worktree |  |
| `--print-cmd` | Print the assembled claude argv as JSON and exit, without spawning (audit tool) |  |
| `--report <format>` | Final-report contract/parser: json or ag2 (default: ag2, or PAI_WORKER_REPORT) |  |
| `--no-report-retry` | Skip the one bounded re-ask when the final AG2 message fails validation |  |


### pai worker ps

List workers of this session (RUNNING + FINISHED); --all for every worker

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--all` | Show workers of all sessions, not just this terminal's |  |


### pai worker follow [id]

Live transcript of one worker, or of this session's running workers

**Arguments**

| Argument | Kind |
|----------|------|
| `[id]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--all` | Follow workers of all sessions |  |
| `--auto-exit [secs]` | Exit secs after the workers end (pane mode; default from config) |  |


### pai worker replay <id>

Print the transcript of one finished or running worker

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--tail <n>` | Only the last n rendered lines |  |


### pai worker watch

ps refreshed every 2 seconds (plain `watch`, colors kept)


### pai worker pane [id]

Open the follow pane for a worker (or one shared pane for this session)

**Arguments**

| Argument | Kind |
|----------|------|
| `[id]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--check` | Only report whether the pane is open, plus the profile file's path, font, and the hosting window's bounds |  |


### pai worker log [what]

all = ledger, tail = last ledger lines, <id> = raw event stream, none = list

**Arguments**

| Argument | Kind |
|----------|------|
| `[what]` | optional |


### pai worker say <id> <text>

Send one message to a running worker (forwarded to its open stdin)

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |
| `<text>` | required |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--goal <text>` | Relabel the worker (its ps / pane goal) before sending the message |  |


### pai worker goal <id> <text>

Relabel a running worker (its ps / pane goal) without sending it a message

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |
| `<text>` | required |


### pai worker handoff <json>

From inside a worker: append a handoff to the parent's inbox and (when it runs) say it to the parent.

Payload: {"kind":"proposal|question|blocker","text":"…","data":{…}} — from/to come from the environment.

**Arguments**

| Argument | Kind |
|----------|------|
| `<json>` | required |


### pai worker merge <id>

Merge a worker's worktree branch (worker/<id>) into the original checkout, then remove the worktree and delete the branch

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |


### pai worker wait <ids...>

Poll workers until they finish; prints each result as one JSON line, exit 1 on failure or timeout

Never busy-wait for a worker: no sleep loops, no sleep-then-`pai worker ps` polling, no manual retry loops. Two sanctioned waits: run workers as background Bash tasks (the harness notifies on completion), or call `pai worker wait`, which blocks until they finish and prints each result. If you catch yourself sleeping to re-check a worker, stop — you already get notified. Supervision events arrive automatically — no polling, no sleeps: the daemon watches your workers and tells you when one finishes, fails or stalls (docs/worker.md, Supervision).

**Arguments**

| Argument | Kind |
|----------|------|
| `<ids...>` | variadic |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--timeout <secs>` | Give up after this many seconds (default 900) |  |


### pai worker discard <id>

Drop a worker's worktree and branch, keeping nothing

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |


### pai worker kill <id>

Send SIGTERM to a running worker process

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |


### pai worker controls <id> <who>

Hand the desktop controls (clickr) to a worker or take them back.

<who> is `you` (the worker may actuate) or `me` (the operator keeps them);
inside a worker's pane, typing "your controls" does the same.

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |
| `<who>` | required |


### pai worker resume <id> <text>

Continue a finished worker on the same provider: claude --resume <session>

**Arguments**

| Argument | Kind |
|----------|------|
| `<id>` | required |
| `<text>` | required |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--print-id` | Print the new worker id on its own line (pane follow handoff) |  |
| `--no-pane` | Do not open a follow pane for the resumed worker |  |


### pai worker proxy [stop]

The local Anthropic↔OpenAI proxy (loopback only); started on demand by `run`,

`proxy` starts/verifies it, `proxy stop` stops it again

**Arguments**

| Argument | Kind |
|----------|------|
| `[stop]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--port <n>` | Port to listen on (default 8797) |  |


### pai worker mcp [list]

MCP servers workers may load via --mcp / roles, and the configured sets

**Arguments**

| Argument | Kind |
|----------|------|
| `[list]` | optional |


### pai worker status-line [term] [cwd] [session]

One-line worker summary for a status bar (empty when none in scope).

Called by statusline-command.sh with ITERM_SESSION_ID, the pane cwd and
the claude session id (claims workers spawned by that session's Bash).

**Arguments**

| Argument | Kind |
|----------|------|
| `[term]` | optional |
| `[cwd]` | optional |
| `[session]` | optional |


### pai worker on

Route Agent-tool subagents to workers (default when a provider exists)


### pai worker off

Stop routing: Agent tool runs on Anthropic again


### pai worker fallback [action] [provider]

Machine-wide fallback: every NEW Claude Code process runs on a worker

provider (settings.json env + model pin) until switched back.
on [provider] switches (default: active), off restores settings.json
exactly, status shows state and running sessions. CLAUDE_SETTINGS_PATH
points at another settings.json for dry runs.

**Arguments**

| Argument | Kind |
|----------|------|
| `[action]` | optional |
| `[provider]` | optional |


### pai worker install

Migrate: Agent hook in settings.json, ~/.local/bin glm* shims, old script cleanup


### pai worker providers

Providers: list (default), add, remove, use, enable, disable, test


### pai worker providers add <name>

Add a provider; the first one also turns workers on and seeds classes.

Example: pai worker providers add glm --url https://…/anthropic \
           --key sk-… --model glm-5.3 --fast-model glm-5.3-flash
--key writes the token inline as `key:` in workers.yaml (quoted, file kept 0600);
--key-file writes only a path to a 0600 file holding it — use one or the other.
OpenAI-protocol: --protocol openai --upstream-url https://…/v1 (runs via the PAI proxy).
Codex (ChatGPT plan): --engine codex — runs through the Codex CLI.
Image generation: --engine image — `pai worker run --capability image` then POSTs
straight to {url}/images/generations instead of spawning claude.

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--base-url <url>` | Anthropic-compatible API base URL (required unless --protocol openai) |  |
| `--url <url>` | Alias of --base-url |  |
| `--model <model>` | Default model id for this provider |  |
| `--key <token>` | API token, written inline as `key:` (quoted); use instead of --key-file |  |
| `--key-file <path>` | File holding the API token (0600); omit for token "local" |  |
| `--fast-model <model>` | Cheaper model for spotchecks and routing |  |
| `--env <name=value>` | Extra env for runs (repeatable) | `` |
| `--note <text>` | Human note shown in `providers list` |  |
| `--protocol <proto>` | anthropic (default) or openai — openai runs through the local PAI proxy |  |
| `--upstream-url <url>` | Chat Completions base URL (required for --protocol openai) |  |
| `--engine <engine>` | claude (default), codex, or image — codex runs `codex exec --json`, image POSTs {url}/images/generations |  |
| `--context-window <tokens>` | Context window for the meter (default 200000; init event overrides) |  |
| `--quota-probe <url>` | URL whose JSON first number is the quota percent (0-100) |  |
| `--cost-tier <1-5>` | Cost tier 1 (cheapest) … 5 (most expensive; default 3) |  |
| `--tags <tags>` | Capability tags, comma-separated (from: code, vision, image-gen, long-context, fast, reasoning) | `` |


### pai worker providers update <name>

Change cost tier and tags of a provider (routing constraints use these)

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--cost-tier <1-5>` | Cost tier 1 (cheapest) … 5 (most expensive) |  |
| `--tags <tags>` | Capability tags, comma-separated (from: code, vision, image-gen, long-context, fast, reasoning); --tags '' clears | `` |


### pai worker providers remove <name>

Remove a provider and any classes pointing at it

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |


### pai worker providers use <name>

Make this provider the active one for runs without --provider/--class

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |


### pai worker providers enable <name>

Enable a provider (also clears its cooldown)

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |


### pai worker providers disable <name>

Disable a provider (auto-routing skips it; --provider still works)

**Arguments**

| Argument | Kind |
|----------|------|
| `<name>` | required |


### pai worker providers test [name]

One-word pong probe through a provider (default: the active one)

**Arguments**

| Argument | Kind |
|----------|------|
| `[name]` | optional |


### pai worker classes

Classes: which provider serves draft / implement / review / …


### pai worker classes list

List classes and their targets (default action)


### pai worker classes set <class> [target]

Point a class at a provider (or provider/fast), or give only constraints:

classes set research --max-cost-tier 2 --require-tags long-context,reasoning

**Arguments**

| Argument | Kind |
|----------|------|
| `<class>` | required |
| `[target]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--provider <name>` | Pin the class to this provider (object form) |  |
| `--mcp <names>` | MCP servers/sets for runs of this class (comma-separated) |  |
| `--max-cost-tier <1-5>` | Auto-routing considers only providers up to this cost tier |  |
| `--require-tags <tags>` | Auto-routing needs these tags (comma-separated) |  |
| `--order <providers>` | Per-class routing order overriding workers.routing.order (comma-separated) |  |


### pai worker classes unset <class>

Remove a class (runs then use the active provider)

**Arguments**

| Argument | Kind |
|----------|------|
| `<class>` | required |


### pai worker model [what] [model]

Model ids per provider: no args lists them,

`model <model-id>` sets the active provider's default model (back-compat),
`model <capability>` shows one capability, `model <capability> <model-id>` sets it.
Capability names are open (default, fast, image, … — any ^[a-z][a-z0-9-]*$ name);
`pai worker capability` picks which provider serves one across the whole config.
--provider targets another provider.

**Arguments**

| Argument | Kind |
|----------|------|
| `[what]` | optional |
| `[model]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--provider <name>` | Provider to read or change (default: the active one) |  |


### pai worker capability [name] [providers]

Which provider(s) serve a capability (e.g. image), independent of any one

provider's own model table: no args lists every preference and what it
resolves to; `capability <name> <provider>[,<provider>…]` sets the
preference list (first usable one wins); `capability <name> --unset`
removes it. An `engine: image` provider preferred for "image" runs
`pai worker run --capability image` directly against its images API.

**Arguments**

| Argument | Kind |
|----------|------|
| `[name]` | optional |
| `[providers]` | optional |

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--unset` | Remove the preference for <name> |  |
| `--json` | Machine-readable output |  |


### pai worker config

workers.yaml itself: path, init, migrate, check, inline-keys


### pai worker config path

Print the resolved workers.yaml path


### pai worker config init

Write the commented starter workers.yaml (refuses if one already exists)


### pai worker config migrate

Two things this can mean, chosen from what is on disk:

- workers.yaml still at an old location (~/.claude/workers.yaml or
    ~/.config/pai/workers.yaml): moved byte-for-byte to ~/.claude/pai/workers.yaml
    (no JSON involved); the old file is renamed to workers.yaml.migrated-<date>.
  - no workers.yaml yet: built from the JSON `workers` section, which is
    backed up to workers.json.migrated-<date> next to config.json.

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--force` | Overwrite an existing workers.yaml |  |
| `--dry-run` | Print the plan without writing anything |  |


### pai worker config check [path]

Validate workers.yaml (or the file at [path]); exits non-zero with file:line on error

**Arguments**

| Argument | Kind |
|----------|------|
| `[path]` | optional |


### pai worker config inline-keys

Move each provider's key_file contents inline as `key:` (quoted); key files are left on disk

**Options**

| Option | Description | Default |
|--------|-------------|---------|
| `--dry-run` | Print the plan (provider names and key file paths, never key values); write nothing |  |


## See also

[`pai audit`](audit.md) · [`pai backup`](backup.md) · [`pai clear-names`](clear-names.md) · [`pai config`](config.md) · [`pai daemon`](daemon.md) · [`pai db`](db.md) · [`pai end`](end.md) · [`pai help`](help.md) · [`pai identity`](identity.md) · [`pai kg`](kg.md) · [`pai launch`](launch.md) · [`pai mcp`](mcp.md) · [`pai memory`](memory.md) · [`pai notify`](notify.md) · [`pai observation`](observation.md) · [`pai obsidian`](obsidian.md) · [`pai pause`](pause.md) · [`pai project`](project.md) · [`pai projects`](projects.md) · [`pai registry`](registry.md) · [`pai restore`](restore.md) · [`pai session`](session.md) · [`pai sessions`](sessions.md) · [`pai setup`](setup.md) · [`pai shell-init`](shell-init.md) · [`pai skill`](skill.md) · [`pai task`](task.md) · [`pai topic`](topic.md) · [`pai update`](update.md) · [`pai zettel`](zettel.md)

Run `pai help <area>` to read any of these in the terminal.

