Scripting & automation
Run Stella headlessly in CI with JSON and streaming-JSON output, environment-variable configuration, and enforced budgets.
Stella is built to run unattended. The --output-format flag switches from the
human-oriented terminal rendering to machine-readable output, and every important flag has
an environment-variable equivalent for CI.
Output formats
--output-format (env STELLA_OUTPUT_FORMAT) takes one of three values:
| Value | Shape | Use |
|---|---|---|
text | Interactive human rendering (default) | Local, interactive use |
json | One final JSON object summarizing the turn | Capture a result in a script |
stream-json | One JSON line per agent event, as it happens | Live progress in a pipeline |
stream-json is a line-per-event serialization of Stella's internal event enum — a stable
machine interface you can parse incrementally. Each line is one event object tagged
"type" in snake_case (stage, text, reasoning, tool_start, tool_result, …).
The vocabulary is additive-only: skip lines whose type you don't recognize. In
particular, text_delta lines stream the answer token by token as a best-effort live
preview — the text event that follows carries the full step text and is authoritative
(replace any accumulated deltas with it; a retried model call re-streams its deltas).
# One final JSON object
stella --output-format json run "list the public API of the auth module"
# Newline-delimited event stream, parsed as it arrives
stella --output-format stream-json run "fix the failing test" | while IFS= read -r line; do
echo "$line" | jq -r '.type // "event"'
donejson embeds those same event objects under an events key in its single summary
object — exactly what stream-json would have emitted line by line. The summary's other
keys depend on the execution path: a default run goes through the staged pipeline and
summarizes as status, text, cost_usd, reason, task_class, verdict,
revisions, candidates_run, model, and events; a --no-pipeline (raw step-loop)
run summarizes as status, text, cost_usd, reason, model, and events, plus the
top-level files_touched telemetry payload — which the
pipeline summary does not carry.
Configure via environment
Every core flag has an environment variable, so a CI job can be configured without editing the command line:
| Flag | Environment variable |
|---|---|
--model | STELLA_MODEL |
--base-url | STELLA_BASE_URL |
--output-format | STELLA_OUTPUT_FORMAT |
--budget | STELLA_BUDGET |
export STELLA_MODEL=anthropic/claude-fable-5
export STELLA_OUTPUT_FORMAT=stream-json
export STELLA_BUDGET=5
stella run "update the changelog for the pending release"Headless credentials
In CI, supply the provider key through the environment (or a mounted
settings.json) — never the interactive prompt, which is
skipped when there is no TTY:
export ANTHROPIC_API_KEY="$SECRET_ANTHROPIC_KEY"
stella --output-format json run "generate release notes from the diff since the last tag"With no resolvable key and no TTY, Stella fails fast with a clear error rather than hanging on a prompt that can never be answered. Make sure a key is in the environment before the run.
Enforced budgets in CI
Pair headless runs with an enforced budget so an automated job can never overspend:
stella --budget 3 --output-format json run "port the utils module to the new client"Work aborts cleanly once the cap is reached — never mid-tool — so the workspace is left in a consistent state.
Exit codes
stella exits 0 on success and non-zero on failure (an unresolved credential, an
invalid flag, or an aborted run), so you can gate a pipeline on its result.
Extension hooks
A typed, in-process event bus that lets extensions observe Stella's activity and gate policy-sensitive actions — tool calls, file writes, shell commands, and delivery — with allow / modify / deny / require-approval decisions.
Donate
Stella is free, BYOK, phone-home-free, and vendor-agnostic — built and funded by one person. If it saves you time or money, consider sponsoring.