# CLI and tool reference

The `yaag` CLI and the yaag extension start the same Runs. The CLI is for a
terminal; the tools are for a pi session.

## Usage

<!-- embed: @yaag/cli/src/argv.ts#USAGE -->

```text
usage:
  yaag run <program.ts> [--quiet] [--args <json>] [--profile <id>] [--run-id <id>] [--record <file>] [--replay <file>] [--resume <file>] [--answers <json>|@<file>] [--events-fd <n>] [--config <file>] [--no-config] [--host-session <file>] [--host-tool-call <id>]
  yaag run --eval <source> [--quiet] [--args <json>] [--profile <id>] [--run-id <id>] [--record <file>] [--replay <file>] [--resume <file>] [--answers <json>|@<file>] [--events-fd <n>] [--config <file>] [--no-config] [--host-session <file>] [--host-tool-call <id>]
  yaag run --eval-fd <n> [--quiet] [--args <json>] [--profile <id>] [--run-id <id>] [--record <file>] [--replay <file>] [--resume <file>] [--answers <json>|@<file>] [--events-fd <n>] [--config <file>] [--no-config] [--host-session <file>] [--host-tool-call <id>]
  yaag describe <program.ts> [--export <name>] [--registered-name <name>]
  yaag catalog [--kind program|agent] [--pattern <glob>] [--dir <path>]
  yaag setup-workspace [dir]

--eval runs an Orchestration Program that you give as source text. Give a
program file or --eval, but do not give both. A program that --eval runs can
import "@yaag/runtime" and "typebox" only. A program that imports other modules
must be a file.

--eval-fd reads the program source from descriptor <n>, and needs the writer to
close that descriptor. It keeps the source out of the process argument list,
where every local user can read it. The yaag extension always uses it.

--config <file> reads one more config file for this Run. Give it one time only.
yaag reads it after the global config and after the project config.

--no-config tells yaag to ignore the global config and the project config. It
does not ignore --config. yaag still finds the Program Directory, because
$yaag.project needs it.

--profile <id> selects the Profile of this Run by id. It outranks the profile
key of a config file. An id the program does not declare stops the Run before
it starts.

--run-id <id> names the Run id that the caller made. yaag records the id, and
the Control Channel of the Run follows it (ADR-0054). The yaag extension gives
it for each Run it starts.

--answers <json> gives the answers of a --resume Run. The JSON is an object of
text values, and each key is the id of a question the Cassette holds. Write
--answers @<file> to read the same JSON from a file. An answer that names no
open question stops the Run. A question you do not answer stays open, and the
Run pauses on it again.

A Run that pauses writes a paused document on stdout, and exits with code 3.
The document holds v, outcome, the questions, the checkpoint path, and the
resume fields: run the same program again with --resume <file> and --answers.
On a terminal, yaag writes each question on stderr, reads one answer for each
question from stdin, and resumes the Run in the same process; a blank line
leaves that question open. Ctrl-C drops the answers, writes the paused document
and exits 3.

A --resume or --replay Run also needs the program: give the program file,
--eval <source>, or --eval-fd <n>. A Cassette holds the history of a Run, and never the program
to run.

A Cassette is gzip-compressed JSON: read one with zcat <file> | jq. yaag also
reads the plain JSON that an older yaag wrote.

catalog lists the Orchestration Programs yaag finds — scanned in a Program
Directory and registered in a registry file — and the registered Agent
Definitions. --kind program covers both program kinds; --kind agent covers the
registered Agent Definitions. --pattern is a substring, or a glob when it holds
* or ?; it is never a regular expression. --dir narrows to one Program
Directory, and it never lists a global entry. A broken entry is listed with a !
flag; catalog exits 0 when the read succeeded.

describe --export <name> reads that named export as an Agent Definition
instead of the default export. It writes the declared name of the Definition.
--registered-name <name> gives the name a registry file entry states; a
different declared name is reported as a deviation. An export that is absent, a
module that throws on import, and an export that defineAgent did not make are
each reported in the output, and describe still exits 0.

Warning: describe imports the module and executes its top level. Keep program module top level side-effect free. A Catalog Resolve — await $yaag.<scope>.importAgent(name) — is the one permitted top-level await: describe is meant to run it.
```

A bad invocation prints this usage text on stderr and exits with code 2. Those
argv errors have no troubleshooting entry: the usage text is the fix.

