| name | acp-spawn |
| description | Run an ACP agent with a prompt and capture its full output as JSONL. Use when the user wants to run an agent, trace an agent invocation, inspect lifecycle events, debug ACP protocol behavior, or mentions "acp-spawn", "spawn", or "ACP". |
acp-spawn
A CLI tool that wraps any ACP agent and emits structured lifecycle events while passing through all agent output as JSONL.
What acp-spawn Does
acp-spawn runs an ACP agent, optionally sends a prompt via the ACP protocol handshake, and streams the full output (including thinking, tool calls, and responses) as JSONL on stdout.
Quick Start
acp-spawn run --prompt "say hello" -- opencode acp
This starts opencode acp, performs the ACP handshake, sends the prompt, and streams all output as JSONL until the agent finishes.
Usage
Run an agent with a prompt
acp-spawn run --prompt "fix the bug" -- opencode acp
acp-spawn run --prompt "say hello" -- codex-acp
acp-spawn run --prompt "say hello" -- claude-agent-acp
Run an agent without a prompt
Just wraps the child process lifecycle and passes through stdout:
acp-spawn run -- opencode acp
Set a working directory
acp-spawn run --cwd /path/to/project --prompt "review the code" -- opencode acp
Link to a parent run
Set RUN_ID in the environment — the child will receive PARENT_RUN_ID:
RUN_ID=run-parent-123 acp-spawn run --prompt "do the task" -- opencode acp
Filter output with jq
acp-spawn run --prompt "say hello" -- opencode acp | jq -c 'select(.event)'
acp-spawn run --prompt "say hello" -- opencode acp | jq -c 'select(.event | not)'
acp-spawn run --prompt "say hello" -- opencode acp | jq -s 'last | select(.event)'
How --prompt Works
When you pass --prompt, acp-spawn emits spawn_started, then performs the full ACP handshake before streaming agent output:
- Emits
spawn_started after the child process starts
- Sends
initialize → reads response
- Sends
session/new → extracts sessionId
- Sends
session/prompt with your text → reads response
- All three handshake responses and any interleaved notifications are passed through to stdout
- Streams the agent's remaining output as JSONL until it finishes
If the ACP handshake fails, acp-spawn emits spawn_failed, terminates the child process, and exits with an error.
Without --prompt, acp-spawn just wraps the child process lifecycle and passes through stdout.
CLI Reference
| Flag | Description |
|---|
--prompt <TEXT> | Prompt text to send via ACP protocol handshake. |
--cwd <DIR> | Working directory. Defaults to .. |
-- <command> [args...] | The agent command and its arguments (required). |
Output Format
acp-spawn writes lifecycle events as JSONL to stdout and passes child stdout through unchanged. Every line is valid JSON when the child also emits ACP or JSONL output.
Lifecycle events (generated by acp-spawn)
spawn_started — emitted when the agent process starts:
{"run_id":"run-...","timestamp":"...","event":"spawn_started","data":{"spawn_id":"spawn-...","agent":"opencode","command":["opencode","acp"],"cwd":"/path/to/dir","timeout_ms":300000,"pid":12345}}
spawn_completed — emitted when the agent exits successfully (code 0):
{"run_id":"run-...","timestamp":"...","event":"spawn_completed","data":{"spawn_id":"spawn-...","duration_ms":1234,"exit_code":0,"result":{"status":"success","summary":"agent 'opencode' completed successfully","run_id":"run-...","exit_code":0}}}
spawn_failed — emitted when the agent exits non-zero, times out, or is cancelled:
{"run_id":"run-...","timestamp":"...","event":"spawn_failed","data":{"spawn_id":"spawn-...","duration_ms":5000,"reason":"child process timed out after 5000ms","exit_code":null,"result":{"status":"failed","summary":"child process timed out after 5000ms","run_id":"run-...","error":"child process timed out after 5000ms"}}}
ACP protocol output (passthrough)
When --prompt is used, all ACP protocol data is passed through to stdout:
- initialize response (id:1) — agent capabilities and protocol version
- session/new response (id:2) — session ID and available models
- session/prompt response (id:3) — stopReason (e.g. "end_turn")
- session/update notifications — agent_thought_chunk, agent_message_chunk, tool_call, tool_call_update, available_commands_update, usage_update, etc.
The agent's stdout is passed through unchanged. acp-spawn never parses, wraps, or modifies ACP output.
Stderr
Agent stderr and runtime errors go to stderr, never stdout.
Run Tracing
acp-spawn propagates run context through environment variables:
| Env var | Purpose |
|---|
RUN_ID | Unique ID for the current run. |
PARENT_RUN_ID | ID of the parent run (empty for root runs). |
SPAWN_ID | Unique ID for this spawn operation. |
If RUN_ID is already set in the calling environment, acp-spawn captures it as parent_run_id. This enables nested tracing: a parent agent can spawn a child via acp-spawn and the relationship is visible in both stdout events and the child's environment.
Timeout and Cancellation
- Default timeout: 5 minutes (300000 ms).
- On timeout or signal (SIGINT / SIGTERM): sends SIGTERM to the entire child process group, waits 500 ms, then sends SIGKILL.
- The
spawn_failed event includes the termination reason.
- The child runs in its own process group so all grandchildren are cleaned up.
Non-Goals
acp-spawn intentionally does NOT:
- Store traces or provide querying/aggregation
- Normalize non-ACP stdout into ACP format
- Orchestrate multiple runs (queuing, scheduling, retries)
- Parse, wrap, enrich, or reinterpret child ACP events beyond lifecycle emission and native passthrough