-p "message"
for one turn of plain-text output, a bare -p reading the message from stdin, and
--json for the machine-readable line. The JSON contract is the same one
python -m ziro_cli.cli.chat_once emits, so a script can move between the two without
changing its parser. Headless runs never open the setup wizard or the session
pickers: the user resolves from --user or the last-used user, and a missing
user or missing API key fails fast to stderr with a pointer at ziro setup.
A -p run behaves like a TUI session with nobody at the keyboard. It has the same
tools (MCP servers are connected before the turn in both), the same background
behavior (subagents and shell commands detach and hand back a handle exactly as
in the TUI, and with auto_reinvoke on, a finished background subagent triggers
the same follow-up turn), and the same plugins (trust you recorded is honored;
ZIRO_STRICT_SECURITY still keeps dangerous parts inert).
The one difference is who answers an approval. A headless run has no human to
ask, so any permission prompt is auto-denied rather than blocking, and the
JSON lists it under interrupts. Pass --mode auto to get the TUI’s
no-confirmation mode instead.
The JSON line also carries recursion_limited: true when the turn stopped at
its step limit before finishing. The process then exits with code 3 (1 still
means an error). Run the same --thread again with continue to resume.
Unattended background jobs stay stricter: their
children always block and dangerous plugin parts stay inert.
Reference files with @path in the message, or add media from outside the project
with the repeatable --attach <path>. Anything skipped or downgraded is printed to
stderr as [ziro] <notice>, and --json adds a notices array to its output. See
File references.
This page is in progress. See the chat-once reference for
the flag list and CLI reference for the dispatch rule.
chat_once
Flags, JSON fields, and the background job queue.
Background jobs
Submitting and polling work that outlives one turn.