flow-walker-cli 0.2.0

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/AGENTS.md ADDED
@@ -0,0 +1,299 @@
1
+ # flow-walker — Agent Workflow Guide
2
+
3
+ ## What is flow-walker
4
+
5
+ flow-walker is the **flow layer** — it discovers, executes, and reports on app flows.
6
+ It uses [agent-flutter](https://github.com/beastoin/agent-flutter) and [agent-swift](https://github.com/beastoin/agent-swift) as **transport layers** that control specific platforms.
7
+
8
+ **Six commands:**
9
+ - `walk` — BFS-explore the app, discover screens, generate YAML flows
10
+ - `run` — Execute a YAML flow, produce run.json + video + screenshots
11
+ - `report` — Generate self-contained HTML report from run results
12
+ - `push` — Upload report to hosted service, return shareable URL
13
+ - `get` — Fetch run data from hosted service by run ID
14
+ - `schema` — Machine-readable command introspection (agent discovery)
15
+
16
+ ## Agent-first workflow
17
+
18
+ ```bash
19
+ # 1. Discover available commands
20
+ flow-walker schema # → { version, commands: [...] }
21
+ flow-walker schema run # → args, flags with types, exit codes
22
+
23
+ # 2. Dry-run to verify flow resolves
24
+ flow-walker run flow.yaml --dry-run # → per-step resolved/unresolved + reasons
25
+
26
+ # 3. Execute
27
+ flow-walker run flow.yaml --json # → run.json with unique run ID
28
+
29
+ # 4. Report
30
+ flow-walker report ./run-output/<run-id>/
31
+
32
+ # 5. Share (hosted)
33
+ flow-walker push ./run-output/<run-id>/ --json # → { id, url, htmlUrl, expiresAt }
34
+
35
+ # 6. Retrieve run data later
36
+ flow-walker get 25h7afGwBK --json # → run.json content
37
+
38
+ # Version check
39
+ flow-walker --version # → flow-walker 0.1.0
40
+ flow-walker --version --json # → {"version":"0.1.0"}
41
+ ```
42
+
43
+ ## Prerequisites
44
+
45
+ 1. **agent-flutter** installed and in PATH (`npm install -g agent-flutter-cli`)
46
+ 2. Flutter app running with Marionette initialized
47
+ 3. ADB connected (Android) or Simulator running (iOS)
48
+ 4. Run `agent-flutter doctor` to verify setup
49
+
50
+ ## Run IDs
51
+
52
+ Every `flow-walker run` generates a unique **10-char base64url ID** (e.g. `25h7afGwBK`).
53
+
54
+ - Output goes to `<output-dir>/<run-id>/` — multiple runs never overwrite
55
+ - `run.json` includes `"id": "25h7afGwBK"` as top-level field
56
+ - Agents can correlate runs by ID across logs, reports, and API calls
57
+ - Composite key `{flow}/{id}` (e.g. `tab-navigation/25h7afGwBK`) for human reference
58
+
59
+ ## Canonical workflows
60
+
61
+ ### Auto-explore an app
62
+
63
+ ```bash
64
+ agent-flutter connect ws://127.0.0.1:38047/abc=/ws
65
+ flow-walker walk --skip-connect --max-depth 3 --output-dir ./flows/
66
+ # Output: YAML flows + _nav-graph.json
67
+ ```
68
+
69
+ ### Execute a flow
70
+
71
+ ```bash
72
+ flow-walker run flows/tab-navigation.yaml --output-dir ./results/
73
+ # => Run ID: 25h7afGwBK
74
+ # => Output: ./results/25h7afGwBK/run.json, recording.mp4, step-*.png, device.log
75
+ ```
76
+
77
+ ### Generate report
78
+
79
+ ```bash
80
+ flow-walker report ./results/25h7afGwBK/
81
+ # Output: report.html (self-contained, can be shared)
82
+ ```
83
+
84
+ ### Run a flow suite
85
+
86
+ ```bash
87
+ for flow in flows/*.yaml; do
88
+ flow-walker run "$flow" --output-dir ./results/ --json
89
+ done
90
+ # Each run gets its own subdirectory by run ID
91
+ ```
92
+
93
+ ## Output shapes
94
+
95
+ ### run.json
96
+
97
+ ```json
98
+ {
99
+ "id": "25h7afGwBK",
100
+ "flow": "tab-navigation",
101
+ "device": "Pixel_7a",
102
+ "startedAt": "2026-03-12T10:00:00Z",
103
+ "duration": 14200,
104
+ "result": "pass",
105
+ "steps": [
106
+ {
107
+ "index": 0,
108
+ "name": "Verify home tab",
109
+ "action": "assert",
110
+ "status": "pass",
111
+ "timestamp": 0,
112
+ "duration": 2300,
113
+ "elementCount": 22,
114
+ "screenshot": "step-1-tab-home.png",
115
+ "assertion": {
116
+ "interactive_count": { "min": 20, "actual": 22 },
117
+ "bottom_nav_tabs": { "min": 4, "actual": 4 }
118
+ }
119
+ }
120
+ ],
121
+ "video": "recording.mp4",
122
+ "log": "device.log"
123
+ }
124
+ ```
125
+
126
+ ### _nav-graph.json (from walk)
127
+
128
+ ```json
129
+ {
130
+ "nodes": [
131
+ { "id": "abc123", "name": "home-screen", "elementCount": 24, "visits": 3 }
132
+ ],
133
+ "edges": [
134
+ { "source": "abc123", "target": "def456", "element": { "ref": "@e3", "type": "button", "text": "Settings" } }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ ### Schema envelope (from schema)
140
+
141
+ ```json
142
+ {
143
+ "version": "0.1.0",
144
+ "commands": [
145
+ {
146
+ "name": "run",
147
+ "description": "Execute a YAML flow...",
148
+ "args": [{ "name": "flow", "required": true, "type": "path", "description": "..." }],
149
+ "flags": [{ "name": "--json", "type": "boolean", "description": "..." }],
150
+ "exitCodes": { "0": "all steps pass", "1": "one or more steps fail", "2": "error" },
151
+ "examples": ["flow-walker run flows/tab-navigation.yaml"]
152
+ }
153
+ ]
154
+ }
155
+ ```
156
+
157
+ ### push result (from push --json)
158
+
159
+ ```json
160
+ {
161
+ "id": "25h7afGwBK",
162
+ "url": "https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK",
163
+ "htmlUrl": "https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK.html",
164
+ "expiresAt": "2026-04-11T13:22:12.070Z"
165
+ }
166
+ ```
167
+
168
+ ### Command outputShape (from schema)
169
+
170
+ Commands that produce structured output declare their fields via `outputShape`:
171
+
172
+ ```json
173
+ {
174
+ "name": "run",
175
+ "outputShape": [
176
+ { "name": "id", "type": "string", "description": "Unique 10-char run ID" },
177
+ { "name": "flow", "type": "string", "description": "Flow name" },
178
+ { "name": "result", "type": "pass|fail", "description": "Overall result" },
179
+ { "name": "duration", "type": "number", "description": "Total milliseconds" },
180
+ { "name": "steps", "type": "StepResult[]", "description": "Per-step results" }
181
+ ]
182
+ }
183
+ ```
184
+
185
+ Commands with `outputShape`: `run`, `push`, `get`. Use `flow-walker schema <cmd>` to inspect.
186
+
187
+ ### Agent-readable run data
188
+
189
+ After push, structured run data is available. URLs are agent-first — JSON by default:
190
+
191
+ ```bash
192
+ # JSON (default) — for agents
193
+ curl https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK
194
+ curl https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK.json
195
+
196
+ # HTML — for humans
197
+ open https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK.html
198
+ ```
199
+
200
+ Returns run.json structure (without local file paths like video/screenshot filenames).
201
+
202
+ CLI equivalent:
203
+
204
+ ```bash
205
+ flow-walker get 25h7afGwBK # pretty-printed JSON
206
+ flow-walker get 25h7afGwBK --json # compact JSON (pipe-friendly)
207
+ flow-walker get 25h7afGwBK | jq '.steps[] | select(.status=="fail")'
208
+ ```
209
+
210
+ ### Structured error (on failure)
211
+
212
+ ```json
213
+ {
214
+ "error": {
215
+ "code": "INVALID_INPUT",
216
+ "message": "Path contains traversal sequences",
217
+ "hint": "Remove .. from path",
218
+ "diagnosticId": "a1b2c3d4"
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## YAML flow format
224
+
225
+ ```yaml
226
+ name: flow-name
227
+ description: What this flow tests
228
+ app: Omi # optional: app name
229
+ app_url: https://omi.me # optional: app URL
230
+ covers:
231
+ - app/lib/pages/home.dart
232
+ prerequisites:
233
+ - auth_ready
234
+ setup: normal
235
+
236
+ steps:
237
+ - name: Step description
238
+ press: { type: button, position: rightmost }
239
+ assert:
240
+ interactive_count: { min: 20 }
241
+ has_type: { type: switch, min: 2 }
242
+ screenshot: label
243
+ ```
244
+
245
+ ### Press targets
246
+
247
+ | Target | Syntax |
248
+ |--------|--------|
249
+ | By ref | `{ ref: "@e3" }` |
250
+ | By type | `{ type: button }` |
251
+ | By position | `{ type: button, position: rightmost }` |
252
+ | By nav tab | `{ bottom_nav_tab: 0 }` |
253
+
254
+ ### Assertions
255
+
256
+ | Assertion | Syntax |
257
+ |-----------|--------|
258
+ | Element count | `interactive_count: { min: 20 }` |
259
+ | Nav tabs | `bottom_nav_tabs: { min: 4 }` |
260
+ | Element type | `has_type: { type: switch, min: 2 }` |
261
+ | Text visible | `text_visible: ["Featured", "Home"]` |
262
+ | Text absent | `text_not_visible: ["Error", "Sign In"]` |
263
+
264
+ Text assertions use Android UIAutomator (via `agent-flutter text`) to check visible text from the accessibility layer. This captures text that Marionette snapshots miss (labels, content descriptions, system UI).
265
+
266
+ ## Exit codes
267
+
268
+ | Code | Meaning |
269
+ |------|---------|
270
+ | `0` | Success |
271
+ | `1` | Flow has failing steps |
272
+ | `2` | Error (invalid args, file not found, device error) |
273
+
274
+ ## Error codes
275
+
276
+ Every error returns `{"error": {"code": "...", "message": "...", "hint": "...", "diagnosticId": "..."}}`.
277
+
278
+ | Code | Meaning | Common cause |
279
+ |------|---------|-------------|
280
+ | `INVALID_ARGS` | Bad CLI arguments | Missing required arg, unknown subcommand |
281
+ | `INVALID_INPUT` | Input fails validation | Path traversal, control chars, bad URI format |
282
+ | `FILE_NOT_FOUND` | Required file missing | No flow YAML, no run.json, remote run not found |
283
+ | `FLOW_PARSE_ERROR` | Invalid YAML flow | Malformed YAML, missing name/steps |
284
+ | `COMMAND_FAILED` | External command error | agent-flutter failure, network error, upload failure |
285
+
286
+ ## Environment variables
287
+
288
+ Precedence: CLI flag > env var > default.
289
+
290
+ | Variable | Purpose | Default |
291
+ |----------|---------|---------|
292
+ | `FLOW_WALKER_OUTPUT_DIR` | Default output directory | `./run-output/` |
293
+ | `FLOW_WALKER_AGENT_PATH` | Path to agent-flutter binary | `agent-flutter` |
294
+ | `FLOW_WALKER_DRY_RUN` | Enable dry-run mode | `0` |
295
+ | `FLOW_WALKER_JSON` | Force JSON output | auto (TTY detection) |
296
+ | `FLOW_WALKER_API_URL` | Hosted service URL for push/get | `https://flow-walker.beastoin.workers.dev` |
297
+ | `AGENT_FLUTTER_DEVICE` | ADB device ID | auto-detect |
298
+
299
+ JSON output precedence: `--no-json` > `--json` > `FLOW_WALKER_JSON=1` > TTY auto-detect (non-TTY defaults to JSON).
package/CLAUDE.md ADDED
@@ -0,0 +1,81 @@
1
+ # CLAUDE.md — Working on flow-walker
2
+
3
+ ## Publish rule
4
+
5
+ This repo is the **publish target** only. Source of truth is [`beastoin/autoloop`](https://github.com/beastoin/autoloop).
6
+ All code changes must go through autoloop's phase-gated build loop first, then get copied here for npm publish.
7
+ Do not edit this repo directly — add a new phase program in autoloop instead.
8
+
9
+ ## Project overview
10
+
11
+ `flow-walker` is a Node.js CLI that auto-explores Flutter apps, executes YAML test flows, and generates HTML reports.
12
+ It builds on [agent-flutter](https://github.com/beastoin/agent-flutter) for all device interaction.
13
+
14
+ **Six commands:**
15
+ - `walk` — BFS-explore the app, discover screens, generate YAML flows
16
+ - `run` — Execute a YAML flow, produce run.json + video + screenshots
17
+ - `report` — Generate self-contained HTML report from run results
18
+ - `push` — Upload report to hosted service, return shareable URL
19
+ - `get` — Fetch run data from hosted service by run ID
20
+ - `schema` — Machine-readable command introspection
21
+
22
+ **Design principles:**
23
+ 1. **agent-flutter as transport** — never touches VM Service or ADB directly
24
+ 2. **Fingerprint by structure** — screen identity uses element types/counts, not text
25
+ 3. **Safety first** — blocklist prevents pressing destructive elements
26
+ 4. **Self-contained output** — HTML reports embed everything as base64
27
+ 5. **YAML as contract** — flows are portable, readable, version-controllable
28
+
29
+ ## Architecture
30
+
31
+ - `src/cli.ts` — entry point, arg parsing, subcommand routing
32
+ - `src/walker.ts` — BFS exploration algorithm
33
+ - `src/fingerprint.ts` — screen identity hashing
34
+ - `src/graph.ts` — navigation graph data structure
35
+ - `src/safety.ts` — blocklist evaluation
36
+ - `src/yaml-writer.ts` — YAML flow generation
37
+ - `src/agent-bridge.ts` — thin wrapper around agent-flutter CLI
38
+ - `src/flow-parser.ts` — YAML → Flow object parsing
39
+ - `src/runner.ts` — flow step execution engine
40
+ - `src/reporter.ts` — HTML report generation
41
+ - `src/capture.ts` — video, screenshot, logcat helpers
42
+ - `src/run-schema.ts` — RunResult type + validation + run ID generation
43
+ - `src/types.ts` — shared type definitions
44
+ - `src/errors.ts` — structured error handling (FlowWalkerError)
45
+ - `src/validate.ts` — input validation (paths, URIs, control chars)
46
+ - `src/command-schema.ts` — command schema for agent discovery
47
+ - `src/push.ts` — report upload to hosted service
48
+
49
+ ## Build and test
50
+
51
+ ```bash
52
+ npm install
53
+ npm test # all tests
54
+ npx tsc --noEmit # typecheck
55
+ ```
56
+
57
+ ## Code conventions
58
+
59
+ - TypeScript ESM modules (`.ts` imports with explicit extension)
60
+ - Node built-ins only; no external runtime dependencies
61
+ - Node.js ≥ 22 with `--experimental-strip-types`
62
+ - Exit codes: 0 = success, 1 = flow failure, 2 = error
63
+
64
+ ## Phase history
65
+
66
+ | Phase | Focus | Eval |
67
+ |-------|-------|------|
68
+ | 1 | walk: BFS explorer, fingerprinting, safety, YAML generation | eval.sh (19 gates) |
69
+ | 2 | run + report: flow executor, video/screenshots, HTML viewer | eval2.sh (25 gates) |
70
+ | 3 | Agent-grade: structured errors, schema, input hardening, run IDs | eval3.sh (41 gates) |
71
+ | 4 | Hosted reports: push command, Cloudflare Worker + R2 | eval4.sh (17 gates) |
72
+ | 5 | Landing page: live metrics, stats tracking | eval5.sh (14 gates) |
73
+ | 6 | Agent-friendly run data + app metadata | eval6.sh (13 gates) |
74
+
75
+ ## What not to do
76
+
77
+ - Do not edit this repo directly — use autoloop
78
+ - Do not add external runtime dependencies
79
+ - Do not change exit code semantics
80
+ - Do not bypass blocklist safety checks
81
+ - Do not access VM Service or ADB directly (use agent-flutter)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 beastoin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # flow-walker
2
+
3
+ Auto-discover app flows, execute YAML test flows, generate HTML reports.
4
+
5
+ flow-walker is the **flow layer** — it defines, discovers, executes, and reports on flows. It uses [agent-flutter](https://github.com/beastoin/agent-flutter) and [agent-swift](https://github.com/beastoin/agent-swift) as **transport layers** that control specific platforms.
6
+
7
+ ```
8
+ flow-walker (flows: walk, run, report, push, get, schema)
9
+ |
10
+ agent-flutter (Flutter apps on Android/iOS)
11
+ agent-swift (native macOS/iOS apps)
12
+ |
13
+ devices
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ npm install -g flow-walker-cli
20
+
21
+ # Explore app automatically
22
+ flow-walker walk --app-uri ws://127.0.0.1:38047/abc=/ws
23
+
24
+ # Execute a specific flow
25
+ flow-walker run flows/tab-navigation.yaml
26
+
27
+ # Generate HTML report
28
+ flow-walker report ./run-output/<run-id>/
29
+
30
+ # Share report (hosted)
31
+ flow-walker push ./run-output/<run-id>/
32
+
33
+ # Retrieve run data
34
+ flow-walker get 25h7afGwBK
35
+
36
+ # Discover commands (agent-first)
37
+ flow-walker schema
38
+ flow-walker schema run
39
+
40
+ # Version
41
+ flow-walker --version
42
+ ```
43
+
44
+ ## Prerequisites
45
+
46
+ - Node.js >= 22
47
+ - [agent-flutter](https://github.com/beastoin/agent-flutter) installed and in PATH
48
+ - Flutter app running with Marionette initialized
49
+ - ADB connected (Android) or Simulator running (iOS)
50
+
51
+ ## Commands
52
+
53
+ ### `walk` — Auto-explore
54
+
55
+ Discovers screens by pressing every interactive element, building a navigation graph, and generating YAML flow files.
56
+
57
+ ```bash
58
+ flow-walker walk --app-uri ws://... --max-depth 3 --output-dir ./flows/
59
+ flow-walker walk --skip-connect --json # NDJSON: one event per line
60
+ flow-walker walk --dry-run # plan without pressing
61
+ ```
62
+
63
+ ### `run` — Execute flow
64
+
65
+ Runs a YAML flow step-by-step. Each run gets a **unique ID** (10-char base64url like `P-tnB_sgKA`). Output goes to `<output-dir>/<run-id>/` so multiple runs never overwrite each other.
66
+
67
+ ```bash
68
+ flow-walker run flows/tab-navigation.yaml
69
+ # => Run ID: 25h7afGwBK
70
+ # => Output: ./run-output/25h7afGwBK/
71
+
72
+ flow-walker run flows/login.yaml --json # machine-readable
73
+ flow-walker run flows/settings.yaml --dry-run # parse + resolve without executing
74
+ ```
75
+
76
+ Output per run:
77
+ - `run.json` — structured results with run ID, per-step status, timing, assertions
78
+ - `recording.mp4` — screen recording with step timestamps
79
+ - `step-N-*.png` — per-step screenshots
80
+ - `device.log` — filtered device logs
81
+
82
+ ### `report` — Generate HTML viewer
83
+
84
+ Self-contained HTML with embedded video, screenshots, and clickable step timeline.
85
+
86
+ ```bash
87
+ flow-walker report ./run-output/25h7afGwBK/
88
+ ```
89
+
90
+ ### `push` — Share report
91
+
92
+ Uploads report.html to the hosted service and returns a shareable URL. No auth, no config.
93
+
94
+ ```bash
95
+ flow-walker push ./run-output/25h7afGwBK/
96
+ # => URL: https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK
97
+
98
+ flow-walker push ./run-output/25h7afGwBK/ --json
99
+ # => {"id":"25h7afGwBK","url":"https://...","htmlUrl":"https://....html","expiresAt":"2026-04-11T..."}
100
+ ```
101
+
102
+ Reports are stored for 30 days. Re-pushing the same run is idempotent — returns the same URL with updated expiry. Use `FLOW_WALKER_API_URL` env var to point at a custom server.
103
+
104
+ Push also uploads `run.json` (stripped of local file paths). URL scheme is agent-first:
105
+
106
+ ```bash
107
+ # Agent: JSON by default
108
+ curl https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK
109
+ curl https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK.json
110
+
111
+ # Human: HTML report
112
+ open https://flow-walker.beastoin.workers.dev/runs/25h7afGwBK.html
113
+ ```
114
+
115
+ ### `get` — Retrieve run data
116
+
117
+ Fetches structured run data from the hosted service by run ID. Returns JSON (the same run.json uploaded during push, minus local file paths).
118
+
119
+ ```bash
120
+ flow-walker get 25h7afGwBK # pretty-printed
121
+ flow-walker get 25h7afGwBK --json # compact (pipe-friendly)
122
+ flow-walker get 25h7afGwBK | jq '.steps[] | select(.status=="fail")'
123
+ ```
124
+
125
+ ### `schema` — Agent discovery
126
+
127
+ Machine-readable command introspection. Returns versioned JSON with args, flags (with types), exit codes, and examples.
128
+
129
+ ```bash
130
+ flow-walker schema # all commands with version envelope
131
+ flow-walker schema run # single command detail
132
+ ```
133
+
134
+ Agents can discover capabilities programmatically — no --help parsing needed.
135
+
136
+ ## YAML flow format
137
+
138
+ ```yaml
139
+ name: tab-navigation
140
+ description: Bottom nav bar detection, switch between 4 tabs
141
+ app: Omi # optional: app name (shown in reports)
142
+ app_url: https://omi.me # optional: app URL (linked in reports)
143
+ covers:
144
+ - app/lib/pages/home/page.dart
145
+ prerequisites:
146
+ - auth_ready
147
+ setup: normal
148
+
149
+ steps:
150
+ - name: Verify home tab and nav bar
151
+ assert:
152
+ interactive_count: { min: 20 }
153
+ bottom_nav_tabs: { min: 4 }
154
+ screenshot: tab-home
155
+
156
+ - name: Switch to tab 2
157
+ press: { bottom_nav_tab: 1 }
158
+ screenshot: tab-2
159
+
160
+ - name: Scroll in current tab
161
+ scroll: down
162
+
163
+ - name: Verify developer settings has switches
164
+ assert:
165
+ has_type: { type: switch, min: 2 }
166
+
167
+ - name: Verify visible text on screen
168
+ assert:
169
+ text_visible: ["Featured", "Home"]
170
+ text_not_visible: ["Error", "Sign In"]
171
+
172
+ - name: Return to home tab
173
+ press: { bottom_nav_tab: 0 }
174
+ screenshot: final
175
+ ```
176
+
177
+ ### Step actions
178
+
179
+ | Action | Syntax | Description |
180
+ |--------|--------|-------------|
181
+ | `press` | `{ type: button, position: rightmost }` | Press by type, position, ref, or bottom_nav_tab |
182
+ | `scroll` | `down` / `up` / `left` / `right` | Scroll the screen |
183
+ | `fill` | `{ type: textfield, value: "text" }` | Enter text into a field |
184
+ | `back` | `true` | Navigate back |
185
+ | `assert` | `{ interactive_count: { min: N } }` | Assert element counts, nav tabs, element types |
186
+ | `screenshot` | `name` | Capture screenshot with label |
187
+
188
+ ### Assertions
189
+
190
+ | Assertion | Syntax | Description |
191
+ |-----------|--------|-------------|
192
+ | `interactive_count` | `{ min: 20 }` | Min total interactive elements on screen |
193
+ | `bottom_nav_tabs` | `{ min: 4 }` | Min bottom navigation tabs |
194
+ | `has_type` | `{ type: switch, min: 2 }` | Min elements of a specific type |
195
+ | `text_visible` | `["Featured", "Home"]` | Text must be visible on screen (via UIAutomator) |
196
+ | `text_not_visible` | `["Error", "Sign In"]` | Text must NOT be visible on screen |
197
+
198
+ ## Agent-friendly design
199
+
200
+ Built following [Poehnelt's CLI-for-agents principles](https://justin.poehnelt.com/posts/rewrite-your-cli-for-ai-agents/):
201
+
202
+ - **Schema introspection** — `flow-walker schema` returns versioned JSON with typed args/flags
203
+ - **Structured errors** — every error returns `{code, message, hint, diagnosticId}` in JSON
204
+ - **Input hardening** — path traversal, control chars, URI format all validated
205
+ - **TTY-aware JSON** — `--no-json` > `--json` > `FLOW_WALKER_JSON=1` > TTY auto-detect
206
+ - **Dry-run** — `--dry-run` parses and resolves targets without executing (includes resolve reasons)
207
+ - **NDJSON streaming** — walk emits `walk:start`, `screen`, `edge`, `skip` events as one JSON per line
208
+ - **Unique run IDs** — 10-char base64url per run, filesystem-safe, URL-safe
209
+ - **Hosted sharing** — `flow-walker push` uploads report and returns a URL, no auth needed
210
+ - **Agent-first URLs** — `/runs/:id` defaults to JSON; `.html` suffix for humans
211
+ - **App metadata** — optional `app` + `app_url` in YAML flows, shown in reports and landing page
212
+ - **Environment variables** — `FLOW_WALKER_OUTPUT_DIR`, `FLOW_WALKER_AGENT_PATH`, `FLOW_WALKER_DRY_RUN`, `FLOW_WALKER_JSON`, `FLOW_WALKER_API_URL`
213
+ - **Exit codes** — 0 = success, 1 = flow failure, 2 = error
214
+
215
+ ## Architecture
216
+
217
+ ```
218
+ flow-walker CLI (flow layer)
219
+ | shells out to
220
+ agent-flutter CLI / agent-swift CLI (transport layer)
221
+ | connects via
222
+ VM Service + Marionette / XCTest (platform-specific)
223
+ | controls
224
+ App on device/emulator/desktop
225
+ ```
226
+
227
+ **Design principles:**
228
+ 1. **Pluggable transport** — same YAML flows, different backends
229
+ 2. **Fingerprint by structure** — screen identity uses element types/counts, not text
230
+ 3. **Safety first** — blocklist prevents pressing destructive elements
231
+ 4. **Self-contained output** — HTML reports embed everything as base64
232
+ 5. **YAML as contract** — flows are portable, readable, version-controllable
233
+
234
+ ## Phase history
235
+
236
+ | Phase | Focus | Status |
237
+ |-------|-------|--------|
238
+ | 1 | walk: BFS explorer, fingerprinting, safety, YAML generation | Complete |
239
+ | 2 | run + report: flow executor, video/screenshots, HTML viewer | Complete |
240
+ | 3 | Agent-grade: structured errors, schema, input hardening, run IDs | Complete |
241
+ | 4 | Hosted reports: push command, Cloudflare Worker + R2 | Complete |
242
+ | 5 | Landing page: live metrics, stats tracking | Complete |
243
+ | 6 | Agent-friendly run data + app metadata | Complete |
244
+
245
+ ## License
246
+
247
+ MIT
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "flow-walker-cli",
3
+ "version": "0.2.0",
4
+ "description": "Automatic app flow extraction via agent-flutter",
5
+ "type": "module",
6
+ "bin": {
7
+ "flow-walker": "./src/cli.ts"
8
+ },
9
+ "scripts": {
10
+ "test": "node --experimental-strip-types --test tests/*.test.ts",
11
+ "build": "tsc --noEmit"
12
+ },
13
+ "engines": {
14
+ "node": ">=22"
15
+ },
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "typescript": "^5.7.0",
19
+ "@types/node": "^22.0.0"
20
+ }
21
+ }