The `--config` and `--no-config` flags select the config layers of a Run. See
[Configuration](configuration.md#locations). The `--profile <id>` flag selects
the Profile of a Run; it outranks the `profile` key of a config file.

`--host-session <file>` names the pi session file the Run starts from, and
`--host-tool-call <id>` names the id of the `yaag_run` call in it. On `run`,
give both: one without the other is an argv error (`--host-session needs
--host-tool-call`, `--host-tool-call needs --host-session`) that prints the
usage text and exits 2. yaag copies the file, closes that call in the copy, and
gives the program `ctx.host`. Your own file is never written, and the copy is
removed when the Run settles; a killed CLI can leave it behind.

On `--replay` and `--resume`, `--host-session` alone is legal: yaag hashes the
file and compares it with the recorded Run. A different conversation is a
replay Divergence on `hostHash`. `--host-tool-call` is accepted and ignored
there. With no `--host-session`, a replay or a resume takes the recorded hash,
so `ctx.host` still exists and there is no Divergence.

[`HOST_SESSION_UNUSABLE`](troubleshooting.md#host_session_unusable) stops the
Run before it starts when the file is missing, unreadable, empty, has no
session header, holds a line that is no session entry, or holds no entry with
that tool call id. The message names the reason and the path (`host session
<path> is unusable: <reason>`). For the program side, read [A Host Fork of your
pi session](authoring.md#a-host-fork-of-your-pi-session).

## Paused runs

A Run that `ctx.askUser` or `ctx.pause` suspended exits with code 3. It writes
the paused document on stdout when stdin is not a terminal; the log lines stay
on stderr, so `yaag run p.ts < /dev/null | jq` reads the document alone. On a
terminal stdin, yaag prompts instead. This is the shape:

<!-- embed: @yaag/cli/src/terminal/paused-document.ts -->

```ts
export interface PausedDocument {
  readonly v: 1;
  readonly outcome: "paused";
  /** The reason the first `ctx.pause` gave, when it gave one. */
  readonly reason?: string;
  readonly questions: readonly PausedQuestion[];
  /** Path of the published Checkpoint the Run resumes from (ADR-0021). */
  readonly checkpoint: string;
  readonly resume: PausedResume;
  /** Drain and summarizer warnings; they never change the outcome. */
  readonly warnings?: readonly string[];
}
```

Each optional field is absent, and never null: `reason` is absent when no
`ctx.pause` gave one, `agent` is absent for a question the program asked, and
`resume.file` is absent for a program that `--eval` or `--eval-fd` ran.
`questions` is `[]` for a plain `ctx.pause`. `resume.answersTemplate` holds one
empty answer for each open question: fill it in and give it to `--answers`.

`warnings` holds one line for each Ask that failed while the pause drained,
and one for each question summary that failed. A warning reports what went
wrong beside the outcome, and it never changes the outcome.

A resume that yaag runs in the same process keeps the run id of the Run it
continues. A resume that the pi extension starts is a new Run, with a new
`rN` id that names its predecessor.

On a terminal, yaag prompts instead. It writes each question on stderr with the
Agent name (or `[program]`), the summary or the context, and the question text,
then reads one answer for each question from stdin. A blank line leaves that
question open. A plain `ctx.pause` shows its reason and `press Enter to resume
now, Ctrl-C to exit paused`. yaag then resumes the Run in the same process.
Ctrl-C drops the answers of that prompt. Ctrl-C, the end of the input before
the first answer, and an answer set that answers nothing each write the paused
document and exit 3. See `--answers` in the usage text above.

Note: a plain `ctx.pause` does not resolve on a resume yet, so `press Enter to
resume now` runs the program again and pauses at the same step. Answer a
`ctx.askUser` question to make progress.

## Describe

`yaag describe <program.ts>` writes one JSON line: `name`, `description`,
`args`, and `profiles`, which is `null` without Profiles. The `yaag_describe`
tool renders that line as text: the name, the description, one `args:` row for
each argument (type, `(required)`, description), then `profiles:` with the
default, the Roles, and each Profile's candidates. An absent field is `(none)`.
`yaag describe <file> --export <name>`, and the `yaag_describe` tool with
`exportName`, read one export as an Agent Definition and write another shape:
`file`, `exportName` and `declared.name`. `--registered-name <name>` compares it
with a registry file's name; the tool reads that entry itself. When they differ,
the output holds `deviates` with both values, and the tool block flags
`(deviates)` and prints them. No entry, disagreeing entries, or an unreadable
registry file means no comparison, never a failure.

When yaag cannot read the Definition, or the export is a Profiled Definition,
the output holds one `finding` code:

| Code | Meaning |
|---|---|
| `EXPORT_NOT_FOUND` | The module has no export with that name. |
| `MODULE_IMPORT_THREW` | The import of the module threw. The detail is one line. |
| `NOT_AN_AGENT_DEFINITION` | The export is not a value that `defineAgent` made. |
| `PROFILED_DEFINITION` | The export is a Profiled Definition that `useProfile` made. It has no metadata until a Profile is active. |

A finding is a result, not a failure: describe writes it on stdout and exits 0,
and the tool call still succeeds. Only a bad invocation exits 2. See
[Catalog and registry files](configuration.md#catalog-and-registry-files).

## Catalog

`yaag catalog` and the `yaag_catalog` tool list the Orchestration Programs —
scanned in a Program Directory and registered in a registry file — and the
registered Agent Definitions, from one contract. The CLI writes one line for
each entry: the kind, the name, the layer (`project` or `global`), the file
path, then `export=`, `tags=` and `origin=`, then the flags, then `-- ` and the
description; a registered program row shows its curated description and tags,
and it prints no `export=`. The tool writes a count and then the same fields in
one block. `origin=`, and the source of a block, name the registry file or the
Program Directory of the row, which tells two rows of one name apart.

A problem is a flag, never a failure: `!missing-file` or `(file missing)` for an
absent file, `!invalid: <message>` or `(broken: …)` for an entry that is not
valid, and a line of its own — the `problems:` region of the tool — for a
registry file that yaag cannot read at all, which a narrowed query keeps. `yaag
catalog` exits 0 when the read itself was good. See [Catalog and registry
files](configuration.md#catalog-and-registry-files).

`yaag catalog` reads the nearest Program Directory at or above the working
directory. When no directory there holds a `.yaag` directory, the command lists
the entries of the global registry file only. `--dir` names one directory
instead, and it does not have to hold a `.yaag` directory: the command lists the
`.ts` files there as programs. A directory that yaag cannot read is reported the
same way an unreadable registry file is. `yaag catalog` reads the global
registry file in each case, and it reports a file that it cannot read even for a
narrowed query.

## The /yaag menu

`/yaag` is the only yaag command. It opens the menu in the TUI. In a client
without a TUI it gives a notification only.

The root level has three entries: **Runs**, **Status**, and **Settings**.

- **Runs** shows the Runs of this session. Select a Run to open its
  interactive tree as the next level. When the session has no Run, the screen
  shows "No Runs in this session." and tells you to start one with `yaag_run`.
- **Status** shows the resolved `bun` executable and the CLI entry point, then
  the counts of the live, the settled, and the restored Runs. Under the counts
  it shows three sections.
  - **Paths** shows the runs directory, the checkpoints directory, the global
    config file, and one project config file for each Program Directory in
    pi's working directory. A path that is not on disk gets the tag
    `(missing)`. When there is no Program Directory, the section says
    `No Program Directory under <directory>.` yaag looks for the Program
    Directories again each time you open the screen: the section shows
    `Loading…`, then the project config files. A new one shows at the next open.
  - **Programs** shows the name of each program in the Catalog. **Agents**
    shows the name of each registered Agent Definition. A row from the global
    registry file gets the tag `(global)`. yaag reads the Catalog again each
    time you open the screen: the lists show `Loading…`, then the screen draws
    them again. An empty list shows `No programs.` or `No agents.`
  - When the Catalog has broken entries, one line gives their count and tells
    you to run `yaag_catalog` for the details.
- **Settings** has two entries. **Show cost and tokens** is a toggle, off by
  default. When it is on, every Run view shows the cost of the Run in its
  header, and the cost and the tokens of each Agent and nested node in its row.
  It applies to the open views at once, and yaag keeps it in
  `view-settings.json` beside the global config file for the next session.
  **Setup workspace** does the same setup as `yaag_setup_workspace`, in pi's
  current directory, and shows the report in the pane.

Each frame has a header with the path, for example `yaag (v0.16.1) › Runs › r3`.
The root segment names the installed version of `@yaag/extension`. A rule
above the header and a rule at the bottom bound the frame. The `→` cursor marks
the selected entry.

| Key | Result |
|---|---|
| `esc` | Goes up one level. At the root level it closes the menu. |
| `ctrl+c` | Closes the menu from any level. |
| `ctrl+q`, `m` | In the Run tree, each key opens the Run Control Menu. The menu lists only the actions that the Run permits at that moment: Pause, Stop, Resume or Reattach, and Cancel. A Run that permits no action shows no menu and no `[m]` hint. |
| `r` | In the Run tree of a settled Run, shows the Result region. A second press hides it. |

### The Run tree

Press `enter` on a Run tree row for the three read-only actions of that node:
**Open transcript**, **Show system prompt**, and **Copy session path**. `↑` and
`↓` scroll a panel, `esc` closes it, and the system prompt shows as markdown.

### The Run Control Menu

`ctrl+q` and `m` in the Run tree, and `m` in the Runs list, open the same menu.
The menu lists only the actions that the Run permits at that moment.

| Run state | Rows |
|---|---|
| live, this session | Pause, Stop, Cancel |
| live orphan, the record names a Control Channel | Pause, Stop, Reattach, Cancel |
| live orphan, no Control Channel in the record | Stop, Cancel |
| a Resumable Run of this session | Resume, Cancel |
| a Resumable Run of another session | Reattach, Cancel |
| terminal, not resumable | no menu |

**Pause.** The menu pauses with the default deadline of 60 seconds. Only
`yaag_pause` takes a `deadlineMs`. After the pause the header reads
`rN · paused`, and the menu then offers Resume.

**Reattach.** Reattach is one gesture. yaag pauses the Run at a Checkpoint, and
then starts a new Run from that Checkpoint. The new Run belongs to this
session, it gets a new id, and it reports its result to this session as a
follow-up. While the pause runs, the header reads `rN · pausing…`, and the tree
takes no other key. The new tree heads with `r7 (resumed from r3)`, and the old
row in the Runs list shows `→ r7`. One Checkpoint resumes one time. Reattach
never changes the arguments, the config, or the Profile of the Run. The row
says `Resume` for a Run of this session, and `Reattach` for a Run of another
session.

**A failed pause.** The tree stays on the old Run and shows the reason in the
footer. yaag never falls back to `SIGKILL` here. See
[A pause was refused](troubleshooting.md#a-pause-was-refused).

**A Run parked on a Question** resumes without an answer. The new Run parks on
the same Question, and you answer it by the ordinary path. `--answers` answers
it instead: yaag matches each answer to the question of that id, writes the
answer of an Agent question back into the session of the Agent, and continues
the Agent with a short prompt. The same answer a second time changes nothing; a
different answer for the same question stops the Run.

`esc` at the root level is the reliable exit. `ctrl+c` is a help only: pi gives
`ctrl+c` to `app.clear` at app level, and can thus take the byte first.

A live Run continues when you leave the menu or close it.

The Run tree of the menu fits short terminals. The tree band shows at most ten
rows and at least five. `↑` and `↓` move the selection. When the selection
crosses an edge of the band, the band scrolls by one row. The rule line below
the tree states how many rows are hidden above and below it, for example
`─── ↑3 · ↓12 ───`. No key scrolls the band.

The Result of a settled Run is hidden when the tree opens. Press `r` to show
it. A shown Result takes the rows that the tree band leaves, and a long Result
is cut with a tail marker that states the count of the missing lines, for
example `… 42 more lines`. The Result of a live Run, of a restored Run, and of
the tool block in the transcript is always shown: only the interactive trees
hide it.

The same rules hold for the alt-screen tree of `yaag run` on a terminal.
The final frame on stderr, after the alt-screen closes, always shows the full
Result.

The commands `/yaag-status` and `/yaag-setup-workspace` are removed. They are
now the Status screen and the Settings screen of this menu.

## Tools

### yaag_run

Runs an Orchestration Program. It maps to `yaag run`.

| field | maps to | notes |
|---|---|---|
| `file` | `yaag run <program.ts>` | Path of the program file. |
| `script` | `--eval-fd` | Inline source. It can import `@yaag/runtime` and `typebox` only. |
| `args` | `--args <json>` | A JSON object string. |
| `profile` | `--profile <id>` | Selects the Profile of this Run by id. It outranks the `profile` key of a config file. An id the program does not declare stops the Run at the start. |
| `background` | none | Extension only. Starts the Run and returns its Run id. |
| `record` | `--record <file>` | Writes the Cassette here. The Cassette is gzip-compressed JSON; to read one, use `zcat <file> \| jq`. yaag makes a missing directory of this path, and stops the Run at the start when it cannot. A completed Run whose Cassette cannot be written keeps its result and reports the loss. |
| `resume` | `--resume <file>` | Replays matching Asks, then continues live. |
| `answers` | `--answers <json>` | Answers of a resume, keyed by question id. Give it with `resume`. |
| `config` | `--config <file>` | Reads one more config file for this Run. |
| `noConfig` | `--no-config` | Ignores the global config and the project config. yaag still finds the Program Directory, because `$yaag.project` needs it. |
| `forkHost` | `--host-session <file>` `--host-tool-call <id>` | Gives the Run the Host Session's conversation as a fork source. The program reads it as `ctx.host`. yaag drops it when the call gives `resume`. A session with no session file gets a `HOST_SESSION_UNUSABLE` error and no Run. |

### The Run block in a pi session

A `yaag_run` call is two lines in the transcript. The first line reads
`yaag_run(inline:review)  ... (ctrl+o to expand)`, and the second reads
`r1 · running 1m14s`.

The first line is the call. A file Run shows the path of its program. An inline
Run shows `inline`, or `inline:<name>` when the Run told yaag the name that the
program declares. The name comes from the Run, not from the source text.

The second line is the state of the Run: its Run id, its status, and the time.
The five statuses are:

| status | meaning |
|---|---|
| `running` | The Run is live and the call waits for it. |
| `started` | The Run is in the background and the call returned. |
| `finished` | The Run ended and its program completed. |
| `failed` | The Run failed, or the call itself failed. |
| `interrupted` | The Run stopped, paused, or died before its program ended. |

The hint at the end of the call line names the key that expands the block.
pi binds `ctrl+o` by default. The hint is not shown when the key is not bound.

The time of a Run that did not end is the time from its start to the last draw
of the block. The time of a Run that ended is the time the Run took.

The expanded block shows each parameter that the call gives, one on each line,
and then the full Run tree. A parameter that the call does not give is not
shown. `script` shows the full program source. A very large source is cut, and
the block then shows the marker `… (truncated, N bytes total)`. The error
output of a failed Run is also shown in the expanded block only.

Each Agent row of the Run tree shows one time: `active`. It is the sum of the
times of the Asks of the Agent, and it includes the Ask that runs now. It stops
when the Agent exits. The time moves each second in the interactive Run tree of
[the /yaag menu](cli.md#the-yaag-menu) and in the inline frame of a blocking
Run. In the tool block of the transcript it moves when a new progress frame
comes in. The time stops when the Run ends.

The Run Summary keeps the active time of each Agent, so a tree that yaag builds
from a stored Run — the tree of `yaag_status`, and the tree of a Run that ended
before the view opened — shows the same time as the live tree.

The cost of the Run and the cost and the tokens of each Agent are not shown by
default. Turn them on with **Show cost and tokens** in
[the Settings screen](cli.md#the-yaag-menu). `yaag_status` follows the setting.

### A Run that pauses

A Run that `ctx.askUser` or `ctx.pause` suspended ends the `yaag_run` call. What
you see depends on the Host Session.

A blocking call in a session with a user interface asks each question itself.
yaag then starts the resume with your answers. It repeats this until the Run
ends, so the model reads the result of the Run alone.

#### The ask-user popup

A session with a terminal shows one popup for the whole pause. Its title is
`Run <id> paused — <n> questions`, and it shows one block for each question:

- `[1] planner` — the number of the question and the Agent that asked it. A
  question of the program shows `program`.
- the line that tells you why the program asks. It is the Question Summary, or
  the context of the question. A question with neither shows no such line.
- `Q: <question>` — the question.
- the answer field. A `❯` marks the field you type in.

The line that tells you why the program asks, and the question, show as
markdown: headings, lists, code blocks, tables and emphasis show as structure,
not as markers. The answer field shows your text exactly as you type it.

These keys control the popup:

| key | function |
|---|---|
| `↵` | Go to the next field. On the last field, send the answers. |
| `ctrl+s` | Send the answers from any field. |
| `esc` | Cancel the round. The call then reports the pause. |
| `tab` / `shift+tab` | Go to the next or to the previous field. |
| `↑` / `↓` | Scroll the popup. |

A blank field is not an answer: yaag does not send it, and the Run pauses on
that question again. Long text wraps to the width of the popup.

A session with no terminal, such as an RPC client, shows one input dialog for
each question, in the order the program asked. The title of the dialog is
`[<agent>] <summary>`, and the question is on the line below it. `esc` in a
dialog cancels the whole round.

A blocking call in a session with no user interface reports the pause as text:
the Checkpoint path, each open question with its summary, and the fields of the
resume call.

The resume fields carry one blank answer for each question that is still open.
Give each blank a value before you resume: a blank answer is an empty answer,
and its question does not come back.

A background Run reports the pause in one follow-up message with the same three
things. Answer it with `yaag_run({ resume: "<checkpoint>", answers: { … } })`.

One Checkpoint resumes one time. Each resume is a new Run with a new Run id, and
the record of the new Run names the Run it resumed. A `ctx.pause` that asks no
question never resumes by itself: yaag reports its reason and waits for you.

### yaag_status

Reports the Runs of the session, or one Run.

| field | maps to | notes |
|---|---|---|
| `id` | none | Extension only. Omit it for every Run of the session. |

### yaag_stop

Stops a background Run and reaps its Agents.

| field | maps to | notes |
|---|---|---|
| `id` | none | Extension only. The Run id that `yaag_run` returned. |

A Run of the session stops through its own stop capability. An orphaned Run
that an earlier session left behind is asked over its Control Channel first, so
it can settle its Agents and publish a Checkpoint. yaag kills the process group
only when the channel does not answer: no socket, a refusal, or no reply inside
the budget. The report names the Checkpoint when the Run published one.

When the process of an orphan already ended, yaag settles its record and the
report says `Run <id> was no longer running; its record is settled as
interrupted.` over a report whose head line is `Run <id> interrupted.`

### yaag_pause

Pauses a background Run at a clean boundary and publishes a Checkpoint.

| field | maps to | notes |
|---|---|---|
| `id` | none | Extension only. The Run id that `yaag_run` returned. |
| `deadlineMs` | none | Extension only. How long each Ask in flight may wrap up before it is cut short. 1000 to 600000, default 60000. |

yaag sends the pause over the Control Channel of the Run. Each Ask in flight
gets a steer to wrap up, and is cut short at the deadline. The Run then settles
its Agents, publishes a Checkpoint, and ends.

The call returns only after the Run ended. The report starts with
`Run <id> paused.` over a `Checkpoint: <path>` line, and then the program, the
Agents seen, the Asks that settled, and what the Run spent. Resume it with
`yaag_run({ resume: "<checkpoint>" })`. One Checkpoint resumes one time. When
publication fails, the report says `Checkpoint lost: <text>` instead of naming
a path.

Errors:

- `No Run with id <id>.` — no record and no live Run holds that id.
- `Run <id> already ended.` — the Run is history; read it with `yaag_status`.
- `Pause failed: <code>: <message>` — the Control Channel refused the pause, or
  it did not answer. The code says which: `BUSY`, `RUN_ENDED`, `RUN_SETTLING`,
  `CONNECT_FAILED`, `CHANNEL_CLOSED`, or `FLUSH_BUDGET`. The record stays as it
  was, so `yaag_stop` is still available.

### yaag_describe

Reports the name, description, and arguments of a program as a text block. It
maps to `yaag describe`. With `exportName` it prints one Agent Definition block: the
declared name, file, export, deviation, and finding. See [Describe](#describe).

| field | maps to | notes |
|---|---|---|
| `file` | `yaag describe <program.ts>` | Describing imports the module and executes its top level. A Catalog Resolve is the one permitted top-level await. |
| `exportName` | `yaag describe <file> --export <name>` | Describes one registered Agent Definition. Omit it for the program contract. |

### yaag_catalog

Lists the Catalog for a pi session. Extension only: it reads files and it
imports no module. Each call reads the files again. See [Catalog](#catalog).

| field | maps to | notes |
|---|---|---|
| `kind` | none | `program` (scanned and registered) or `agent`. Omit it for both. |
| `pattern` | none | A substring, or a glob with `*` and `?`. Never a regex. An empty pattern lists all. |
| `dir` | none | One Program Directory. It never matches a global entry. |

### yaag_setup_workspace

Creates `.yaag/` with the editor types. It maps to `yaag setup-workspace`.

| field | maps to | notes |
|---|---|---|
| `dir` | `yaag setup-workspace [dir]` | Defaults to pi's working directory. |

## Background Runs

Only the extension has background Runs. Call `yaag_run` with `background: true`.
It returns a Run id. Poll it with `yaag_status`, pause it with `yaag_pause`,
and end it with `yaag_stop`.
A Run record outlives the session, so a later session reaches a Run by its id.
A paused or an interrupted Run whose record names a Checkpoint offers Resume or
Reattach in the `/yaag` menu.

## One side only

- CLI only: `--replay` (strict replay, no model calls) and `--quiet`.
- Extension only: `yaag_status`, `yaag_stop`, `yaag_pause`, `yaag_catalog`, and
  background Runs.
