Right now pi -p feels like a black box: it runs the prompt, sits silently, then prints the final assistant text and exits. Other CLIs (e.g. OpenCode) stream realtime progress even in their “print” / non-interactive mode: partial assistant output, tool calls, tool output, errors, etc.
I want the same experience in pi, without breaking the current “final-only” behavior that people rely on in scripts.
Current behavior
pi -p "prompt" (text mode): waits for completion, prints only the final assistant text, exits
pi --mode json "prompt": prints the full JSON event stream in realtime
What I want
Add a streaming option to print mode that emits human-readable realtime output.
CLI behavior
- New flag:
--stream (-s)
- Defaulting:
- If stdout is a TTY and
--stream is not specified: streaming should be auto-enabled
- If stdout is non-TTY (piped/redirected) and
--stream is not specified: keep the existing final-only output (backwards compatible)
--mode json stays exactly as-is (raw JSON events)
Output format (human-readable)
- Plain text by default (minimal/no ANSI), with stable prefixes for non-text events:
[thinking] ...
[tool <name>] calling...
[tool <name>] output:
[error] ...
- Partial assistant text should stream as it arrives (append to stdout)
- Tool output should use sane truncation defaults:
- truncate very large outputs
- clearly mark truncation (
[truncated])
- if there’s a known “full output path” available, print it
Important: no duplicate final output
If partial assistant text is streamed, the final assistant response should not be printed again in full at the end. (No double-printing.)
Where this likely lives
packages/coding-agent/src/cli/args.ts — add --stream / -s
packages/coding-agent/src/main.ts — wire flag into print mode
packages/coding-agent/src/modes/print-mode.ts — subscribe to session events and render them to stdout
packages/coding-agent/test/* — add tests for streaming + deduplication
packages/coding-agent/README.md — document --stream
packages/coding-agent/CHANGELOG.md — Unreleased entry
Implementation sketch
- Parse
--stream / -s and decide:
streamingEnabled = parsed.stream || (process.stdout.isTTY && parsed.stream !== false)
- In
runPrintMode():
- If
streamingEnabled: subscribe to session events and print as they arrive
- incremental text chunks → write to stdout
- tool call start/end/results → prefixed lines/blocks
- thinking/progress → brief lines
- errors/aborts → stderr, non-zero exit on error
- If not enabled: keep current final-only behavior
- Ensure stdout is drained before exit (avoid losing the tail of streamed output)
Acceptance criteria
pi -p --stream "prompt" streams:
- partial assistant text live
- tool calls + tool output in chronological order with clear prefixes
- thinking/progress lines
- errors/aborts to stderr; non-zero exit on error
- no duplicate final assistant output
pi -p "prompt" > out.txt stays final-only unless --stream is explicitly passed
pi --mode json "prompt" unchanged
- Tests cover streaming and deduplication
Examples
- Human (live):
pi -p --stream "summarize my repo"
- Script (unchanged):
pi -p "summarize my repo" > result.txt
- JSON (unchanged):
pi --mode json "summarize my repo"
Right now
pi -pfeels like a black box: it runs the prompt, sits silently, then prints the final assistant text and exits. Other CLIs (e.g. OpenCode) stream realtime progress even in their “print” / non-interactive mode: partial assistant output, tool calls, tool output, errors, etc.I want the same experience in pi, without breaking the current “final-only” behavior that people rely on in scripts.
Current behavior
pi -p "prompt"(text mode): waits for completion, prints only the final assistant text, exitspi --mode json "prompt": prints the full JSON event stream in realtimeWhat I want
Add a streaming option to print mode that emits human-readable realtime output.
CLI behavior
--stream(-s)--streamis not specified: streaming should be auto-enabled--streamis not specified: keep the existing final-only output (backwards compatible)--mode jsonstays exactly as-is (raw JSON events)Output format (human-readable)
[thinking] ...[tool <name>] calling...[tool <name>] output:[error] ...[truncated])Important: no duplicate final output
If partial assistant text is streamed, the final assistant response should not be printed again in full at the end. (No double-printing.)
Where this likely lives
packages/coding-agent/src/cli/args.ts— add--stream/-spackages/coding-agent/src/main.ts— wire flag into print modepackages/coding-agent/src/modes/print-mode.ts— subscribe to session events and render them to stdoutpackages/coding-agent/test/*— add tests for streaming + deduplicationpackages/coding-agent/README.md— document--streampackages/coding-agent/CHANGELOG.md— Unreleased entryImplementation sketch
--stream/-sand decide:streamingEnabled = parsed.stream || (process.stdout.isTTY && parsed.stream !== false)runPrintMode():streamingEnabled: subscribe to session events and print as they arriveAcceptance criteria
pi -p --stream "prompt"streams:pi -p "prompt" > out.txtstays final-only unless--streamis explicitly passedpi --mode json "prompt"unchangedExamples
pi -p --stream "summarize my repo"pi -p "summarize my repo" > result.txtpi --mode json "summarize my repo"