A prompt as an argument runs one turn and exits. That is enough to drop raunen into a pipeline โ or, with --json, into a program.
The answer goes to stdout and everything else โ thinking, tool calls, switches, warnings โ to stderr, so a pipe gets the reply and nothing else:
$ raunen 'summarise the diff on this branch' | pbcopyWhen a program consumes the result, --json prints one document instead:
$ raunen --json 'what does main.go do?' { "output": "It parses flags, loads the config, and starts either the TUI or a\n" "single one-shot turn.", "exit_code": 0, "model": "ollama/qwen3.5:latest", "session_id": "20260820-091144-2ee0", "steps": 2, "tool_calls": [ { "name": "read", "status": "success" }, { "name": "grep", "status": "success" } ], "usage": { "prompt": 4192, "completion": 210, "total": 4402 } }
raunen --json 'review this branch' | jq -r .output for the prose, or jq -e '.tool_calls[] | select(.status=="error")' to fail a pipeline when a tool did.
The document is printed even when the run fails, with the reason in error โ a consumer parsing stdout should never have to handle "sometimes there is JSON and sometimes there is not":
{
"output": "",
"error": "This request requires more credits, or fewer max_tokens.",
"exit_code": 1,
"model": "openrouter/moonshotai/kimi-k3",
"tool_calls": [],
"usage": { "total": 0 }
}tool_calls is always an array, never null, so it can be measured on a turn that called nothing. A tool that failed is reported as "status": "error" with the message, but does not fail the run โ the model is told and usually recovers. And model is the model that answered, which is not always the one asked for, since escalation can move up the ladder mid-turn.
| Exit | Meaning |
|---|---|
0 | the turn finished |
1 | it failed; error says why |
130 | interrupted with ctrl+c |
130 is the shell's convention for SIGINT, there so a script can tell "the user stopped this" from "the model failed". An interrupted run still prints its document, with whatever the model had produced in output.
A one-shot turn and an interactive one produce the same kind of conversation, so raunen 'question' followed by raunen --continue picks it up. --no-save opts out for a throwaway question, and then session_id is empty rather than naming a session that was never written. The conversation is saved before the result is reported, so a turn that ran out of context is still resumable.