Run Pi with no person attached
Print and JSON modes, how the mode is chosen when you do not say, what the exit status means in each, and how --tools narrows an unattended agent.
A CI job and a pre-commit hook both need Pi with no terminal. The mode
determines how input enters the agent, how output is exposed and how a failure
is reported. Every claim below was observed on 1.0.4 by driving the
shipped pi binary as a child process with a scripted model — the real
command line, mode selection and exit codes, with no credential and no
provider.
Pick the mode
pi --print "Summarize the changes" # one final string, then exit
pi --mode json "Review this repository" > events.jsonl # events on stdout
git diff | pi --print "Review this change" # piped stdin, prepended to the prompt
pi --mode json "@notes.md" "summarise" # @path includes a text file
pi --mode json -- "-5 degrees is cold" # -- stops option parsing
pi --print "Review this change" --tools read,grep,find,ls --no-sessionWith terminal stdin and stdout, Pi opens the terminal UI unless a mode is selected. When either stream is redirected and neither JSON nor RPC is selected, it chooses print mode — so a script that merely pipes input and captures output gets print mode implicitly. Be explicit anyway; a script that relies on TTY detection changes behaviour when run from an interactive shell.
What the exit status means
| Mode | Successful run | Failed final response |
|---|---|---|
| final text on stdout, exit 0 | non-zero exit, message on stderr, nothing on stdout | |
| JSON | events on stdout, exit 0 | the same failure in the event stream, still exit 0 |
The JSON asymmetry is deliberate: a script that checks only $? calls a
failed review a success. Pi still exits non-zero if the invocation itself
throws. Print mode exposes no intermediate events — the whole interface is
the final text.
Narrowing an unattended agent
A model that asked for bash under --tools read,grep,find,ls received a
failed tool result and the command did not run; with the default tools it did.
--no-session wrote no session file; without it, a run against a real
directory wrote one under the agent directory.
| Flag | Observed effect |
|---|---|
--tools read,grep,find,ls |
bash, edit and write are not offered; a request for bash fails and nothing runs |
--no-session |
in-memory session, nothing persisted |
Mechanism and limitations
Documented: cli-integration.md pins the mode-selection rule and the
print and JSON exit behaviour, and cli.md pins the input table (message,
@path, piped stdin, --) plus the caution to use --print rather than
--mode text for one-shot. Since 1.0.4, --tools alone does not remove MCP
tools; --no-mcp is the documented way to connect no servers.
Observed: the two tables above are asserted by headless.test.ts, run
against the shipped binary on 1.0.4 with the scripted model injected through
an extension: real subprocess, real configuration directory, real argument
parsing.
--tools narrows the model’s vocabulary; it does not change the process’s
operating-system permissions and does not constrain code that never asked for
a tool. Tool selection is not a sandbox. Print and JSON are one-shot: the
process never accepts a second instruction after the first. This example does
not show how a real model answers.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.