Example

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-session

With 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
Print 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.

Full source and test .

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.