awf commands
awf is the operator's command: you run workflows, follow them and answer them with it. An agent inside a run never types awf; it answers with wfa.
Each command's help below is the file awf {command} -h prints, included as it is, so this page and the command can't disagree. The prose around it says what the command is for and which page teaches it.
Help
awf -h # the commands, grouped; also awf, awf help, awf --help
awf help run # one command's usage, options, exit codes; awf run -h
awf help reference # every command's usage lines, on one page
awf --versionEvery command takes -h or --help anywhere before --, and prints its help whatever else is given. A word that must be -h itself, such as a message's text, goes after --.
An unknown command exits 2 and names the commands it is closest to; a workflow file given as a command gets the awf run line it meant.
JSON
With --json, stdout always ends with one document, but for awf inbox, which prints none when it has no items to read. When the command worked, it is the result. When it failed, it is a refusal, and stderr still says why, for people:
{"v": 1, "code": "no-run", "error": "no run review-mr/7 under /work/.awf/runs, or in a project awf knows"}A document with code is a refusal; no result has one. Check for it first, as the exit code doesn't tell them apart: awf emit exits 1 with a full result when the run's attempt didn't start, and awf tick exits 1 with the runs it woke when it couldn't wake some. A command that prints a line per item, as awf inbox --json does, ends with its refusal. A usage error is a refusal too, whenever --json is anywhere in the arguments before --. The exit codes are each command's own, as its help says. A project that installs an agentswf from before this rule runs on it, and prints what it printed: there, an empty stdout is a failure stderr explains.
A code means the same in every command that prints it, and, where the client meets the same failure, is the word ClientError throws for it:
code | The command failed because | Printed by |
|---|---|---|
usage | its arguments can't be parsed, or two of them clash | every command |
no-run | there is no such run | run, emit, runs, logs, inbox, reply, send |
ambiguous | a name fits more than one: a run's id, runs of two workflows or projects; or --as, an agent or --to, those with choices | emit, runs, logs, inbox, reply, send |
id | a run or event id the grammar refuses, one the workflow's id(args) returns, or one another run has | run, emit, runs, logs, inbox, reply, send |
file | the workflow file is missing, doesn't load, or doesn't export the workflow | run, emit |
event | the workflow doesn't take the event's name, data or source, or takes no events | emit |
prepare | the workflow refuses its arguments or options, or its id(args) throws | run, emit |
no-id | a workflow that starts on events, with no --id and no id(args) | run |
other-args | a run there, started with other arguments or options | run |
not-started | no attempt started: Ctrl-C, a session not taken, a claim given up, or a run that is running or completed, or whose working folder is gone | run |
unreachable | the run's files, a new run's folder included, can't be read or written, or it doesn't answer | run, emit, runs, logs, inbox, reply, send |
cursor | a cursor or handle doesn't fit: made for another run or filter, past the run's end, or naming no event | logs, inbox |
no-engine, other-engine | the project's agentswf is missing, or another version than this awf | run, emit, runs, logs, inbox, reply, send |
project | the workflow isn't in a project | run, emit |
internal-error | something failed that is neither the caller's nor the run's, such as awf's own record of projects; error says what | every command |
awf reply and awf send also refuse for the participant's reasons, and awf inbox with no-participant, a run that never connected the one asked for.
A command's --json carries "v", which changes when a field is renamed or removed. It is 1 for every command but these: awf runs {run} --json is at 2, as its agent and stage states are a fixed set; awf inbox --json is at 2, as it took the shared no-run and cursor; and awf send --json and awf reply --json are at 3, as their success lost its code. A refusal carries its command's "v". awf run --json prints the run's out.json, whose own version says its format, and its refusals carry "v": 1.
Durations
--timeout and --wait take a duration, as a workflow does: a decimal and s, m, h or d, such as 90s, 1.5h or 1d.
Run a workflow
awf run
Starts a run of a workflow file, or continues one with --id and --continue. Quick start runs one for the first time; Runs covers ids, attempts, stages, --continue, --from-stage and --values, and owns the exit codes of an attempt. --timeout is on Timeouts, --sandbox on Sandboxes, --here on Calling session, and --workspace and --keep-panes on Harnesses.
usage: awf run [options] <workflow-file> [options] [-- workflow arguments...]
awf run [options] <workflow-file> --id <id> --continue
run options: --id <id>, --continue, --from-stage <stage>, --values <file>,
--timeout <duration>, --run-root <directory>, --cwd <directory>, --sandbox <file>,
--workspace <run|origin|name>, --keep-panes <never|on-failure|always>,
--json, --quiet, --no-watch, --here
Each awf run is an attempt of a run: a new run, with --id's id or a generated one, or, with
--continue, the run --id names, with the arguments, --cwd, --sandbox, --workspace and
--keep-panes it was started with. A continue reuses the stages that succeeded, up to the first
with no record or to --from-stage, and runs the rest.
A new run with --from-stage starts there. A stage before it with no record to reuse takes its
value from --values, a JSON object by stage name, checked by its result; one that returns
nothing is passed. Without one the attempt stops, naming the stage and its schema.
A run is kept in .awf/runs/<workflow>/<id> under --cwd, unless --run-root names another folder
for .awf/runs. A workflow that gives file: import.meta.path keeps its runs in its file's project
unless --run-root names another folder: the nearest directory at or above the file holding .awf/,
else the file's own, so --continue finds a run a program started there from anywhere.
A run has no deadline unless --timeout gives it one, per attempt: a duration, a decimal and s, m,
h or d, such as 20m or 1d.
A workflow that knows how to present its result prints that; --json prints the full result instead.
Either way the full result is kept as out.json in the run's folder, beside report.md
when the workflow writes one. A run that fails or is cancelled once its agents have started keeps
out.json too, with what it spent and why it ended; --json prints it. When no run comes of it,
--json prints a refusal whose code says why: usage, file, prepare, no-id, id, no-run,
other-args, not-started, unreachable, project, no-engine, other-engine or internal-error;
--here takes no --json, nor does opening a run of a workflow with events, which prints no
document. A second Ctrl-C stops awf at once, without it; a continue then reads
what its agents spent.
Progress, the workflow's log and how the run ended go to stderr, and only the result to stdout.
--quiet, for a scheduler or supervisor, leaves out the progress and the log: a run that completes
or parks writes nothing to stderr, and one that doesn't says how it ended. awf's own problems and
a person's question still print. awf logs reads everything left out.
--cwd sets the directory the workflow and its agents work in; it defaults to the current one.
--sandbox puts every agent of the run in one sandbox, working in --cwd; the file is a JSON spec
such as {"read": ["/data/request.md"], "srt": {}}, naming srt or docker, whose relative paths are
from --cwd. A workflow that opens a sandbox of its own is refused.
A sandbox with its own Herdr, as a docker box has, gets a tab in the run's workspace showing its
panes; --no-watch leaves it out, and awf still prints the command that shows them.
--workspace says where each TUI agent's tab opens: run, the run's own Herdr workspace (the
default); origin, the one awf run was typed in; or any other word, the workspace with that label,
found or made. --keep-panes says whether an agent's pane stays once it is done: never (the
default), on-failure, when its last turn wasn't answered, or always. A kept pane's harness is left
running, and yours. Both are kept with the run for every attempt, a woken one too. A run an event
started has neither, and takes the defaults. A continue may give either again with the run's
value, or its default where the run had none, and no other.
--here, run by an agent in a Herdr pane, starts the run in a new tab and has it take that agent's
session over as one of its agents, from its next turn until the run ends. It prints a line for
the agent to end its turn with, which is how the run finds the session.
A run of a workflow with events is named by --id, or by the workflow's id(args); one without
starts may have an id made, as any run. awf run starts it with its arguments and no event,
detached in its project, as awf emit would, and the run waits at its first receive. A run already
there is only woken. A run an event starts has no argv: its args are what prepare makes of none.
Its --continue takes --from-stage, as any run's does, and no --values. A --from-stage the attempt
never reaches runs the attempt until it would park or return, and stops it there.
Exits 0 when the run completed or parked, 1 when it failed or timed out, 2 on a usage error or
when the run is refused, 3 when it stopped and can go on with --continue, and 128 plus the
signal's number when it was interrupted.
Examples:
awf run examples/minimum-review/review-loop.ts
awf run --timeout 20m examples/minimum-review/review-loop.ts
awf run examples/minimum-review/review-loop.ts -- packages/engine/src
awf run examples/minimum-review/review-loop.ts --cwd ../other-repo
awf run examples/pane-layout/workflow.ts --workspace origin --keep-panes on-failure
Workflow files are trusted code and run with your filesystem and process authority.awf test
Runs a workflow's tests, each agent replaced by scripted answers: no agent starts, nothing is metered. Testing teaches writing them; the Testing API lists the calls.
usage: awf test [paths...] [-t <pattern>] [--watch] [--timeout <duration>]
Runs a workflow's tests (*.test.ts and *.spec.ts files) under the current directory, or under
each file or directory given, with agentswf/workflow, agentswf/testing and typebox served as
awf run serves them: nothing is installed. -t runs the tests whose names match the pattern;
--watch reruns them on a change; --timeout bounds each test, 5s by default.
Exits 0 when every test passed, 1 when one failed or none was found, 2 on a usage error. What
it prints is for reading, not parsing.awf init
Sets up a folder for writing workflows, with a hello workflow and its test to start from. Quick start begins with it.
usage: awf init [dir] [--no-install]
Sets up the current directory, or dir, for writing workflows: hello/workflow.ts, a workflow
with one agent, and hello/workflow.test.ts, its test; package.json, tsconfig.json, .gitignore
and AGENTS.md. Then runs bun install, unless --no-install, for editor types: awf runs a
workflow against its own copies either way.
Never overwrites. A file that exists is skipped, except that package.json gains the
devDependencies and scripts it lacks and .gitignore the lines it lacks.
Exits 0; 1 when a file could not be written, or bun install failed (the files written are
kept); 2 on a usage error.awf emit
Sends an event to a workflow with events, starting its run or waking the one that waits; or, by the run's ref, to a run that is there, as a participant's message to its code, {key}:reply. Events covers workflows with events; Hosting events calls awf emit from a webhook or a CI job.
usage: awf emit --target <file> --id <id> <event> [<json>] [--event-id <id>] [--source <name>] [--json]
awf emit <run> <key>:reply '{"text": …, "author": …}' [--event-id <id>] [--source <name>] [--cwd <directory>] [--run-root <directory>] [--json]
Sends an event to the run of a workflow with events that --id names. That run starts if it has
none and the event is one that starts one, and is woken if it has one: told while it runs,
continued when it is parked or crashed. The run lives in the project of <file>, the nearest
directory above it holding .awf/, whoever sends. --event-id names the occurrence: the same id
again is a duplicate. --source names the integration that sent it, such as github, which the
event a wait returns carries.
The second form sends a participant's message to the code of a run that is there, <workflow>/<id>
or an <id> only one workflow has, here or in a project awf knows: <key>:reply, which the workflow
declares, checked against the file the run's last attempt ran. "author" is optional. Its
--event-id is the message's own id, so a resend is a duplicate. It never starts a run, and may
arrive before the run connects the participant: it waits for the run's next wait. The first form
takes only the workflow's own events.
Prints what became of the event, and what was done to the run: accepted, duplicate or refused.
--json prints one document: the result, or a refusal whose code says why it failed: usage,
no-run, ambiguous, id, file, event, prepare, project, no-engine, other-engine, unreachable or
internal-error. Exits 0 for any of the three; 1 when <file> could not be loaded, or the run
isn't found or can't be read, and nothing is kept, or when the run's attempt could not be
started (the event is kept, and the next event or awf tick tries again); 2 on a usage error, for
a workflow with no events, or when the event is wrong for its target: its name, its data, or its
id.awf tick
Wakes the waiting runs of workflows with events whose timers are due, and continues the ones that crashed. Run it on a schedule: Hosting events covers that.
usage: awf tick [--cwd <directory>] [--run-root <directory>] [--json]
awf tick --all [--json]
Wakes every run of a workflow with events under --cwd's .awf/runs, or --run-root, the runs awf
runs lists there, as an event would: continues a parked run whose wait's timer is due, and a
crashed or failed one until it has failed three times in a row. Runs of other workflows are left
as they are. --all wakes them in every project awf has started one in, as ~/.awf/projects lists
them. A project's runs are woken by its own agentswf: one at another version than this awf runs
its own awf tick for them. Run it on any schedule; a run is continued once however many wakers
come at once.
Prints a line for each run it woke, and nothing when it woke none, and one line on stderr
counting runs with events it can't read, which it doesn't wake; --json prints one document,
those runs under unreadable, on exit 1 too, or a refusal whose code says why it failed: usage or
internal-error. Exits 0; 1 when a project is gone, declares agentswf without installing it, or
its own agentswf failed, or a run could not be woken, the others still woken; 2 on a usage
error.Look at runs
awf runs
Lists the runs here, or shows one in depth: its attempts, its stages, what each agent is doing, and what to run next. Following runs teaches it.
usage: awf runs [--limit <n>] [--cwd <directory>] [--run-root <directory>] [--json]
awf runs <run> [--cwd <directory>] [--run-root <directory>] [--json]
The runs here, newest first, or one run in depth: its attempts, the stages of the last, what
each agent is doing, and what to run next. The list shows 20 unless --limit says another
number. A run is <workflow>/<id>, or an <id> only one
workflow has. Runs are listed under --cwd's .awf/runs, or --run-root, as awf run keeps them; a
run named is found there first, then in each project awf has started a run with events in.
--json prints one document, the interface for scripts: when it fails, a refusal whose code says
why: usage, no-run, ambiguous, id, unreachable, no-engine, other-engine or internal-error. Exits
0 on any read that worked, 1 when there is no such run, 2 on a usage error, 3 when the run's
files could not be read.In awf runs {run}, and its --json, an agent's state is one word, the one wfa peers uses:
| State | The agent |
|---|---|
idle | has had no turn yet |
working | is in a turn |
waiting | is in a turn, after wfa waiting; live has its reason and check-in |
awaiting-reply | is in a turn, awaiting a peer's reply; live names the peer and until when |
answered, unanswered, blocked, timed-out, failed, cancelled | ended its last turn so |
stopped | was stopped while in a turn |
interrupted | was in a turn when the run's process went (version 1 said was working) |
A stage's state is running, interrupted, reused, provided, or how it ended: succeeded, stopped or failed.
awf logs
What a run did and is doing, its events and its agents' sessions merged by time, read once or followed. Following runs teaches it, including following a run from another agent.
usage: awf logs [<run>] [@<handle>] [--agent <key>] [--stage <name>] [--after <cursor>]
[--limit <n>] [--wait <duration> | --follow] [--full] [--cwd <directory>]
[--run-root <directory>] [--json]
What a run did and is doing: its events and what its agents did, read from their own session
files, merged by time. With no run, the newest here. Without --after, the last 40 events; with
it, what came after, oldest first. Every answer ends with the command that gets only what is
new. --wait: with nothing to show yet, wait for the first event, at most that long, up to 5m,
such as 90s; --follow prints each event as it comes until the run ends. @<handle> names one
event, and --full prints its text, with common credential shapes masked; --json carries it
always.
With --json, a failure prints a refusal whose code says why: usage, no-run, ambiguous, id,
cursor, unreachable, no-engine, other-engine or internal-error. Exits 0 on any read that worked,
1 when there is no such run or the cursor or handle does not fit it, 2 on a usage error, 3 when
the run's files could not be read.awf open
Opens a copy of an agent's session in its harness's TUI where the agent worked, or asks it one question with -p; --no-fork opens the session itself, the run held meanwhile. Following runs teaches it.
usage: awf open <run> <agent> [--no-fork] [--session <id>] [-p [<text>]]
[--cwd <directory>] [--run-root <directory>] [--json]
A run is <workflow>/<id>, or an <id> only one workflow has, here or in a project awf knows.
Without an agent, it prints the command that opens each of the run's agents.
Opens a copy of an agent's latest session in its harness's own TUI, where the agent worked: its
working directory, its home and skills, its sandbox, on the model and effort it ran on, with the
harness's own permission prompts. The copy is made without asking the model, and is yours: the
run and the agent's session are unchanged, and awf keeps no record of it.
--no-fork opens the session itself: what you say stays with the agent, awf logs shows it, and the
next attempt resumes it. It is refused while the run is live; while it is open, no attempt starts
and an event waits, and once the harness exits a run with events is woken for it.
--session opens another session in the agent's place: a copy an earlier awf open made, as it is;
or one of the agent's earlier sessions, as its latest would be.
-p asks one question headless and prints the answer; the text is its argument, or stdin without
one. It runs as the agent's turns ran, without prompts, in its sandbox when it had one; on
claude, billed per token headless, it is refused unless the agent ran metered.
As the harness exits, awf prints the command that opens the same session again.
With --json, -p prints {"v": 1, "session", "forkedFrom"?, "reply", "again"}, or a refusal whose
code says why: usage, no-run, ambiguous, id, no-session, live, opened, unreachable, no-engine,
other-engine or internal-error. no-session is an agent with no session or operation to open;
live, --no-fork, or a sandboxed agent, on a running run, or a --no-fork whose session changed as
it held the run; opened, a run another awf open --no-fork holds, or a sandbox another awf open
has open; internal-error, a copy or a -p question that failed. A TUI has no JSON: --json without
-p is a usage error. Exits with the TUI's exit code, or 0 once -p printed its answer; 1 when
refused or failed; 2 on a usage error, or a -p or a TUI the agent's harness can't give; 3 when
the run or the agent's place could not be read, or awf itself failed.Coordinate a run from outside
awf inbox
Reads what agents said to a participant outside the run, a person or an app, and what it said back. Answering for a participant teaches it.
usage: awf inbox [<run>] [--open] [--workflow <name>] [--as <key>] [--after <cursor>] [--wait <duration>]
[--cwd <directory>] [--run-root <directory>] [--json]
What agents said to a participant outside the run, and what it said back: routes, messages,
answers, questions that closed unanswered, and each attempt's end, oldest first. Without a run,
every run here that connected one; --workflow narrows that. A run is <workflow>/<id>, or an <id>
only one workflow has. --after reads on from a cursor the last answer gave; --wait waits up to
that long, at most 5m, when nothing is new. --open lists the questions still waiting for an
answer, a snapshot. --json prints one item per line, and when it fails a refusal last whose code
says why: usage, no-run, ambiguous, id, cursor, no-participant, unreachable, no-engine,
other-engine or internal-error.
Exits 0 on a read that worked, 1 when there is no such run, it has no participant, or the
cursor is another's, 2 on a usage error, 3 when the run's files could not be read.awf reply and awf send
awf reply answers a question an agent asked; awf send messages an agent first. A message on a participant to a run's code is awf emit {run} {key}:reply. They share one help. Answering for a participant teaches both.
usage: awf reply <run> <agent> [--to <id>] [--as <participant>] [<text>]
awf send <run> <agent> [--as <participant>] [<text>]
each takes [--cwd <directory>] [--run-root <directory>] [--json]
A run is <workflow>/<id>, or an <id> only one workflow has, here or in a project awf knows.
Answers a question an agent asked a participant outside the run, or messages an agent first,
over a route the workflow allowed. The text is the argument, or stdin without one. --to names
the question, as awf inbox prints it; it may be left out when the agent has one open. --as
names the participant when the workflow connects more than one. Neither waits for an answer:
it comes back in awf inbox. A message on a participant to the run's code is sent with awf emit
<run> <participant>:reply '{"text":…,"author":…}' --event-id <id>.
With --json, each prints {"v": 3, "id": …} when delivered, or a refusal whose code says why: the
participant's, or usage, no-run, ambiguous, id, unreachable, no-engine, other-engine or
internal-error. Exits 0 when delivered, 1 when refused (the code says why), 2 on a usage error,
3 when the run could not be reached.With --json, each prints one line: {"v": 3, "id": …} when delivered, or a refusal with its code and error. Past the shared codes, a participant's own: ended, no-participant, unknown-agent, ambiguous (with choices), expired, no-route, peer-stopped, closed, too-large, limit, inbox-full, or unsupported-version, when the run's engine is another version of awf than the command's, as after installing awf anew while the run is live. Version 1 named peer-stopped stopped; version 2 said "code": "delivered" on success, and not-found for no-run.
This machine
awf allowance
What is left of each harness's plan, as the harness itself shows it. Harnesses explains subscription and metered; Cost covers a run's time and tokens.
usage: awf allowance [harness...] [--json]
What is left of each harness's plan, as the harness itself shows it: claude's /usage, as
claude -p /usage prints it; codex's /status, as its app-server answers it; and cursor's /usage,
read in a Herdr pane. pi has none of its own. Nothing asks a model. Every harness when
none is named: claude, codex, pi, cursor.
A plan's monthly price is an estimate at the vendor's list price as of the table's date:
plans 2026-10-05.
--json prints one document, the interface for scripts, with the plan as the harness names it and
no price; what it prints otherwise is for reading. A harness that could not be read says why; a
failure is a refusal whose code says why: usage or internal-error. Exits 0 once every harness
was asked, 2 on a usage error.