@swmansion/argent 0.19.1-next.0 → 0.19.1-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-cmds.mjs +2 -1
- package/dist/installer.mjs +2 -1
- package/dist/mcp-server.mjs +3 -2
- package/dist/tool-server.cjs +6379 -11086
- package/package.json +1 -1
- package/rules/argent.md +4 -1
- package/skills/argent-create-flow/SKILL.md +47 -26
- package/skills/argent-metro-debugger/SKILL.md +2 -0
- package/skills/argent-react-native-app-workflow/SKILL.md +11 -11
package/package.json
CHANGED
package/rules/argent.md
CHANGED
|
@@ -77,7 +77,10 @@ Decision order:
|
|
|
77
77
|
Call `screenshot` separately only for a baseline before any action or after a delay.
|
|
78
78
|
- Always open apps with `launch-app` or `open-url` — never tap home screen icons.
|
|
79
79
|
- Always use `run-sequence` when performing multiple sequential device actions where you don't need to observe the screen between steps. More in `argent-device-interact` skill.
|
|
80
|
-
- When the session ends or the user says they are done: call `stop-all-simulator-servers
|
|
80
|
+
- When the session ends or the user says they are done: call `stop-all-simulator-servers` with `devices: [...]`
|
|
81
|
+
naming the devices this session actually used. One tool-server is shared by every other agent using this
|
|
82
|
+
argent install, so an unscoped call tears down their devices too; reserve that form for a deliberate
|
|
83
|
+
machine-wide cleanup.
|
|
81
84
|
If the user started Metro separately, ask whether to call `stop-metro` (specify the port if not 8081).
|
|
82
85
|
- If tools provided by mcp-server are not sufficient and action can be done using `xcrun`, `adb`, or other commands, use the command. Examples: changing device options, performing a device action such as lock, shake, etc.
|
|
83
86
|
- When waiting for an action, do not call `screenshot` repeatedly without a proper wait mechanism. Use the `await-ui-element` tool to block until the UI settles (e.g. wait for an element to become `visible`/`hidden`, or to contain expected `text`) instead of polling.
|
|
@@ -9,6 +9,8 @@ A flow is a sequence of steps saved to a `.yaml` file in the `.argent/flows/` di
|
|
|
9
9
|
|
|
10
10
|
Flows store **no device id**: the runner binds a device (the single booted one, or pass `device`/`platform`). A recorded coordinate `gesture-tap` is captured as a portable `tap: { selector }` step whenever the tapped element has stable text/identifier.
|
|
11
11
|
|
|
12
|
+
The one exception is a device _scope_ rather than a target: `stop-all-simulator-servers`' `devices` list is kept in the YAML, because without it the step means the machine-wide sweep and would tear down devices other agents are mid-session on. Replay rebinds a recorded scope only when you pass `device` explicitly — an auto-detected device would retarget the teardown at a device the flow never named, which is the cross-agent teardown the scope exists to prevent. So the recorded ids are what run when you replay without `device`, and when you hand-run the step (see _Strategy 2 — Manual recovery + continue_); on another host they reap nothing and come back in `unmatched`. Re-record the cleanup flow there, or pass `device`. A step that recorded NO scope is still narrowed onto whatever device the run resolved, since binding can only make the machine-wide sweep smaller. A cleanup flow whose only step is that teardown needs no device and runs whether none or several are booted.
|
|
13
|
+
|
|
12
14
|
**Two flow types**
|
|
13
15
|
|
|
14
16
|
- **e2e** — begins with a `launch:` step, which starts that app from scratch (terminate + relaunch), so the flow controls its own start state. No `executionPrerequisite`. May `run:` other flows, and may itself be a `run:` target — when nested, its `launch` runs inline, restarting the app for that sub-scenario. **On Chromium a launch is a process, not a relaunch:** the "device" is the booted app (its id is the CDP port). The runner needs a device before step 1, so it boots for the launch the run _begins_ with, following a leading `run:` — a fragment whose first step composes a chromium e2e flow boots that flow's app (pass `--platform chromium` when the launch names several platforms, or the target is ambiguous and auto-detection is used instead). That first launch then just settles the instance it was booted for; every _later_ launch — a nested e2e flow's own, or a mid-flow `launch:` of the same app — boots its own instance and the run moves onto it for the remaining steps, replacing the one the runner already owns for that app. Every instance the runner boots is torn down at run end; one you pinned with `--device` is attached to, never killed — so relaunching _that_ app mid-flow fails if it holds a single-instance lock. A launch that names no id for the run's platform is an error — a `chromium:` entry does not make a flow runnable on iOS, and the run never switches platforms mid-flight. Record one by adding a `restart-app` of the app under test as the **first** step — it is captured as the `launch` step. Not on Chromium, though: `restart-app` has no chromium support and only successful calls are recorded, so a recorded chromium flow is always a fragment — write the `launch: { chromium: <app path> }` line into the YAML yourself afterward, and delete any `executionPrerequisite` the recording declared: with its own launch the flow controls its start state, and a launch-first flow must not carry one.
|
|
@@ -126,21 +128,23 @@ The standalone command uses only the auto-started local tool server. It is unava
|
|
|
126
128
|
|
|
127
129
|
## Tools
|
|
128
130
|
|
|
129
|
-
| Tool | Purpose
|
|
130
|
-
| ------------------------ |
|
|
131
|
-
| `flow-start-recording` | Start recording — takes
|
|
132
|
-
| `flow-add-step` | Execute a tool call live and record it if it succeeds
|
|
133
|
-
| `flow-add-echo` | Add a label/comment that prints during replay
|
|
134
|
-
| `flow-finish-recording` | Stop recording and get a summary
|
|
135
|
-
| `flow-read-prerequisite` | Read a flow's execution prerequisite without running it (same `name`/`flow_path` sources)
|
|
136
|
-
| `flow-execute` | Replay a flow — a saved one by `name`, or any flow YAML by absolute `flow_path`
|
|
131
|
+
| Tool | Purpose |
|
|
132
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `flow-start-recording` | Start recording — takes `name` + `project_root` and (fragments only) an optional `executionPrerequisite`; creates the file, truncating any existing one |
|
|
134
|
+
| `flow-add-step` | Execute a tool call live and record it if it succeeds |
|
|
135
|
+
| `flow-add-echo` | Add a label/comment that prints during replay |
|
|
136
|
+
| `flow-finish-recording` | Stop recording and get a summary |
|
|
137
|
+
| `flow-read-prerequisite` | Read a flow's execution prerequisite without running it (same `name`/`flow_path` sources) |
|
|
138
|
+
| `flow-execute` | Replay a flow — a saved one by `name`, or any flow YAML by absolute `flow_path` |
|
|
137
139
|
|
|
138
140
|
Every tool during recording returns the current flow file contents, so you can track what has been recorded. Rules:
|
|
139
141
|
|
|
140
142
|
- **Every step runs live.** You see the real tool result (including screenshots) — verify the step worked before continuing. **Only successful steps are recorded**: a failed call writes nothing to the flow file; fix the issue and try again.
|
|
141
|
-
- **
|
|
142
|
-
- **
|
|
143
|
-
- **
|
|
143
|
+
- **Every recording tool takes `name` + `project_root`.** `flow-add-step`, `flow-add-echo`, and `flow-finish-recording` each name the recording they address, repeating the `name` and the absolute `project_root` (an error is returned if the path is not absolute) given to `flow-start-recording`. Nothing is carried over between calls.
|
|
144
|
+
- **Recording _state_ is isolated; the device is not.** A recording is keyed by its output file, `<project_root>/.argent/flows/<name>.yaml`, so several can be open at once — different names, different projects — and one recording's steps never land in another's file. Nothing is isolated on the device: every step runs live, so two recordings driving one device interleave real UI actions, and one flow's recorded `restart-app` resets the app under the other. Give each concurrent recording its own device.
|
|
145
|
+
- **Starting always truncates the `.yaml`.** `flow-start-recording` resets `<project_root>/.argent/flows/<name>.yaml` to an empty flow on every call — including a name that is only a saved file with no recording in progress, so starting under the name of a committed flow wipes it. `restarted: true` is reported only when a LIVE recording of that flow was discarded, so its **absence does not mean nothing was overwritten**. `discardedSteps` (in the return value) counts the discarded take, but can be absent even on a restart. Starting a _different_ flow abandons nothing.
|
|
146
|
+
- **Pick a name unique to your task.** The key is `(project_root, name)` with no ownership check: if another agent starts the same name + project while you are recording, your file is truncated and it takes the key. **Usually nothing tells you.** The liveness check runs only while an append is executing, so if you were between calls (the common case) your next `flow-add-step`/`flow-add-echo` simply re-resolves the key and appends into the other agent's recording, reporting success. You are told only in the lucky case where a step happened to be in flight at the moment of the takeover: `Recording of "<name>" in <project_root> is no longer active — it was restarted while this step was running…`. `flow-finish-recording` re-resolves the key the same way, so it finishes and clears whichever recording currently holds it — possibly the other agent's, which leaves _them_ with `Active recordings: none in this project` and no step in flight to have warned them. Either way, restart under a fresh name instead of re-adding the step. A name that only _resolves_ to the same file — a differently-cased one on macOS/Windows, or a flow (or `.argent/flows`) symlinked into a shared vault from two projects — is the same key, because the key is the file the filesystem resolves to, not the spelling you passed. That collision is reported rather than silent: the second start says `restarted` with a `discardedSteps` count, and the first recording's next call fails with `… are the same file on this filesystem …` naming both spellings.
|
|
147
|
+
- **Start before adding.** Calling those tools for a flow with no recording in progress returns `No active recording for flow "<name>" in <project_root>. If you have not started it yet, call flow-start-recording — but note it truncates, so if <path> already holds a take you want (finished, or interrupted by a restart), copy it aside or record under a fresh name instead. Active recordings: ...`. The truncation caveat is there because this same error is what you get when your take was finished or dropped by the concurrent-recording cap — and on those branches the `.yaml` on disk is fully populated, so starting again destroys it. (A takeover by another agent is different: it resolves to _their_ recording and succeeds — see the previous bullet — rather than reaching this error.) The tail names only the flows live under **the `project_root` you passed** — `"checkout"`, or `none in this project` — and merely counts any others as `(plus N in other projects)`, since a shared tool-server serves callers whose project paths are not yours to see. So a mistyped `name` is spelled out for you; a wrong `project_root` shows up as your flow missing from a project you expected it in.
|
|
144
148
|
- **Mistakes can be edited out.** Edit the `.yaml` file directly to remove or reorder steps.
|
|
145
149
|
|
|
146
150
|
### flow-add-step arguments
|
|
@@ -148,21 +152,33 @@ Every tool during recording returns the current flow file contents, so you can t
|
|
|
148
152
|
The `command` parameter is the MCP tool name; `args` is a **JSON string** (not an object), omitted entirely for tools with no arguments:
|
|
149
153
|
|
|
150
154
|
```
|
|
155
|
+
name: "checkout-e2e" project_root: "/Users/dev/MyApp"
|
|
151
156
|
command: "gesture-tap"
|
|
152
157
|
args: "{\"udid\": \"<UDID>\", \"x\": 0.5, \"y\": 0.35}"
|
|
153
158
|
|
|
159
|
+
name: "checkout-e2e" project_root: "/Users/dev/MyApp"
|
|
154
160
|
command: "await-ui-element"
|
|
155
161
|
args: "{\"udid\": \"<UDID>\", \"condition\": \"visible\", \"selector\": {\"text\": \"Continue\"}}"
|
|
156
162
|
```
|
|
157
163
|
|
|
164
|
+
Recording a `flow-execute` step carries **two** `name`s: the top-level `name` is the recording being appended to, `args.name` is the flow being run (captured as a `run:` step).
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
name: "checkout-e2e" project_root: "/Users/dev/MyApp"
|
|
168
|
+
command: "flow-execute"
|
|
169
|
+
args: "{\"name\": \"login\", \"project_root\": \"/Users/dev/MyApp\"}"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Caveat to the "only successful steps are recorded" rule: if that sibling is a fragment with an `executionPrerequisite`, `flow-execute` returns its prerequisite **notice** instead of running - still a successful return, so `run: login` is recorded even though nothing executed. Add `"prerequisiteAcknowledged": true` to `args` to actually run it.
|
|
173
|
+
|
|
158
174
|
Record an `await-ui-element` step to **gate** the next step on a screen transition — it blocks until the element is `visible`/`hidden` (or contains `text`), so the following step runs only once the screen has actually settled; prefer this over a fixed `delayMs`. If its condition is not met before the timeout, replay **stops at that step** (the steps after it assume the transition happened). See the `await-ui-element` section of `argent-device-interact` for the full condition/selector reference. The live call sees only the trimmed `describe` tree — if it can't find an identifier you know exists, gate on visible text to get the step recorded, then retarget the identifier in the `await:` form during polish (the directive resolves the full hierarchy — see Selectors); don't conclude the testID is unusable in the flow.
|
|
159
175
|
|
|
160
176
|
## Recording
|
|
161
177
|
|
|
162
178
|
1. **Start, then launch as the first step (e2e) or set the stage yourself (fragment).** Call `flow-start-recording` with a descriptive name and the absolute `project_root`. For an **e2e** flow, record a `restart-app` of the app under test as the **first** step — it runs live (resetting the device for the rest of the recording) and is captured as the flow's `launch` step (`restart-app` has no chromium support, so on Chromium record the flow as a fragment against the running app and add the `launch:` line to the YAML afterward, deleting the `executionPrerequisite` line if you passed one — a launch-first flow must not declare it). For a **fragment**, bring the device to the entry state _before_ recording and pass an `executionPrerequisite` describing it (e.g. "App on the login screen") to `flow-start-recording` instead.
|
|
163
|
-
2. **Build step-by-step**: for each action, call `flow-add-step` with the tool name and args. The tool runs immediately — check the result before moving on, and gate each navigation with an `await-ui-element` step.
|
|
164
|
-
3. **Add labels**: use `flow-add-echo` between steps — echo the expected state, not just the action (see _Making flows resilient_).
|
|
165
|
-
4. **Finish**: call `flow-finish-recording`. It returns the file path where the flow was saved and a summary of all steps.
|
|
179
|
+
2. **Build step-by-step**: for each action, call `flow-add-step` with the same `name` + `project_root`, plus the tool name and args. The tool runs immediately — check the result before moving on, and gate each navigation with an `await-ui-element` step.
|
|
180
|
+
3. **Add labels**: use `flow-add-echo` (same `name` + `project_root`) between steps — echo the expected state, not just the action (see _Making flows resilient_).
|
|
181
|
+
4. **Finish**: call `flow-finish-recording` with the same `name` + `project_root`. It returns the file path where the flow was saved and a summary of all steps.
|
|
166
182
|
5. **Polish**: **read the saved `.yaml` file** and convert the raw `tool:` steps that have a cleaner directive form (the recorder leaves these as tools):
|
|
167
183
|
- `tool: keyboard` typing into a field → `type: { into: "<field>", text: "…" }`, folding in the `tap` that focused the field.
|
|
168
184
|
- `tool: await-ui-element` gating a transition → `await: { visible: "…" }` / `{ hidden: … }` / `{ text: { in: …, equals: … } }`, carrying a custom `timeoutMs` over as a `timeout` sibling key. Converting also upgrades the wait from the trimmed `describe` tree to the flow's full-hierarchy tree (see Selectors). Keep the raw `tool: await-ui-element` step only when it sets a custom `pollIntervalMs`/`bundleId` the directive can't express.
|
|
@@ -176,22 +192,22 @@ Every other recorded tool (a velocity-dependent `gesture-swipe`, a fixed-distanc
|
|
|
176
192
|
|
|
177
193
|
```
|
|
178
194
|
flow-start-recording { name: "open-about", project_root: "/Users/dev/MyApp" }
|
|
179
|
-
flow-add-echo { message: "Start Settings from scratch" }
|
|
180
|
-
flow-add-step { command: "restart-app", args: "{\"udid\": \"ABC\", \"bundleId\": \"com.apple.Preferences\"}" } # ⇒ captured as `- launch: com.apple.Preferences` — this is now an e2e flow
|
|
181
|
-
flow-add-echo { message: "On the Settings root list, tapping the 'General' row" }
|
|
182
|
-
flow-add-step { command: "gesture-tap", args: "{\"udid\": \"ABC\", \"x\": 0.5, \"y\": 0.35}" } # ⇒ captured as `- tap: { text: General }` (portable selector, no udid)
|
|
183
|
-
flow-add-step { command: "await-ui-element", args: "{\"udid\": \"ABC\", \"condition\": \"visible\", \"selector\": {\"text\": \"About\"}}" } # gate the transition
|
|
184
|
-
flow-add-echo { message: "On Settings > General, tapping 'About'" }
|
|
185
|
-
flow-add-step { command: "gesture-tap", args: "{\"udid\": \"ABC\", \"x\": 0.5, \"y\": 0.17}" }
|
|
186
|
-
flow-add-step { command: "await-ui-element", args: "{\"udid\": \"ABC\", \"condition\": \"visible\", \"selector\": {\"text\": \"Model Name\"}}" }
|
|
187
|
-
flow-finish-recording {}
|
|
195
|
+
flow-add-echo { name: "open-about", project_root: "/Users/dev/MyApp", message: "Start Settings from scratch" }
|
|
196
|
+
flow-add-step { name: "open-about", project_root: "/Users/dev/MyApp", command: "restart-app", args: "{\"udid\": \"ABC\", \"bundleId\": \"com.apple.Preferences\"}" } # ⇒ captured as `- launch: com.apple.Preferences` — this is now an e2e flow
|
|
197
|
+
flow-add-echo { name: "open-about", project_root: "/Users/dev/MyApp", message: "On the Settings root list, tapping the 'General' row" }
|
|
198
|
+
flow-add-step { name: "open-about", project_root: "/Users/dev/MyApp", command: "gesture-tap", args: "{\"udid\": \"ABC\", \"x\": 0.5, \"y\": 0.35}" } # ⇒ captured as `- tap: { text: General }` (portable selector, no udid)
|
|
199
|
+
flow-add-step { name: "open-about", project_root: "/Users/dev/MyApp", command: "await-ui-element", args: "{\"udid\": \"ABC\", \"condition\": \"visible\", \"selector\": {\"text\": \"About\"}}" } # gate the transition
|
|
200
|
+
flow-add-echo { name: "open-about", project_root: "/Users/dev/MyApp", message: "On Settings > General, tapping 'About'" }
|
|
201
|
+
flow-add-step { name: "open-about", project_root: "/Users/dev/MyApp", command: "gesture-tap", args: "{\"udid\": \"ABC\", \"x\": 0.5, \"y\": 0.17}" }
|
|
202
|
+
flow-add-step { name: "open-about", project_root: "/Users/dev/MyApp", command: "await-ui-element", args: "{\"udid\": \"ABC\", \"condition\": \"visible\", \"selector\": {\"text\": \"Model Name\"}}" }
|
|
203
|
+
flow-finish-recording { name: "open-about", project_root: "/Users/dev/MyApp" }
|
|
188
204
|
```
|
|
189
205
|
|
|
190
206
|
Then polish the saved file: the two `await-ui-element` steps become `await:` directives (see the file below).
|
|
191
207
|
|
|
192
208
|
## Replaying
|
|
193
209
|
|
|
194
|
-
Call `flow-execute` with exactly one flow source: `name` for a flow saved under `.argent/flows/` (this form also works through a remote tool server), or `flow_path` — an absolute path to any flow `.yaml`. A flow's `run:` targets and `__baselines__/` resolve on the **tool server's** filesystem, beside the YAML it actually reads. `flow_path` requires the agent and the tool server to share a filesystem and is refused when they don't; `name` is what still runs then, but it is not a way to keep siblings and baselines — a remote call reaches the server as an upload of that one YAML into a fresh temp directory, so a `run:` target errors as a missing fragment and a `snapshot` step fails for a missing baseline under a temp path (and `updateBaselines` writes the baseline there, to be deleted with the directory). Remotely, replay self-contained flows; a flow that composes or snapshots needs the agent and the tool server on one filesystem. Pass `project_root` too — it is always required here
|
|
210
|
+
Call `flow-execute` with exactly one flow source: `name` for a flow saved under `.argent/flows/` (this form also works through a remote tool server), or `flow_path` — an absolute path to any flow `.yaml`. A flow's `run:` targets and `__baselines__/` resolve on the **tool server's** filesystem, beside the YAML it actually reads. `flow_path` requires the agent and the tool server to share a filesystem and is refused when they don't; `name` is what still runs then, but it is not a way to keep siblings and baselines — a remote call reaches the server as an upload of that one YAML into a fresh temp directory, so a `run:` target errors as a missing fragment and a `snapshot` step fails for a missing baseline under a temp path (and `updateBaselines` writes the baseline there, to be deleted with the directory). Remotely, replay self-contained flows; a flow that composes or snapshots needs the agent and the tool server on one filesystem. Pass `project_root` too — it is always required here, and replaying reads no recording state, so an in-progress recording never stands in for it. **Pass `device` explicitly whenever more than one device is booted:** auto-detection resolves only when exactly one booted device matches — optionally narrowed by `platform` — and otherwise throws, listing what is available. (A Chromium e2e flow boots and tears down its own instance, but only when the launch resolves to a real Electron app path — a `launch: { chromium: <path> }` map, or `platform: "chromium"` with `device` unset. **Don't force it with `platform: "chromium"` on a recorded flow:** the recorder writes a bare-string `launch:` holding a bundle _id_, which the boot branch reads as an app path and fails with `Electron boot: path does not exist: …`. Hand-edit the launch to `{ chromium: <app path> }` first.) If the flow has an execution prerequisite, the tool returns a **notice** with the prerequisite text instead of running — verify the prerequisite is met (you can also inspect it beforehand with `flow-read-prerequisite`, which takes the same `name`/`flow_path` pair) and call `flow-execute` again with `prerequisiteAcknowledged: true`. A flow without a prerequisite runs immediately. The run executes all steps in order and returns a structured report: `{ ok, passed, failed, skipped, errored, steps }`.
|
|
195
211
|
|
|
196
212
|
**What each step reports.** Raw `tool:` steps include the underlying tool's full `result` (screenshots and other outputs render as usual). The directive steps are summarized: `tap`/`type`/`await`/`assert` report only `status` + `reason`, and `snapshot` adds `artifacts` only when there is something to look at — a failed comparison (baseline/current/diff paths), a missing-baseline failure (`current` only), or a baseline write; a clean pass reports just `status` + `reason`. So converting a `tool: gesture-tap` into a `tap:` directive during cleanup drops only that tap's (uninteresting) raw result — output-bearing tools like `screenshot` have no directive form and stay `tool:` steps, so their results keep flowing through.
|
|
197
213
|
|
|
@@ -250,6 +266,11 @@ For silent misfires and partial divergence, echo annotations (see _Making flows
|
|
|
250
266
|
1. Note the failure step index and error message (if hard error).
|
|
251
267
|
2. Call `screenshot` to see where the app actually is now.
|
|
252
268
|
3. Call `describe` or `debugger-component-tree` to get the current element tree. Remember `describe` shows less than the flow tree — a testID missing from its output can still resolve as a selector (see Selectors).
|
|
269
|
+
|
|
270
|
+
`debugger-component-tree` is an **authoring aid only — never record a `debugger-*` step into a flow.** `device_id` is stripped at record time and re-injected at replay, but `port` is not a device-bind key, so a recorded debugger step carries whatever `port` it was given (or falls through to the 8081 default at replay) and runs against whatever Metro happens to be on that port.
|
|
271
|
+
|
|
272
|
+
When calling any `debugger-*` tool directly, mind the shared-Metro rule: `port` is the **only** project discriminator (default `8081`), so with two RN projects running, pass the `port` of the one under test — otherwise the call lands on whichever Metro owns 8081.
|
|
273
|
+
|
|
253
274
|
4. Compare current state to what the failed step expected. Classify the root cause:
|
|
254
275
|
|
|
255
276
|
| Root cause | Symptoms |
|
|
@@ -273,10 +294,10 @@ Read `.argent/flows/<flow-name>.yaml`, update the broken step's `x`/`y`, `bundle
|
|
|
273
294
|
Manually execute the failed step with corrected coordinates from the Diagnose step, then manually execute remaining steps. Does not fix the YAML — use only when re-recording is not worth it.
|
|
274
295
|
|
|
275
296
|
**Strategy 3 — Re-record from failure point** (structural changes, new intermediate screens).
|
|
276
|
-
Navigate the app to the state just before the failure point. Call `flow-start-recording` with the same
|
|
297
|
+
Navigate the app to the state just before the failure point. Call `flow-start-recording` with the same `name` + `project_root` — the start truncates the saved `.yaml` immediately, so copy the working prefix out of the file first. Re-add that prefix via `flow-add-step` (same `name` + `project_root`), then continue recording new steps from the divergence point. Call `flow-finish-recording` with the same `name` + `project_root`.
|
|
277
298
|
|
|
278
299
|
**Strategy 4 — Full re-record** (major changes, unclear diagnosis, or 3+ broken steps).
|
|
279
|
-
Reset the app to prerequisite state (`restart-app` + `launch-app`). Record from scratch with the same
|
|
300
|
+
Reset the app to prerequisite state (`restart-app` + `launch-app`). Record from scratch with the same `name` + `project_root` — the start truncates the old `.yaml`, so keep a copy if you may want to diff against it.
|
|
280
301
|
|
|
281
302
|
**Decision heuristic:**
|
|
282
303
|
|
|
@@ -27,6 +27,8 @@ All tools accept `port` (default 8081) AND `device_id` (the iOS Simulator UDID,
|
|
|
27
27
|
|
|
28
28
|
One Metro port can serve multiple connected devices (e.g. two simulators on `localhost:8081`, or an iOS simulator alongside an Android emulator with `adb reverse` set up). `device_id` pins every debugger/network/profiler call to a specific device so sessions do not collide.
|
|
29
29
|
|
|
30
|
+
With two or more devices on one Metro, `debugger-connect` refuses a udid/serial and hands back the `logicalDeviceId` to re-target with. That id then keys the session — including for teardown. **Pass it in `stop-all-simulator-servers`' `devices` alongside the device id**, or the session survives your session end holding its CDP socket, console server and log file. The teardown reports what it could not reach in `left_running`; re-call with the id it names.
|
|
31
|
+
|
|
30
32
|
### Connect & diagnostics
|
|
31
33
|
|
|
32
34
|
| Tool | Purpose |
|
|
@@ -137,17 +137,17 @@ Once you discover the correct build/run workflow for a project, **save it to pro
|
|
|
137
137
|
|
|
138
138
|
### 3.5 Device Control
|
|
139
139
|
|
|
140
|
-
| Action | Tool / Command
|
|
141
|
-
| -------------------------- |
|
|
142
|
-
| List devices | `list-devices` tool (iOS + Android)
|
|
143
|
-
| Boot an iOS simulator | `boot-device` tool with `udid`
|
|
144
|
-
| Boot an Android emulator | `boot-device` tool with `avdName`
|
|
145
|
-
| Launch an app | `launch-app` tool (pass device id + bundle id / package name)
|
|
146
|
-
| Restart an app | `restart-app` tool (pass device id + bundle id / package name)
|
|
147
|
-
| Open a URL / deep link | `open-url` tool (pass device id + URL)
|
|
148
|
-
| Rotate device | `rotate` tool
|
|
149
|
-
| Stop simulator server | `stop-simulator-server` tool (iOS UDID or Android serial — one device)
|
|
150
|
-
| Stop all simulator servers | `stop-all-simulator-servers` tool (
|
|
140
|
+
| Action | Tool / Command |
|
|
141
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| List devices | `list-devices` tool (iOS + Android) |
|
|
143
|
+
| Boot an iOS simulator | `boot-device` tool with `udid` |
|
|
144
|
+
| Boot an Android emulator | `boot-device` tool with `avdName` |
|
|
145
|
+
| Launch an app | `launch-app` tool (pass device id + bundle id / package name) |
|
|
146
|
+
| Restart an app | `restart-app` tool (pass device id + bundle id / package name) |
|
|
147
|
+
| Open a URL / deep link | `open-url` tool (pass device id + URL) |
|
|
148
|
+
| Rotate device | `rotate` tool |
|
|
149
|
+
| Stop simulator server | `stop-simulator-server` tool (iOS UDID or Android serial — one device) |
|
|
150
|
+
| Stop all simulator servers | `stop-all-simulator-servers` tool — pass `devices: [...]` to scope the teardown to this session's devices (an unscoped call also tears down other agents' devices; use it only for a machine-wide cleanup) |
|
|
151
151
|
|
|
152
152
|
For full simulator setup workflow, refer to the `argent-ios-simulator-setup` skill.
|
|
153
153
|
|