tapir:run
Run several tapir verbs against the running app in one call.
On this page
Every step is a verb line, so an agent that already knows its next three moves (tap a button, fill a field, read the result) spends one round trip instead of three, and gets one summary line per step instead of three full outputs.
tapir:run runs each step through the verb's own command class, so a step accepts exactly the options the standalone command accepts. The steps share the one VM Service connection of the call.
Synopsis#
dart run tapir tapir:run [--continue] "<step>" ["<step>" ...]
tapir:run requires a running Flutter session (CommandBoot.connected). Each step is one positional argument, so quote it. With no step it prints No steps given and exits 1.
| Option | Type | Default | Description |
|---|---|---|---|
--continue |
flag | false |
Run every step even after one fails, and exit with the first non-zero code. Without it the batch stops at the first failing step. |
Step grammar#
A step is a verb followed by the options of that verb, for example tap --ref=e5 or fill --ref=q1 --text="a b".
- The line is split the way a POSIX shell would: whitespace separates tokens, single quotes keep everything literal, double quotes keep whitespace and honour
\"and\\, and a backslash outside quotes escapes the next character. An unterminated quote or a trailing backslash fails that step. - A bare verb gets the
tapir:prefix (tapmeanstapir:tap). A name with a colon is used as given. - The tokens after the verb are parsed by the verb's own argument parser, so a mistyped option fails that step with the parser's message.
Three kinds of step are rejected, each as a failed step with exit 1:
tapir:runinside atapir:run.- A verb that does not exist (
unknown verb tapir:tapp). - A verb that does not run on the connected app: any command that does not boot connected, such as
tapir:resize,tapir:device,tapir:install,tapir:doctor,tapir:perf_campaignandtapir:perf_compare. Call those on their own.
A step that ends or restarts the app (tapir:close_app, tapir:perf_run) is accepted, but every later step then talks to a dead connection and fails, so make it the last step.
A step that fails does so with the verb's own exit code (tap with no --ref returns 1 from its own check) and a verb that throws exits 3; the batch carries on only with --continue.
Output#
One summary line per step, then the last step's output:
[1/3] tap --ref=e8 ok: Tapped e8
[2/3] fill --ref=q1 --text="a b" ok: Filled q1
[3/3] snap --grep Total ok
- text "Total: 42"
- The text after
ok:is the first non-empty output line of the step, cut to 120 characters (...marks the cut). The rest of a step's output stays out of your context. - The last step's full output follows the summary, because it is the state you act on next; its own summary line carries no digest, so the first output line is not printed twice. A step that produced no output prints only its summary line.
- A failing step prints
[N/M] <step> failed (exit C)followed by its whole output, then the batch stops, or goes on with--continue. - A step's stderr lines (the frame-production banner, the
tapPointOutsideOwnerwarning) are captured into its output with an[ERROR]prefix, so a warning is not lost. - When the batch fails, the last line, on stderr, is
step N failed (exit C): <step>for the first failing step.
Exit codes#
| Exit code | Meaning |
|---|---|
0 |
Every step exited 0. |
1 |
No steps were given; or the first failing step failed on its grammar (nested run, unknown verb, not a connected verb, bad option, bad quoting) or returned 1 itself. |
3 |
The first failing step threw (for example a VM Service error). |
75 |
The first failing step ran before ext.tapir.* was registered; retry once the app has booted. |
| other | The exit code of the first failing step. |
Without --continue the batch ends at that step. With it, every step runs and the exit code is still the first non-zero one.
MCP form#
The tapir_run tool takes the same steps as a list and routes through the substrate command dowel:tapir:run.
{
"name": "tapir_run",
"arguments": {
"steps": ["tap --ref=e5", "fill --ref=q1 --text=\"a b\"", "snap --grep Total"],
"continue": false
}
}
| Property | Type | Required | Description |
|---|---|---|---|
steps |
array | yes | Verb lines, run in order, without the tapir: prefix. |
continue |
boolean | no | Keep running after a failing step (default false). |
The response is the same text the CLI prints.
Working with refs#
eN refs are scoped to one snapshot, and a batch cannot read a snapshot it has not taken yet. So:
- Use refs you already hold from a snapshot taken before the call.
- Between steps that change the screen, prefer a
q<N>handle (tapir:find),--textor--key, which survive a rebuild, over aneNref the first step may invalidate. - End the batch with
snap(or a--snapshot=diffaction) and read the new refs from its output. - Add
--snapshot=diffto the last action step to see only what the batch changed; see tapir:tap.
Examples#
1. Fill a form and read the result#
dart run tapir tapir:run \
"fill --ref=q1 [email protected]" \
"fill --ref=q2 --text=hunter2" \
"tap --ref=q3" \
"snap --interactiveOnly"
2. Keep going past a failure#
dart run tapir tapir:run --continue "tap --ref=e5" "press_key --key=Escape" "snap"
All three steps run. The exit code is the first failing step's, or 0.
Related Documentation#
- Commands index: every verb a step may name.
- tapir:snap: the usual last step.
- tapir:tap:
--snapshot=difffor a cheap read of what a step changed. - MCP tool reference: the
tapir_runschema.