pi-cli-subagent
Pi extension: dispatch a task to an installed external CLI coding agent and return its result inline.
Package details
Install pi-cli-subagent from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-cli-subagent- Package
pi-cli-subagent- Version
0.0.0- Published
- Sep 13, 2026
- Downloads
- 164/mo · 164/wk
- Author
- sero-dan
- License
- MIT
- Types
- extension
- Size
- 372.8 KB
- Dependencies
- 0 dependencies · 5 peers
Pi manifest JSON
{
"extensions": [
"./.pi/extensions/agent-dispatch/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-cli-subagent
A Pi extension that adds a dispatch tool: hand a task to an
installed external CLI coding agent — Codex CLI or Claude Code — and get
its response back inline in your Pi session.
Dispatch is blocking and read-only. The external agent can read your workspace and run read-only commands, but it cannot modify your working tree.
- Why: it produces genuinely independent verification — a different model, a different toolchain, a different set of habits — without pulling all of that agent's context into your Pi session.
- Not: background execution, session resume, or write-enabled delegation.
Requirements
| Requirement | Notes |
|---|---|
| Pi with extension support | The host that loads this package. |
| Node.js ≥ 22.6 | Only for development (--experimental-strip-types). Runtime uses Pi's Node. |
codex on PATH |
Codex CLI, installed and authenticated. Needed for cli: "codex". |
claude on PATH |
Claude Code, installed and authenticated. Needed for cli: "claude". |
You only need the CLI(s) you actually intend to dispatch to. If a CLI is
missing, that dispatch fails with a clear cli-not-installed error rather than
hanging.
The CLIs are runtime dependencies of your environment, not bundled by this package. Authentication is entirely theirs — this extension never handles your credentials.
Installation
Use it as a project-local extension
The extension is already wired into this repository at
.pi/extensions/agent-dispatch/index.ts. Run Pi from this directory and the
dispatch tool is available.
Install as a Pi package
pi install /absolute/path/to/pi-cli-subagent
pi install ./relative/path/to/pi-cli-subagent
pi install git:github.com/user/pi-cli-subagent@v1
Or try it without installing:
pi -e /absolute/path/to/pi-cli-subagent
The package manifest (pi.extensions in package.json) points at the extension
entry point:
"pi": { "extensions": ["./.pi/extensions/agent-dispatch/index.ts"] }
Enable or disable it with pi config (press Tab to switch between
global and project settings).
Usage
The extension registers exactly one tool, dispatch.
| Parameter | Required | Description |
|---|---|---|
task |
yes | The task to hand to the external agent. Passed through unchanged. |
cli |
yes | Which agent to run: "codex" or "claude". |
model |
no | Model identifier passed to the CLI unmodified. Omit to use the CLI's own configured default. |
effort |
no | Reasoning effort level. Omit to use the CLI's configured default. |
scope |
no | Optional scope hint appended after the task (e.g. a directory or file list). |
You rarely call this tool by hand — Pi's model decides to, based on your request:
Have Codex review my uncommitted changes for risk.
Ask Claude, on high effort, to review this file for race conditions.
Get a second opinion from Codex on the schema in
src/db.ts.
Because task is passed through verbatim, review framing, output format, and
"second opinion" behavior are all just prompts — nothing is hardcoded. The only
standing instruction added is a short nudge to assess the evidence independently.
model and effort are never translated between CLIs; if you name them, they go
straight to the CLI.
Model and effort resolution
- Omit
model/effort→ the CLI's own configured default is used. - Name
model/effort→ passed through unvalidated, except for Codex, which is pre-validated against its model catalogue (codex debug models) so an unsupported value fails fast with the valid alternatives listed. - The result reports CLI-confirmed model/effort separately from what you
requested. Values the CLI does not report are labelled
not reportedrather than guessed.
The read-only guarantee
Dispatch refuses to run at all if it cannot enforce read-only across the complete launch configuration — shell, file tools, hooks, and MCP servers — not just shell commands. This is enforced by each CLI's own sandbox, never by asking the agent nicely.
| CLI | Key launch restrictions |
|---|---|
| Codex | -s read-only, --ignore-user-config, --ignore-rules, --disable hooks, --disable plugins |
| Claude | --safe-mode, --strict-mcp-config, --tools Read,Bash, sandbox forced on with allowUnsandboxedCommands: false and the workspace root in denyWrite |
Note that read-only protects your working tree. It does not establish confidentiality or prevent network side effects. See TODO.md.
Failures
Failures are typed and reported with diagnostics rather than thrown. A failed
dispatch sets isError: true and names the cause.
| Kind | Meaning |
|---|---|
missing-cli / unsupported-cli |
No cli, or an unrecognized one. |
cli-not-installed |
The CLI binary is not on PATH. |
read-only-unavailable |
The adapter cannot enforce read-only; dispatch refuses. |
invalid-request |
Rejected by preflight (e.g. an unsupported Codex model). |
spawn |
The process could not be launched. |
nonzero-exit / signal |
The CLI exited non-zero or was killed by a signal. |
error-envelope |
The CLI reported an error in its terminal envelope. |
malformed-stream |
The CLI emitted unparseable output. |
missing-terminal |
Exit 0 with no recognizable terminal result. |
cancelled / shutdown |
Cancelled by the caller, or the session is shutting down. |
Reported usage (tokens/cost) is preserved on both success and failure.
Development
Setup
npm run link-deps # symlink Pi runtime packages from your installed Pi
npm run typecheck # tsc --noEmit
npm test # node --test over test/*.test.ts
link-deps exists so tsc and the test runner resolve the Pi peer packages
(@earendil-works/*, typebox) from your local Pi installation without a
network install. Only the Pi runtime packages and @types/node are linked.
Tests
All tests run against a fake CLI (test/fixtures/fake-cli.mjs) that emits
scripted JSONL, so the suite is deterministic, fast, and free. It covers stream
parsing, failure classification, process lifecycle and cleanup, cancellation,
signal handling, and argument construction.
Live checks
node --experimental-strip-types scripts/live-check.mts <repo>
This harness makes real, paid CLI calls and is run manually, not in CI. It verifies the things fixtures cannot: that each CLI accepts the launch flags, that read-only denials actually happen and the tree is unchanged, and that live streams parse. Use a throwaway repo.
Architecture
.pi/extensions/agent-dispatch/
index.ts Extension entry: owns the process registry, wires tools
tool.ts `dispatch` tool registration + isError patching
dispatch-core.ts CLI-agnostic dispatch logic (no Pi imports)
runner.ts Spawn, stream, terminate, classify the outcome
process-registry.ts Tracks child process groups; kills on cancel/shutdown
adapters/ One module per CLI: codex.ts, claude.ts, util.ts
prompt.ts, render.ts, types.ts
Adding a CLI means adding one adapter and no changes to the tool. An adapter
implements CliAdapter (types.ts):
buildCommand(req, defaults)→ the exact argv/env to spawn (required)parseLine(line, state)→ normalized stream events (required)extractResult(state)→ final text, usage, identity, terminal/error flags (required)preflight(req, ctx)→ validate a request and resolve defaults (optional)discoverModels(ctx)→ enumerate models/efforts (optional)
Every adapter normalizes its CLI's output into the same event stream, so
dispatch-core.ts contains zero CLI-specific knowledge.
Known limitations
Security caveats and deferred work are tracked in TODO.md. The two most important: the child environment uses a denylist, not an allowlist; and detached descendants can escape process-group cleanup.
License
MIT © 2026 Daniel Carter