<!-- Markdown mirror. Canonical: https://empryo.com/docs/recipes/headless -->

# Run from CI or scripts

> Run Empryo without the UI in scripts and CI: pipe in a prompt, get JSON or live events, resume sessions, and fail a pipeline on a review verdict.

Headless mode runs the same agent with the same tools, but without the interface. Use it in shell scripts, git hooks, and CI pipelines.

## Run a prompt

```bash
# Prompt as an argument
empryo --headless "explain the auth middleware"

# Prompt from stdin
echo "find unused exports" | empryo --headless

# One JSON object with the result
empryo --headless --json "list all TODOs"

# One JSON event per line, while the run happens
empryo --headless --events "refactor the store"
```

The agent's answer goes to stdout. Progress lines go to stderr, so you can pipe the answer without extra noise.

## Useful flags

| Flag | What it does |
|------|--------------|
| `--model <id>` | Use a different model for this run |
| `--mode auto` | Let the agent run commands and edits that would normally need your approval |
| `--max-steps <n>` | Stop after this many steps |
| `--timeout <ms>` | Stop after this many milliseconds |
| `--system "..."` | Add your own instructions |
| `--include <file>` | Add a file to the context. Repeatable. |
| `--no-genome` | Skip the startup code scan. Faster for quick questions. |
| `--diff` | Print the changed files at the end |
| `--save-session` | Save the conversation so you can continue it later |
| `--session <id>` | Continue a saved session |
| `--chat` | Multi-turn chat over stdin |
| `--review [what to check]` | Review the changes and exit with the verdict, instead of running a prompt. See [review](/docs/agents/review). |
| `--quiet` | Hide the progress lines |

See the [CLI reference](/docs/reference/cli) for every flag.

## Approvals in headless mode

Nobody is there to click "approve", so by default Empryo **refuses** anything that would normally ask you first, such as destructive commands or writes outside the project folder. The agent is told the action was denied and carries on without it.

If the job needs those actions, and you trust the environment it runs in, use `--mode auto`:

```bash
empryo --headless --mode auto "update the lockfile and fix the build"
```

The [`approvals`](/docs/reference/configuration#behavior) settings are for the desktop app and the terminal UI. A headless run doesn't read them. Reading a web page never asks there, but work outside the project, destructive commands and requests that send data are refused unless you use `--mode auto`.

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | Success, or a PASS review |
| `1` | Error |
| `2` | Timed out |
| `3` | Review verdict: FAIL |
| `4` | Review verdict: PARTIAL |
| `5` | A computer-use step needs a person |
| `130` | Stopped with Ctrl+C |

## Fail a pipeline on a review

```bash
# 0 = PASS or nothing changed, 3 = FAIL, 4 = PARTIAL
empryo --headless --review "check the migration for data loss" --quiet
```

A fresh agent reads the changed files. It has not seen the conversation that made them, so it judges the code on its own. If nothing changed, it exits `0` without calling a model.

## CI example

```yaml
# .github/workflows/lint.yml
- name: Auto-fix lint
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    EMPRYO_NO_PROMPT: "1"
  run: |
    empryo --headless --max-steps 30 --timeout 180000 --diff \
      "run the linter, fix every issue, then typecheck"
```

## Continue a session

```bash
# Step 1: investigate and save
empryo --headless --save-session "analyze the auth module and list the issues"

# Copy the session id from the output, then:
empryo --headless --session <id> --save-session "now fix them"
empryo --headless --session <id> "write tests for the fixes"
```

## Read the event stream

```bash
empryo --headless --events "refactor the store" | while read -r line; do
  type=$(echo "$line" | jq -r '.type')
  case "$type" in
    tool-call) echo "→ $(echo "$line" | jq -r '.tool')" ;;
    done)      echo "done in $(echo "$line" | jq -r '.duration')ms" ;;
  esac
done
```

The main event types are `start`, `text`, `tool-call`, `tool-result`, `step`, `error`, `done`, and `session-saved`. Longer runs can also send events for warnings, compaction, helper agents, and review verdicts, so ignore types you do not handle.

## What is different from the interactive app

- No interface, editor panel, or splash screen.
- Actions that need approval are refused unless you use `--mode auto`.
- You cannot send messages to the agent during the run, except in `--chat` mode.

## Tips for CI

- Store API keys as CI secrets and pass them as environment variables.
- Always set `--max-steps` and `--timeout` so a stuck run cannot use up your budget.
- Set `EMPRYO_NO_GENOME=1` to skip the code scan on small jobs.
- Set `EMPRYO_NO_PROMPT=1` so the first-run add-on prompt never waits for input.
