Contents

EONYX Headless Mode: Running the Agent Without the TUI

Besides the interactive TUI, EONYX can run headless — perform a task and exit. That is what you need for scripts, pipes and CI: predictable output, JSON events and meaningful exit codes.

EONYX runs without the TUI through a single run subcommand — with separated streams and saved sessions, so the conversation can be continued.

eonyx run "describe what this repository does"
eonyx run -m deepseek-v4-pro --yolo "fix the failing test"

The prompt comes from the arguments or from stdin. The argument wins: if it is given, stdin is not read — which is why, when piping, you do not duplicate the prompt as an argument:

eonyx run "explain the error in the log"                  # prompt from the argument
{ echo "explain the error:"; cat error.log; } | eonyx run # prompt from stdin

Flags: -m/--model, -s/--session <id> (continue a specific session), -c/--continue (continue the most recent one), --yolo (drop the permission gate), --json (events as NDJSON), --system-prompt (replace the system prompt).

In normal (text) mode the streams are separated — this makes the output convenient for pipes:

  • stdout — only the final response text from the model (streamed).
  • stderr — reasoning, tool activity (→ name, ← result) and the summary line \n[tokens: N in, M out] [session: <id>].

So you can safely redirect the result:

eonyx run "generate a README from the code" > README.draft.md

Headless runs are saved as sessions too, so the conversation can be continued:

eonyx run "start refactoring the parser"   # creates a session
eonyx run -c "now add tests"               # continues the most recent one
eonyx sessions                             # list: id, date, message count

For automation EONYX gives you machine-readable output and meaningful exit codes.

--json prints NDJSON (one JSON record per line) to stdout — convenient to parse in a pipeline:

eonyx run --json "fix the test" | jq -c 'select(.type=="tool_end")'

Event types: text, reasoning, tool_start (name, input), tool_end (name, result, is_error) and the final result (text, input_tokens, output_tokens, cached_tokens, session).

For CI it matters to tell outcomes apart — EONYX returns meaningful codes:

CodeMeaning
0success
1general error (config, provider, persistence)
2usage error (invalid arguments)
3API/model error during streaming
4task finished, but a tool was blocked by the gate
5hit the max_steps limit without a final answer
130interrupted (Ctrl-C)
eonyx run --yolo "run the linter and fix the findings" || echo "exit $?"
Codes 4 and 5 are not “just an error”
Code 4 means the agent wanted to perform an action but had no permission — add the tool to allowed_tools or run with --yolo. Code 5 means the task did not fit into max_steps; raise the limit in the config.
#!/usr/bin/env bash
set -euo pipefail
export DEEPSEEK_API_KEY="$CI_LLM_KEY"

eonyx run --yolo --json "run go test ./... and fix the failing tests" \
  | tee agent.log \
  | jq -r 'select(.type=="result") | .text'
Cheat sheet
  • Running it
    • eonyx run "…" — prompt from the arguments or stdin (pipe)
    • flags -m, --yolo, --system-prompt
  • Streams
    • stdout — only the model’s final answer
    • stderr — tool activity and token stats
  • --json
    • NDJSON events: text/reasoning/tool_start/tool_end/result
    • convenient to parse with jq
  • Exit codes
    • distinguish success from an API error (3)
    • permission block (4), max_steps limit (5)
  • Sessions
    • headless runs are saved
    • -c continues the most recent one, -s <id> a specific one