Skip to content
DocsHaarness CLI
On this page

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.

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, 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:

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. --variant picks a reasoning variant for this turn.

Pipe in context

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

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:

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.

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

Get output a script can parse

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:

{"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:

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.

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.

    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:

    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 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 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.

Run a saved command or workflow

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

--command runs one of your slash commands with the text as its arguments. --workflow runs a workflow instead of a normal turn.

Keep going until a goal is met

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

--goal treats the prompt as a goal 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.

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

Use the strongest reasoning

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.

Run on another server

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.

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 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
--yolo Approve every permission request; see what it 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