> ## Documentation index
> Fetch the complete documentation index at: https://haarness.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a prompt from a script or CI

`haarness run` sends one prompt, waits for the agent to finish, and prints its reply. It does the
same work as the terminal UI, so you can use it in shell scripts, Git hooks and CI jobs.

**You need:** a model [connected](https://haarness.ai/docs/start/connect-a-provider.md).

```sh
haarness run "List the TODO comments in src/"
```

The reply is printed when the turn finishes. The command exits with status 0 when the turn
completes and non-zero when it fails. On a terminal, a `Working · 12s · Ctrl-C to stop` line
shows on stderr while you wait.

## Choose the model

Without `-m`, a new chat uses your default model: the `model` in your
[configuration](https://haarness.ai/docs/cli/configuration.md#set-your-default-model), or else the model the terminal UI
would pick from a provider you connected. A continued chat keeps its own model. If nothing is
connected, the run says how to connect a provider and starts no chat.

To choose, pass `-m provider/model`:

```sh
haarness run -m anthropic/claude-sonnet-5-5 "…"
haarness run --provider openai --model gpt-5.6 "…"    # the same idea, as two flags
haarness models                                       # what you can use
```

`--agent reviewer` runs the turn with another [agent profile](https://haarness.ai/docs/cli/agents-commands-skills.md).
`--variant` picks a reasoning variant for this turn.

## Pipe in context

When you pipe text in, it is added after the prompt:

```sh
git diff | haarness run "Review this change"
```

With no prompt argument, the piped text is the whole prompt.

With a prompt argument, Haarness reads piped text only if it is already waiting when the run
starts, so a pipe that never closes cannot hold the run. Output from a slow command may not be
there yet; save it to a file first and redirect it in:

```sh
cargo test > test-output.txt 2>&1
haarness run "Why do these tests fail?" < test-output.txt
```

## Attach files

Use `-f` once per file. PDFs, images, audio and short video work when the model supports them.

```sh
haarness run -m gemini/gemini-3.8-flash -f ./brief.pdf -f ./clip.mp4 "Compare these"
```

## Get output a script can parse

```sh
haarness run --format json "List the crates" \
  | jq -r 'select(.type=="assistant") | .text'
```

`--format json` prints one JSON object per line. Each has `type` (`assistant` or `reasoning`),
`text`, `threadID` and `turnID`. Use `threadID` to continue the chat later.

When a run fails, the last line is an error record, and the command exits non-zero:

```json
{"type":"error","threadID":"…","message":"Stopped: the agent asked to …"}
```

Other records you may see: `refused` (a request that `--auto` refused, with its `reason`),
`workflow` (a workflow run's result), and `background` or `background_subagent` (work still
running on the server when the command returns, with its `count`).

In plain-text output, the model's reasoning is printed too, under a `thinking:` line before the
reply (dimmed on a terminal). Add `--no-thinking` to print only the reply.

## Continue a chat

Each run starts a new chat unless you say otherwise:

```sh
haarness run --continue "now add tests"          # the last chat in this folder
haarness run --thread <id> "…"                   # a specific chat
haarness run --continue --fork "try another way" # a copy, leaving the original alone
haarness run --title "Nightly audit" "…"         # name a new chat
```

Chats belong to the folder you run in. See [Find and resume a chat](https://haarness.ai/docs/cli/sessions.md).

## Stop a run

Press **Ctrl-C**. Haarness stops the chat's turn on the server, with any helpers and workflows it
started, and the command exits with status 130. A second Ctrl-C exits at once. A cancelled CI job
that sends SIGTERM stops the run the same way, with status 143. Closing the terminal window a run
is attached to stops it too, with status 129.

Files keep whatever the agent had already written. Open the chat in the terminal UI and run
`/undo` to roll them back.

## Handle permission prompts in CI

Inside the project, the agent edits files and runs commands without asking. When it needs a
permission beyond that, for example to read a file outside the project:

- **At a terminal,** the run asks you there, even when you piped text in. Type `y` to allow it
  once; anything else refuses it. If you answer the same request in the terminal UI or Desktop
  first, the run carries on.

  ```text
  The agent asks to reach outside the project (/etc/hosts).
  Allow it once? [y/N]
  ```

- **With no terminal,** as in CI, the request is refused and the run stops with an error that
  says what was asked:

  ```text
  Error: Stopped: the agent asked to reach outside the project (/etc/hosts), and nobody can
  approve that in this run. Allow it by adding {"permission":{"external_directory":{"/etc/*":"allow"}}}
  to haarness.json in the project or ~/.config/haarness/haarness.json, or run again with --yolo to
  approve every request.
  ```

  The rule it names is the one an "always" answer would save, so you can copy it into your
  configuration.

To let an unattended run do what it needs, choose one:

- **Allow it with [permission rules](https://haarness.ai/docs/security/permissions.md)** in the project's
  configuration. This is the safer choice.
- **Pass `--auto`** to put the chat in **Auto**: a reviewer model approves routine requests once.
  With no terminal, each request it leaves to a person is refused, the agent is told why, and the
  run carries on; five refusals in a row stop it. A run that finished but refused something exits
  with status 3. The chat stays in Auto afterwards, in every app, until someone switches it back.
- **Pass `--yolo`** to approve every request in this run. Rules set to `deny` still block, and
  Haarness Cloud actions that spend money or delete something still ask: with no terminal, the
  run stops at one of those and says it "always needs an answer".

A website in [your Chrome](https://haarness.ai/docs/desktop/use-your-chrome.md) always needs a person: none of these
approve it, and a run with no terminal stops and names the site. All of them are permissions
inside Haarness only; none changes what the operating system allows.

If the agent asks you a question, the run declines it and carries on.

### What `--yolo` leaves running

Nothing that approves. `haarness run` uses the Haarness server for the folder and starts one if
none is running; that server keeps running after the command ends, but `--yolo` stays with the
run. It approves the run's requests once each, including its agents and workflows, and saves no
rule. The terminal UI, Desktop and your other runs in this folder keep asking.

Only `haarness serve --yolo` starts a server that approves for every chat; see
[Approve everything](https://haarness.ai/docs/security/permissions.md#approve-everything-yolo).

## Run a saved command or workflow

```sh
haarness run --command audit "the billing crate"
haarness run --workflow audit --workflow-arg scope=server
```

`--command` runs one of your [slash commands](https://haarness.ai/docs/cli/agents-commands-skills.md) with the text as
its arguments. `--workflow` runs a [workflow](https://haarness.ai/docs/cli/workflows.md) instead of a normal turn.

## Keep going until a goal is met

```sh
haarness run --goal "Migrate the tests to the new API and make them pass"
```

`--goal` treats the prompt as a [goal](https://haarness.ai/docs/tui/goals.md) and keeps working across turns. The
command exits with status 0 only if the goal is achieved; paused or blocked goals exit with an
error.

## Wait for background work

The agent can start helpers that run in the background. By default the command returns when the
main turn ends and says how many helpers are still running on the workspace server.

```sh
haarness run --wait-background "…"
haarness run --background-timeout-ms 600000 "…"   # wait at most 10 minutes
```

## Use the strongest reasoning

```sh
haarness run --ultracode "Audit every API boundary"
```

`--ultracode` uses the strongest reasoning the model offers for this run and lets the agent split
the work into parallel tasks. Keep it for hard problems. See
[Reasoning and ultracode](https://haarness.ai/docs/tui/reasoning.md).

## Run on another server

```sh
haarness run --attach http://build-box:4096 --password "$PASSWORD" "…"
```

For a server that uses a token, pass `--token` or set `HAARNESS_SERVER_TOKEN`. See
[Start a Haarness server](https://haarness.ai/docs/server/run-the-server.md).

## The short form

`haarness "prompt"` is the same as `haarness run "prompt"`. It only works when the prompt does not
contain `/`, does not start with `-`, and is not the name of a command or file. In scripts, write
`haarness run`.

## Keep CI out of the usage statistics

Runs report to the daily [usage statistics](https://haarness.ai/docs/cli/stats-and-insights.md#what-does-haarness-share-with-haarness-cloud)
like any other chat. Set `HAARNESS_TELEMETRY=0` in a CI job to leave it out.

## Reference

| Flag | Does |
| --- | --- |
| `-m, --model` | `provider/model`, or a model ID with `--provider` |
| `--agent`, `--variant` | Agent profile and reasoning variant |
| `-f, --file` | Attach a file (repeatable) |
| `--format json` | One JSON object per line, and an error record on failure |
| `--no-thinking` | Leave reasoning out of the output |
| `-c, --continue` / `-s, --thread` | Continue the last or a given chat |
| `--fork` | Copy that chat first |
| `--title` | Name the new chat |
| `--auto` | Put the chat in Auto; see [Handle permission prompts in CI](https://haarness.ai/docs/cli/headless-runs.md#handle-permission-prompts-in-ci) |
| `--yolo` | Approve every permission request; see [what it leaves running](https://haarness.ai/docs/cli/headless-runs.md#what-yolo-leaves-running) |
| `--command`, `--workflow` | Run a slash command or workflow |
| `--workflow-arg KEY=VALUE` | A workflow argument (repeatable) |
| `--goal` | Pursue the prompt as a goal |
| `--ultracode` | Strongest reasoning for this run |
| `--wait-background` | Wait for background helpers |
| `--background-timeout-ms` | Wait at most this long |
| `--attach` (or `--connect`), `--token`, `--username`, `--password` | Use another server |

| Exit status | Means |
| --- | --- |
| 0 | The turn completed |
| 1 | It failed, or stopped on a permission nobody could answer |
| 3 | It finished, but `--auto` refused at least one request |
| 129 | The terminal it ran in was closed |
| 130 | You pressed Ctrl-C |
| 143 | It received SIGTERM |

## Related

- [Schedule a recurring run on this computer](https://haarness.ai/docs/cli/schedules.md)
- [Permissions](https://haarness.ai/docs/security/permissions.md)
