From 24c67a26eed385e64cbe052c15fc14cbeab7e4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 16 Oct 2026 16:56:55 +0200 Subject: [PATCH 01/69] docs(help): write the root help in ASD-STE100 --- src/__tests__/cli-help.test.ts | 2 +- src/commands/schema/cli-help-overview.test.ts | 4 +-- src/commands/schema/cli-help-overview.ts | 36 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/__tests__/cli-help.test.ts b/src/__tests__/cli-help.test.ts index 22526b2b61..220b737efe 100644 --- a/src/__tests__/cli-help.test.ts +++ b/src/__tests__/cli-help.test.ts @@ -213,7 +213,7 @@ test('help unknown command prints error plus root decision card and skips daemon assert.equal(result.calls.length, 0); assert.match(result.stderr, /Error \(INVALID_ARGS\): Unknown command: not-a-command/); assert.match(result.stdout, /All \d+ commands: agent-device help commands/); - assert.match(result.stdout, /When starting a task with a known app/); + assert.match(result.stdout, /When you start a task with a known app/); assert.doesNotMatch(result.stdout, /Global Flags:/); }); diff --git a/src/commands/schema/cli-help-overview.test.ts b/src/commands/schema/cli-help-overview.test.ts index 00c75a0310..5d49bd034a 100644 --- a/src/commands/schema/cli-help-overview.test.ts +++ b/src/commands/schema/cli-help-overview.test.ts @@ -11,12 +11,12 @@ test('root help is a bounded first-success decision card', async () => { `root help must stay within the 3000-byte decision-card budget, was ${Buffer.byteLength(help, 'utf8')} bytes`, ); assert.match(help, /All \d+ commands: agent-device help commands/); - assert.match(help, /starting a task with a known app/); + assert.match(help, /start a task with a known app/); assert.match(help, /agent-device open --foreground/); assert.match(help, /scroll \[amount\] --settle; back --settle/); assert.match(help, /Do not probe first with devices, apps, appstate, snapshot, or screenshot/); assert.match(help, /Copy refs exactly: @e12, @e12~s4/); - assert.match(help, /Coordinates are last resort/); + assert.match(help, /Coordinates are the last resort/); assert.doesNotMatch(help, /^Commands:/m); assert.doesNotMatch(help, /^Global Flags:/m); }); diff --git a/src/commands/schema/cli-help-overview.ts b/src/commands/schema/cli-help-overview.ts index f9bd11775a..76f4f2569a 100644 --- a/src/commands/schema/cli-help-overview.ts +++ b/src/commands/schema/cli-help-overview.ts @@ -12,37 +12,37 @@ Automates iOS, Android, macOS, TV, and web apps for AI agents. All ${listCliCommandNames().length} commands: agent-device help commands Start: - When starting a task with a known app, first run: + When you start a task with a known app, first run: agent-device open --foreground - It starts the session and returns the initial interactive snapshot with @refs. + This command starts the session and returns the initial interactive snapshot with @refs. Do not probe first with devices, apps, appstate, snapshot, or screenshot. - Unknown app id: devices, then apps, then open . Never invent ids. - Resuming an existing session: continue from its current state; do not reopen it. + If the app id is unknown, run devices, then apps, then open . Never invent ids. + If you resume an existing session, continue from its current state. Do not reopen it. Loop: press|click|fill|longpress ... --settle - hover --settle (web only; reveals hover-gated UI) + hover --settle (web only; shows hover-gated UI) scroll [amount] --settle; back --settle - acts, waits for quiet, and prints the UI diff. Continue from that diff. - Run snapshot -i only when the diff lacks the next target or did not settle. + Each command acts, waits until the UI is quiet, and prints the UI diff. Continue from it. + Run snapshot -i only if the diff does not show the next target, or the UI did not settle. Verify a named expectation with the diff, wait text "...", wait , wait absent , is, get, or find. A bare screenshot is not verification. End with: agent-device close Targets: - Copy refs exactly: @e12, @e12~s4. Keep @ and any ~sN pin; refs go stale - after mutations. A literal @handle is label="@handle", not a bare ref. + Copy refs exactly: @e12, @e12~s4. Keep the @ and any ~sN pin. + A mutation makes refs stale. A literal @handle is label="@handle", not a bare ref. Prefer refs, then id/label/role selectors. Selector keys: ${SELECTOR_KEY_NAMES.join(' ')}. - Coordinates are last resort: after snapshot -i shows no semantic target, or a - sparse/AX-unavailable warning says its refs and selectors are invalid. - Then screenshot, press , and re-snapshot on the changed screen. + Coordinates are the last resort. Use them only if snapshot -i shows no semantic target, + or a sparse/AX-unavailable warning says that its refs and selectors are invalid. + Then run screenshot and press , and take a new snapshot of the changed screen. Rules: - --settle is only for press/click/fill/longpress/hover/scroll/back; never open, - snapshot, or close. type never accepts --settle. - fill --settle replaces; type appends after focus. - Late network/debounce result: wait text "Expected", not snapshot polling. - Output full agent-device commands; no pipes, grep, jq, or pseudo-commands. + Use --settle only with press/click/fill/longpress/hover/scroll/back. + Never use it with open, snapshot, or close. type never accepts --settle. + fill --settle replaces the text. type appends to the focused field. + If a network or debounce result comes late, use wait text "Expected". Do not poll with snapshot. + Write full agent-device commands. Do not use pipes, grep, jq, or pseudo-commands. Stop when the requested end state is visible. Mutations run serially. More commands (exact shapes: agent-device help ): @@ -56,6 +56,6 @@ Guides (agent-device help ): manual-qa / dogfood / validate / debugging / scripting / gestures react-native / react-devtools / cdp / tv / web / macos / remote physical-device / ios-system-ui / maestro - foldable iPhone Duo panels, the fold command, and why refs die on a pose change + foldable iPhone Duo panels, the fold command, and why a pose change makes refs stale `; } From 81b720f045b8cb476171fd06f039930eaf32528d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 16 Oct 2026 16:57:29 +0200 Subject: [PATCH 02/69] docs(help): write the MCP server instructions and help tool text in ASD-STE100 --- src/mcp/server-guide.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/mcp/server-guide.ts b/src/mcp/server-guide.ts index b7dba19c8c..0cda622fc5 100644 --- a/src/mcp/server-guide.ts +++ b/src/mcp/server-guide.ts @@ -15,15 +15,15 @@ import type { ToolResult } from './command-tools.ts'; * `open`, act → settled diff → verify → `close`, ref fidelity, sparse-AX recovery, follow * error hints. Everything situational lives behind the `help` tool. */ -export const MCP_SERVER_INSTRUCTIONS = `agent-device drives iOS, Android, tvOS, Android TV, macOS, Linux, and web apps. Tools mirror CLI commands of the same name; CLI flags are camelCase properties (--settle -> settle: true). +export const MCP_SERVER_INSTRUCTIONS = `agent-device drives iOS, Android, tvOS, Android TV, macOS, Linux, and web apps. Each tool mirrors the CLI command of the same name. CLI flags are camelCase properties (--settle -> settle: true). -Start: known app -> call open {app, foreground: true} at once; do not probe with devices, apps, appstate, snapshot, or screenshot first. open returns the initial interactive snapshot with @refs. Unknown app id: devices, then apps, then open the discovered id; never invent ids. Existing session: continue from its state, do not reopen. +Start: if the app is known, call open {app, foreground: true} immediately. Do not probe first with devices, apps, appstate, snapshot, or screenshot. open returns the initial interactive snapshot with @refs. If the app id is unknown, call devices, then apps, then open the discovered id. Never invent ids. If a session exists, continue from its state. Do not reopen it. -Loop: press/click/fill/longpress/hover/scroll/back with settle: true; the response is the settled UI diff, continue from it. To reach an off-screen target, scroll {direction, until: ""} scrolls until it is on screen in one call, and direction "bottom" runs to the end of the content; repeated bare scrolls are the slow way to find something. snapshot {interactiveOnly: true} only when the diff lacks the next target or did not settle. Verify with wait {kind: "text", text}, wait {selector}, wait {absent: selector}, is, get, or find; a bare screenshot is not verification. End with close. +Loop: call press/click/fill/longpress/hover/scroll/back with settle: true. The response is the settled UI diff. Continue from it. To reach an off-screen target, call scroll {direction, until: ""}. It scrolls in one call until the target is on screen. scroll {direction: "bottom"} scrolls to the end of the content. Repeated bare scrolls are the slow way to find a target. Call snapshot {interactiveOnly: true} only if the diff does not show the next target, or the UI did not settle. Verify with wait {kind: "text", text}, wait {selector}, wait {absent: selector}, is, get, or find. A bare screenshot is not verification. End with close. -Targets: copy refs byte-for-byte (@e12, @e12~s4; keep @ and any ~sN). Refs go stale after mutations. Prefer refs, then id/label/role selectors; coordinates last. On a sparse/AX-unavailable warning its refs and selectors are invalid: screenshot, read the image, press {x, y}, then snapshot the changed screen. +Targets: copy refs byte-for-byte (@e12, @e12~s4; keep the @ and any ~sN). A mutation makes refs stale. Prefer refs, then id/label/role selectors. Use coordinates last. If a sparse/AX-unavailable warning appears, its refs and selectors are invalid. Then call screenshot, read the image, call press {x, y}, and take a snapshot of the changed screen. -Errors carry hints; follow them instead of re-planning. Call help only for specialized work (gestures, scripting, TV, macOS, web, remote, debugging) or an unclear command shape, never at startup.`; +Errors carry hints. Follow the hint instead of re-planning. Call help only for specialized work (gestures, scripting, TV, macOS, web, remote, debugging) or an unclear command shape. Never call help at startup.`; export const HELP_TOOL_NAME = 'help'; @@ -46,11 +46,14 @@ export function terminalOnlyCommandNames(): string[] { */ export const HELP_TOOL: { name: string; description: string; inputSchema: JsonSchema } = { name: HELP_TOOL_NAME, - description: `Usage guides. No topic: the full workflow card. topic = one of ${helpTopicIds().join(', ')} for that guide, or a tool name for its complete flag reference. For specialized work or an unclear command shape only; not a startup step, not needed after an error that carries a hint.`, + description: `Usage guides. Without a topic, it returns the full workflow card. Set topic to one of ${helpTopicIds().join(', ')} for that guide, or to a tool name for its complete flag reference. Use it only for specialized work or an unclear command shape. It is not a startup step. It is not necessary after an error that carries a hint.`, inputSchema: { type: 'object', properties: { - topic: { type: 'string', description: 'Guide topic or tool name; omit for the card.' }, + topic: { + type: 'string', + description: 'A guide topic or a tool name. Omit it to get the card.', + }, }, additionalProperties: false, }, @@ -58,7 +61,7 @@ export const HELP_TOOL: { name: string; description: string; inputSchema: JsonSc // The guides are the CLI's own text: rewriting their prose per surface would fork one // source of truth into two. One line up front states the mapping and its exceptions. -const GUIDE_PREAMBLE = `CLI syntax over MCP: \`agent-device --flag\` = tool ; positionals are named properties (target, text, direction), flags camelCase (--settle -> settle: true, -i -> interactiveOnly: true). Terminal-only (no MCP tool; run in a shell): ${terminalOnlyCommandNames().join(', ')}.\n\n`; +const GUIDE_PREAMBLE = `CLI syntax over MCP: \`agent-device --flag\` = tool . Positionals are named properties (target, text, direction). Flags are camelCase properties (--settle -> settle: true, -i -> interactiveOnly: true). Terminal-only (no MCP tool; run these in a shell): ${terminalOnlyCommandNames().join(', ')}.\n\n`; export function callHelpTool(input: Record): ToolResult { const topic = input.topic; From 56001eb8c0f42d01d5b3445194af0f988fe5da95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 16 Oct 2026 17:00:10 +0200 Subject: [PATCH 03/69] docs(help): write the manual-qa help topic in ASD-STE100 --- .../schema/cli-help-workflows.test.ts | 2 +- src/commands/schema/cli-help-workflows.ts | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/commands/schema/cli-help-workflows.test.ts b/src/commands/schema/cli-help-workflows.test.ts index f3318c48f2..2d24e769e7 100644 --- a/src/commands/schema/cli-help-workflows.test.ts +++ b/src/commands/schema/cli-help-workflows.test.ts @@ -57,7 +57,7 @@ test('usageForCommand resolves manual QA help topic', async () => { assert.match(help, /press\/fill\/click\/longpress --settle/); assert.match(help, /A bare screenshot\/snapshot is not verification/); assert.match(help, /use fill --settle to replace/); - assert.match(help, /use type only to append to an already-focused field/); + assert.match(help, /Use type only to append to an already-focused field/); assert.match(help, /label="Email" editable=true/); assert.match(help, /press 'label="Follow"' --settle/); assert.match(help, /Do not use placeholders such as @ref/); diff --git a/src/commands/schema/cli-help-workflows.ts b/src/commands/schema/cli-help-workflows.ts index 012b363ef9..72a1eb158c 100644 --- a/src/commands/schema/cli-help-workflows.ts +++ b/src/commands/schema/cli-help-workflows.ts @@ -16,20 +16,20 @@ export const manualQaHelpTopics = { summary: 'Follow manual test scripts with exact interactions and verification', body: `agent-device help manual-qa -Use this when asked to follow a manual QA script, test case, checklist, acceptance flow, or user-provided instructions. +Use this topic when the task asks you to follow a manual QA script, test case, checklist, acceptance flow, or user-provided instructions. Contract: Execute the script. Do not explore unrelated screens, read app source, invent missing requirements, or broaden scope. - Stop and report ambiguity when a required target or expected result is not visible after the documented recovery steps. + If a required target or expected result is not visible after the documented recovery steps, stop and report the ambiguity. Loop: - 1. Open the requested app; use --relaunch only when the script needs fresh state. + 1. Open the requested app. Use --relaunch only when the script needs fresh state. 2. Run snapshot -i to get current refs for the next step. - 3. Run press/fill/click/longpress --settle for each mutating step. - 4. Treat a settled:true diff as the next observation. Do not add wait stable or another snapshot when the diff already shows the next target or expected result. + 3. For each mutating step, run press/fill/click/longpress --settle. + 4. Treat a settled:true diff as the next observation. If the diff already shows the next target or expected result, do not add wait stable or another snapshot. 5. If --settle prints not settled, follow its hint before the next ref-based action. 6. Verify named expectations with wait text/selector/absent, get, is, find, or the settled diff. A bare screenshot/snapshot is not verification for a named expectation. - 7. Close the session when the script ends. + 7. When the script ends, close the session. Command shapes: agent-device open com.example.app --relaunch @@ -41,21 +41,21 @@ Command shapes: agent-device wait text "Order placed" 3000 agent-device wait absent 'label="Loading..."' 3000 agent-device close - --relaunch forces fresh app state; a deep link/URL open does not need it. Labels with an apostrophe or quote (label="Don't leave") are shell-quoting hazards: prefer the @ref from the latest snapshot/settle output over quoting the literal label. + --relaunch forces fresh app state. A deep link or URL open does not need it. A label with an apostrophe or a quote (label="Don't leave") is a shell-quoting hazard. For such a label, prefer the @ref from the latest snapshot or settle output to a quoted literal label. Targets: - Prefer refs from the latest snapshot -i or settled diff. Use durable selectors when the label/id is known: label="Search", id="submit", role=button label="Follow". If label text matches both a field and its caption, target the field with label="Email" editable=true. - For text fields, use fill --settle to replace the field value; use type only to append to an already-focused field. + Prefer refs from the latest snapshot -i or settled diff. If you know the label or id, use durable selectors: label="Search", id="submit", role=button label="Follow". If the label text matches both a field and its caption, target the field with label="Email" editable=true. + For a text field, use fill --settle to replace the field value. Use type only to append to an already-focused field. Do not use placeholders such as @ref, @eN,