---
title: "Run Ysra from a script"
description: "Run one task to the end with ysra exec, for scripts, CI and containers: choose the task and folder, output formats, approvals, limits and exit codes."
new_in: "3.0"
---

`ysra exec` runs one task in a folder from start to finish and exits, with no
screen and nothing to type. It is for scripts, CI jobs and containers;
`ysra -p` is the same command. This page covers the options,
what is printed, the exit codes and signing in; every option is also in the
[command reference](commands.md#options-for-ysra-exec).

```console
$ ysra exec "add a --verbose flag to the CLI and test it"
```

## The rule

A run leaves its work as changes in the folder’s files. Unless `--allow`
names them, it never creates a branch, commits, pushes, publishes or deploys,
and it never creates `.git` in a folder without Git. Anything that would need
a person either stops the run with exit `2` or, with `--approval auto`, is
decided by the rules below.

## Give the task and the folder

| To | Write |
|---|---|
| Give a task | `ysra exec "<task>"` |
| Give a task that starts with a dash | `ysra exec -- "-v prints nothing: fix it"` |
| Send input with the task | `git diff | ysra exec "review this change"` |
| Make the piped input the task | `… | ysra exec -` (or no task at all) |
| Work in another folder | `-C <dir>` or `--cd <dir>`: with Git or without; nothing is set up in it. Paths in the task are read against it |
| Go on in this folder’s last thread | `--continue` |
| Go on in a given thread | `--resume <thread>` |

```console
$ ysra exec -C ./service --output-format json "why does the build fail?"
```

## What is printed

| `--output-format` | What it prints |
|---|---|
| `text` (default) | Ysra’s last message, on standard output. When standard error is a terminal, each step shows there as it happens |
| `json` | One JSON object at the end: the result |
| `stream-json` | One JSON object per line as Ysra works, each with its time (`ts`), ending with the result |

`--trace <file.jsonl>` also writes every raw event the CLI receives to that
file and keeps the local transcript (`~/.ysra/transcript-….jsonl`). Where the
API offers it, the server’s record of the run is saved beside it as
`<file>.server.json`, at most `--trace-max-mb` (default 512 MB). An API without it, or a record refused or cut short, is noted in the
result, never a failure.

<!-- media: cli-exec-stream | A stream-json run ending in its result line | In a container, run `ysra exec --approval auto --output-format stream-json -C /app "make the tests pass"` on a small Node project with one failing test; record the event lines scrolling and the final result line with exit_code 0 -->

### The result

The result object ends `json` and `stream-json` output.

| Field | What it holds |
|---|---|
| `type` | Always `result` |
| `status` | `done`, `unverified`, `failed`, `refused`, `needs_approval`, `needs_input`, `out_of_credits`, `account_paused`, `interrupted`, `timeout`, `max_turns` or `stop_unconfirmed` |
| `pause_reason` | Why the thread is paused, when it is: `unverified`, `interrupted`, `out_of_budget` (the credit limit was reached), `cancelled`, `rewound`. Otherwise `null` |
| `exit_code` | The exit code, as in [Exit codes](#exit-codes) |
| `thread` | The thread’s id, to continue with `--resume`. Empty when the run never started |
| `result` | Ysra’s last message |
| `summary` | A summary of the work, when there is one |
| `question` | What Ysra needs, when it stopped for a decision or an answer |
| `credits_used` | Credits this run used |
| `decisions` | Each question met and the answer taken, with `--approval auto` |
| `errors` | What went wrong, if anything |
| `review`, `review_rounds` | With `--review mayz` (and `ysra mayz --print`): the last Mayz report, and how many reviews ran |
| `limit`, `stop`, `turns`, `max_turns_enforced_by` | With `--timeout` or `--max-turns`: what ended the run, whether the server confirmed the work stopped (`confirmed`, `unknown`, `not_live`), the model calls made, and whether the server or only this client enforced the turn limit |
| `computer_backups` | With `--allow-computer-writes`: each file overwritten and the copy kept first |
| `trace` | With `--trace`: where the run was kept |
| `notes` | Things worth knowing about how the run was done |

### The events

With `stream-json`, each line has a `type`. Only what a person would see on
screen is printed.

| `type` | What it is |
|---|---|
| `start` | The run started: the thread, the folder, the approval setting, and whether it continues a thread |
| `message` | Something Ysra says |
| `step` | Something Ysra did, and whether it failed |
| `note` | A status line, with its tone |
| `plan` | Ysra’s plan, as numbered steps |
| `question` | A question Ysra asks, with its choices when there are some |
| `decision` | A question and the answer taken for it |
| `error` | An error |
| `result` | The last line: the result above |

## Who decides when nobody is there { #approvals }

| Setting | When Ysra would ask |
|---|---|
| `--approval ask` (default) | The run stops at the first decision, leaves it open, prints the question and exits `2`. With `--approve-plan`, a plan is accepted instead of stopping there |
| `--approval auto` | Ysra goes ahead with what the work in the folder needs: plans, commands, installs, Docker for this thread, deleting files in the folder. It declines what reaches outside the folder: commits, pull requests, pushes, publishes, deploys and remote machines. On Windows, commands that delete never run |
| `--approval read-only` | Ysra only looks: nothing in the folder is changed, installed or deleted. Use it for questions, reviews and reports |
| `--approval auto --allow <kinds>` | The same, and also goes ahead with the kinds named: `commit`, `push`, `deliver` (a draft pull request), `deploy`, `publish`, `undo`. Interrupting the work, remote machines and the whole computer stay off |

What `--approval auto` needs, and what it still won’t do:

| Situation | What happens |
|---|---|
| Not in a container, no `--yes-i-am-sandboxed` | `--approval auto` is refused. Add `--yes-i-am-sandboxed` on a disposable machine (a VM, a CI runner) |
| A command would run the project’s code (tests, scripts, builds) uncontained, without `--yes-i-am-sandboxed` | The run stops with exit `2` |
| A question needs a typed answer, or a sign-in only a person can do | Not guessed: the run stops with that question |
| A stop that asks nothing, or whose only choice is **Carry on** | Continued, a few times at most |
| Running as root (or with password-free `sudo`) in a container or with `--yes-i-am-sandboxed` | Missing packages are installed with `apt-get`, `apk`, `dnf` or `yum`, without prompts; Git too |
| Git is missing otherwise | The run stops before it starts and says what to install |
| `ysra` started as root or an elevated Administrator | Refused, unless `YSRA_ALLOW_ROOT=1` (a container made for the run) |
| An organisation policy file turns an option off | That option is refused; nothing on the command line turns it back on. See [Environment](commands.md#environment) |

```console
$ ysra exec --approval auto --output-format stream-json -C /app "make the tests pass"
$ ysra exec --approval auto --yes-i-am-sandboxed --allow commit,push "fix it and push"
```

### The home folder or the whole disk

`--allow-home` lets the folder be the home folder or the top of the disk,
where the service offers it. There, commands only read and can't reach the
network; where that isn't possible, commands are refused. With nobody at the
keyboard only plain looks run; anything else stops the run with exit `2`.
Commands never write there.

`--allow-computer-writes <path>,…` adds file creates and edits through Ysra’s
own write tool, inside those paths only, with `--approval auto`:

| Rule | Value |
|---|---|
| Paths | Absolute, inside your home folder |
| Per write | At most 20 files, 5 MB a file |
| Backups | Each edited file is copied first to `~/.ysra/computer-backups/<run>/` |
| Never | Deletes, moves, installs, upgrades or permission changes; keys, credentials, shell start-up files and system places, even when listed |
| With `--approval ask` | Every write still asks |

## Limits on a run

| Option | What happens when it is reached |
|---|---|
| `--max-credits <n>` (1 to 50,000) | Ysra stops before the thread uses more than n credits; exit `3` |
| `--timeout <dur>` (`90s`, `15m`, `2h`, `1h30m`; a plain number is seconds) | The work is interrupted at its next safe point and kept; exit `8` |
| `--max-turns <n>` | Once the thread has made n model calls in this run, the work is interrupted at its next safe point and kept; exit `9` |
| Either limit, and the server doesn’t confirm in time that the work stopped | Exit `10`, status `stop_unconfirmed`: the work may still be running on the server |

## Have Mayz review the work

`--review mayz` has [Mayz](../developer/mayz.md) review the work in the same
thread when it is done. While Mayz says it needs work (or is unsafe), Ysra
fixes the findings and Mayz reviews again, up to `--review-rounds` reviews in
all (1 to 5, default 2: a review, the fixes, a review of the fixes). The exit
code then carries the last verdict (`5`, `6` or `7`).

```console
$ ysra exec --approval auto --review mayz --timeout 30m --trace run.jsonl "build the API"
```

## Model and effort

| Option | What it does |
|---|---|
| `--mode spark|forge|summit` | A [Code mode](../developer/modes.md): Spark for small, clear jobs, Forge for everyday building (the default), Summit for large or tricky work. `--model` and `--effort` win over it |
| `--model <name>` | Use this model. An unknown one stops the run: “There’s no model called … Choose one of: …” |
| `--effort quick|balanced|thorough` | How hard Ysra thinks (`low`, `medium`, `high` work too). Default: your setting |

## Answer and continue

When a run stops for a decision or an answer, the text output ends with the
question and the command to answer it:

```console
$ ysra exec "upgrade the date library"
…
Ysra needs a decision: Run the migration on the test database?
Answer with: ysra exec --resume 5f0c… "<your answer>"
$ ysra exec --resume 5f0c… "yes, go ahead with the plan"
```

## Exit codes

| Code | Status | What it means | What to do |
|---|---|---|---|
| `0` | `done` | Done, and every check passed. With `--review mayz`: Mayz says ready | Review the changes and keep them |
| `1` | `failed`, `interrupted` | Failed, or stopped with Ctrl+C. Also: the folder couldn’t be connected, the place to run it wasn’t ready, the device was unavailable, or Git is missing; the reason is printed | Read `errors`; fix the cause and run again |
| `2` | `needs_approval`, `needs_input` | Needs an approval or an answer. The question is printed | [Answer and continue](#answer-and-continue) with `--resume` |
| `3` | `out_of_credits`, `account_paused` | Out of credits, or the account is paused | See [When credits run out](../workspace/credits.md#when-credits-run-out) |
| `4` | `refused` | The task was declined | See [When a session stops by itself](../developer/steering.md#when-a-session-stops-by-itself) |
| `5` | `done` or `unverified` | `--review mayz`: Mayz still says it needs work after the last review | Read `review`; run again with `--resume` or more `--review-rounds` |
| `6` | `done` or `unverified` | `--review mayz`: Mayz says it’s unsafe after the last review | Read `review` before using the change |
| `7` | `done` or `unverified` | `--review mayz`: the review didn’t finish, or no report came | Run the review again |
| `8` | `timeout` | `--timeout` was reached; the work is kept | Continue with `--resume`, or allow more time |
| `9` | `max_turns` | `--max-turns` was reached; the work is kept | Continue with `--resume`, or allow more turns |
| `10` | `stop_unconfirmed` | A limit was reached but the server didn’t confirm the work stopped | Check the thread before starting another run on the same folder |
| `11` | `unverified` | Finished and the changes are written, but not every check finished. The result says which | Check what is unproven before keeping the change. CLIs before 1.12 returned `0` here |
| `12` | `folder_missing` | The thread’s folder has been deleted or moved: “That folder isn't there any more.” | Run again with `-C` pointing at the folder where it is now |

```console
$ ysra exec --output-format json "update the changelog" > result.json
$ echo $?
0
```

## Sign in from a script

Run `ysra login` once on a computer you use yourself. In CI, a container or
any place without a browser, set `FORGE_API_TOKEN` to a Ysra API token
instead: nothing is saved on that computer.

```console
$ FORGE_API_TOKEN=… ysra exec "make the tests pass"
```

The token is used only to reach Ysra. The commands a run starts don’t get it
in their environment.

### A token from this computer’s sign-in

`ysra auth token` prints a short-lived access token from this computer’s
sign-in, and nothing else, for a program that runs Ysra where you aren’t
signed in, such as a container. Sign in once with `ysra login`, or
`ysra login --device` on a machine without a browser.

```console
$ FORGE_API_TOKEN="$(ysra auth token)" ysra exec -C /work "fix the build"
```

| Rule | Value |
|---|---|
| Lifetime of the printed token | At least 30 more minutes (Ysra refreshes the sign-in first when needed) |
| What is printed | Only the short-lived token; the refresh token stays on this computer |
| Not signed in | Exit `1`, with the reason |

## If it doesn’t work

| Symptom | Likely cause | What to do |
|---|---|---|
| `--approval auto` is refused | Not in a container | Add `--yes-i-am-sandboxed` on a disposable machine only |
| Exit `2` at a test or build command with `--approval auto` | Project code would run uncontained | Run in a container, or add `--yes-i-am-sandboxed` |
| Exit `1`, “needs Git on this machine” | Git is missing | Install Git (`apt-get install -y git`) and run again |
| Exit `1` before a `start` event | Not signed in, or the token expired | `ysra login`, or pass a fresh `FORGE_API_TOKEN` |
| Exit `10` | The server didn’t confirm the stop | Open the thread and check it has stopped |

## Related

- [Command reference](commands.md#options-for-ysra-exec) — every option of `ysra exec` in one table.
- [How a Code thread works](../developer/how-a-thread-works.md) — the task states behind `done`, `unverified` and the rest.
- [Mayz](../developer/mayz.md) — what a review checks and how verdicts are reached.
