agent-device 0.6.2 → 0.7.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.
@@ -258,7 +258,7 @@
258
258
  MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
259
259
  MTL_FAST_MATH = YES;
260
260
  ONLY_ACTIVE_ARCH = YES;
261
- SDKROOT = iphoneos;
261
+ SDKROOT = auto;
262
262
  SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
263
263
  SWIFT_OPTIMIZATION_LEVEL = "-Onone";
264
264
  };
@@ -315,7 +315,7 @@
315
315
  LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
316
316
  MTL_ENABLE_DEBUG_INFO = NO;
317
317
  MTL_FAST_MATH = YES;
318
- SDKROOT = iphoneos;
318
+ SDKROOT = auto;
319
319
  SWIFT_COMPILATION_MODE = wholemodule;
320
320
  VALIDATE_PRODUCT = YES;
321
321
  };
@@ -350,7 +350,9 @@
350
350
  SWIFT_EMIT_LOC_STRINGS = YES;
351
351
  SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
352
352
  SWIFT_VERSION = 5.0;
353
- TARGETED_DEVICE_FAMILY = "1,2";
353
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
354
+ TARGETED_DEVICE_FAMILY = "1,2,3";
355
+ TVOS_DEPLOYMENT_TARGET = 15.6;
354
356
  };
355
357
  name = Debug;
356
358
  };
@@ -383,7 +385,9 @@
383
385
  SWIFT_EMIT_LOC_STRINGS = YES;
384
386
  SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
385
387
  SWIFT_VERSION = 5.0;
386
- TARGETED_DEVICE_FAMILY = "1,2";
388
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
389
+ TARGETED_DEVICE_FAMILY = "1,2,3";
390
+ TVOS_DEPLOYMENT_TARGET = 15.6;
387
391
  };
388
392
  name = Release;
389
393
  };
@@ -404,7 +408,9 @@
404
408
  SWIFT_OBJC_BRIDGING_HEADER = "AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h";
405
409
  SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
406
410
  SWIFT_VERSION = 5.0;
407
- TARGETED_DEVICE_FAMILY = "1,2";
411
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
412
+ TARGETED_DEVICE_FAMILY = "1,2,3";
413
+ TVOS_DEPLOYMENT_TARGET = 15.6;
408
414
  TEST_TARGET_NAME = AgentDeviceRunner;
409
415
  };
410
416
  name = Debug;
@@ -426,7 +432,9 @@
426
432
  SWIFT_OBJC_BRIDGING_HEADER = "AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h";
427
433
  SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
428
434
  SWIFT_VERSION = 5.0;
429
- TARGETED_DEVICE_FAMILY = "1,2";
435
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
436
+ TARGETED_DEVICE_FAMILY = "1,2,3";
437
+ TVOS_DEPLOYMENT_TARGET = 15.6;
430
438
  TEST_TARGET_NAME = AgentDeviceRunner;
431
439
  };
432
440
  name = Release;
@@ -40,6 +40,7 @@ final class RunnerTests: XCTestCase {
40
40
  private let postSnapshotInteractionDelay: TimeInterval = 0.2
41
41
  private let firstInteractionAfterActivateDelay: TimeInterval = 0.25
42
42
  private let scrollInteractionIdleTimeoutDefault: TimeInterval = 1.0
43
+ private let tvRemoteDoublePressDelayDefault: TimeInterval = 0.0
43
44
  private let minRecordingFps = 1
44
45
  private let maxRecordingFps = 120
45
46
  private var needsPostSnapshotInteractionDelay = false
@@ -783,6 +784,28 @@ final class RunnerTests: XCTestCase {
783
784
  }
784
785
  needsPostSnapshotInteractionDelay = true
785
786
  return Response(ok: true, data: snapshotFast(app: activeApp, options: options))
787
+ case .screenshot:
788
+ // If a target app bundle ID is provided, activate it first so the screenshot
789
+ // captures the target app rather than the AgentDeviceRunner itself.
790
+ if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
791
+ let targetApp = XCUIApplication(bundleIdentifier: bundleId)
792
+ targetApp.activate()
793
+ // Brief wait for the app transition animation to complete
794
+ Thread.sleep(forTimeInterval: 0.5)
795
+ }
796
+ let screenshot = XCUIScreen.main.screenshot()
797
+ guard let pngData = screenshot.image.pngData() else {
798
+ return Response(ok: false, error: ErrorPayload(message: "Failed to encode screenshot as PNG"))
799
+ }
800
+ let fileName = "screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).png"
801
+ let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName)
802
+ do {
803
+ try pngData.write(to: URL(fileURLWithPath: filePath))
804
+ } catch {
805
+ return Response(ok: false, error: ErrorPayload(message: "Failed to write screenshot: \(error.localizedDescription)"))
806
+ }
807
+ // Return path relative to app container root (tmp/ maps to NSTemporaryDirectory)
808
+ return Response(ok: true, data: DataPayload(message: "tmp/\(fileName)"))
786
809
  case .back:
787
810
  if tapNavigationBack(app: activeApp) {
788
811
  return Response(ok: true, data: DataPayload(message: "back"))
@@ -790,7 +813,7 @@ final class RunnerTests: XCTestCase {
790
813
  performBackGesture(app: activeApp)
791
814
  return Response(ok: true, data: DataPayload(message: "back"))
792
815
  case .home:
793
- XCUIDevice.shared.press(.home)
816
+ pressHomeButton()
794
817
  return Response(ok: true, data: DataPayload(message: "home"))
795
818
  case .appSwitcher:
796
819
  performAppSwitcherGesture(app: activeApp)
@@ -934,7 +957,7 @@ final class RunnerTests: XCTestCase {
934
957
 
935
958
  private func isReadOnlyCommand(_ command: Command) -> Bool {
936
959
  switch command.command {
937
- case .findText, .snapshot:
960
+ case .findText, .snapshot, .screenshot:
938
961
  return true
939
962
  case .alert:
940
963
  let action = (command.action ?? "get").lowercased()
@@ -961,7 +984,7 @@ final class RunnerTests: XCTestCase {
961
984
 
962
985
  private func isRunnerLifecycleCommand(_ command: CommandType) -> Bool {
963
986
  switch command {
964
- case .shutdown, .recordStop:
987
+ case .shutdown, .recordStop, .screenshot:
965
988
  return true
966
989
  default:
967
990
  return false
@@ -990,10 +1013,13 @@ final class RunnerTests: XCTestCase {
990
1013
  back.tap()
991
1014
  return true
992
1015
  }
993
- return false
1016
+ return pressTvRemoteMenuIfAvailable()
994
1017
  }
995
1018
 
996
1019
  private func performBackGesture(app: XCUIApplication) {
1020
+ if pressTvRemoteMenuIfAvailable() {
1021
+ return
1022
+ }
997
1023
  let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
998
1024
  let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.05, dy: 0.5))
999
1025
  let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5))
@@ -1001,12 +1027,64 @@ final class RunnerTests: XCTestCase {
1001
1027
  }
1002
1028
 
1003
1029
  private func performAppSwitcherGesture(app: XCUIApplication) {
1030
+ if performTvRemoteAppSwitcherIfAvailable() {
1031
+ return
1032
+ }
1004
1033
  let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
1005
1034
  let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.99))
1006
1035
  let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.7))
1007
1036
  start.press(forDuration: 0.6, thenDragTo: end)
1008
1037
  }
1009
1038
 
1039
+ private func pressHomeButton() {
1040
+ if pressTvRemoteHomeIfAvailable() {
1041
+ return
1042
+ }
1043
+ XCUIDevice.shared.press(.home)
1044
+ }
1045
+
1046
+ private func pressTvRemoteMenuIfAvailable() -> Bool {
1047
+ #if os(tvOS)
1048
+ XCUIRemote.shared.press(.menu)
1049
+ return true
1050
+ #else
1051
+ return false
1052
+ #endif
1053
+ }
1054
+
1055
+ private func pressTvRemoteHomeIfAvailable() -> Bool {
1056
+ #if os(tvOS)
1057
+ XCUIRemote.shared.press(.home)
1058
+ return true
1059
+ #else
1060
+ return false
1061
+ #endif
1062
+ }
1063
+
1064
+ private func performTvRemoteAppSwitcherIfAvailable() -> Bool {
1065
+ #if os(tvOS)
1066
+ XCUIRemote.shared.press(.home)
1067
+ sleepFor(resolveTvRemoteDoublePressDelay())
1068
+ XCUIRemote.shared.press(.home)
1069
+ return true
1070
+ #else
1071
+ return false
1072
+ #endif
1073
+ }
1074
+
1075
+ private func resolveTvRemoteDoublePressDelay() -> TimeInterval {
1076
+ guard
1077
+ let raw = ProcessInfo.processInfo.environment["AGENT_DEVICE_TV_REMOTE_DOUBLE_PRESS_DELAY_MS"],
1078
+ !raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
1079
+ else {
1080
+ return tvRemoteDoublePressDelayDefault
1081
+ }
1082
+ guard let parsedMs = Double(raw), parsedMs >= 0 else {
1083
+ return tvRemoteDoublePressDelayDefault
1084
+ }
1085
+ return min(parsedMs, 1000) / 1000.0
1086
+ }
1087
+
1010
1088
  private func findElement(app: XCUIApplication, text: String) -> XCUIElement? {
1011
1089
  let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@ OR value CONTAINS[c] %@", text, text, text)
1012
1090
  let element = app.descendants(matching: .any).matching(predicate).firstMatch
@@ -1109,6 +1187,9 @@ final class RunnerTests: XCTestCase {
1109
1187
  }
1110
1188
 
1111
1189
  private func swipe(app: XCUIApplication, direction: SwipeDirection) {
1190
+ if performTvRemoteSwipeIfAvailable(direction: direction) {
1191
+ return
1192
+ }
1112
1193
  let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
1113
1194
  let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.2))
1114
1195
  let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
@@ -1127,6 +1208,24 @@ final class RunnerTests: XCTestCase {
1127
1208
  }
1128
1209
  }
1129
1210
 
1211
+ private func performTvRemoteSwipeIfAvailable(direction: SwipeDirection) -> Bool {
1212
+ #if os(tvOS)
1213
+ switch direction {
1214
+ case .up:
1215
+ XCUIRemote.shared.press(.up)
1216
+ case .down:
1217
+ XCUIRemote.shared.press(.down)
1218
+ case .left:
1219
+ XCUIRemote.shared.press(.left)
1220
+ case .right:
1221
+ XCUIRemote.shared.press(.right)
1222
+ }
1223
+ return true
1224
+ #else
1225
+ return false
1226
+ #endif
1227
+ }
1228
+
1130
1229
  private func pinch(app: XCUIApplication, scale: Double, x: Double?, y: Double?) {
1131
1230
  let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
1132
1231
 
@@ -1741,6 +1840,7 @@ enum CommandType: String, Codable {
1741
1840
  case swipe
1742
1841
  case findText
1743
1842
  case snapshot
1843
+ case screenshot
1744
1844
  case back
1745
1845
  case home
1746
1846
  case appSwitcher
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-device",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Callstack",
@@ -16,7 +16,9 @@
16
16
  "build": "rslib build",
17
17
  "clean:daemon": "rm -f ~/.agent-device/daemon.json && rm -f ~/.agent-device/daemon.lock",
18
18
  "build:node": "pnpm build && pnpm clean:daemon",
19
- "build:xcuitest": "rm -rf ~/.agent-device/ios-runner/derived/device && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=iOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived",
19
+ "build:xcuitest": "pnpm build:xcuitest:ios",
20
+ "build:xcuitest:ios": "rm -rf ~/.agent-device/ios-runner/derived/device && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=iOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived",
21
+ "build:xcuitest:tvos": "rm -rf ~/.agent-device/ios-runner/derived/tvos && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=tvOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived/tvos",
20
22
  "build:all": "pnpm build:node && pnpm build:xcuitest",
21
23
  "ad": "node bin/agent-device.mjs",
22
24
  "format": "prettier --write .",
@@ -6,6 +6,7 @@ description: Automates interactions for iOS simulators/devices and Android emula
6
6
  # Mobile Automation with agent-device
7
7
 
8
8
  For exploration, use snapshot refs. For deterministic replay, use selectors.
9
+ For structured exploratory QA bug hunts and reporting, use [../dogfood/SKILL.md](../dogfood/SKILL.md).
9
10
 
10
11
  ## Start Here (Read This First)
11
12
 
@@ -22,7 +23,7 @@ Use this skill as a router, not a full manual.
22
23
 
23
24
  - No target context yet: `devices` -> pick target -> `open`.
24
25
  - Normal UI task: `open` -> `snapshot -i` -> `press/fill` -> `diff snapshot -i` -> `close`
25
- - Debug/crash: `open <app>` -> `logs clear --restart` -> reproduce -> `logs path` -> targeted `grep`
26
+ - Debug/crash: `open <app>` -> `logs clear --restart` -> reproduce -> `network dump` -> `logs path` -> targeted `grep`
26
27
  - Replay drift: `replay -u <path>` -> verify updated selectors
27
28
 
28
29
  ## Canonical Flows
@@ -43,6 +44,7 @@ agent-device close
43
44
  ```bash
44
45
  agent-device open MyApp --platform ios
45
46
  agent-device logs clear --restart
47
+ agent-device network dump 25
46
48
  agent-device logs path
47
49
  ```
48
50
 
@@ -68,6 +70,14 @@ agent-device session list
68
70
  ```
69
71
 
70
72
  Use `boot` only as fallback when `open` cannot find/connect to a ready target.
73
+ Use `--target mobile|tv` with `--platform` (required) to pick phone/tablet vs TV targets (AndroidTV/tvOS).
74
+
75
+ TV quick reference:
76
+ - AndroidTV: `open`/`apps` use TV launcher discovery automatically.
77
+ - TV target selection works on emulators/simulators and connected physical devices (AndroidTV + AppleTV).
78
+ - tvOS: runner-driven interactions and snapshots are supported (`snapshot`, `wait`, `press`, `fill`, `get`, `scroll`, `back`, `home`, `app-switcher`, `record` and related selector flows).
79
+ - tvOS `back`/`home`/`app-switcher` map to Siri Remote actions (`menu`, `home`, double-home) in the runner.
80
+ - tvOS follows iOS simulator-only command semantics for helpers like `pinch`, `settings`, and `push`.
71
81
 
72
82
  ### Snapshot and targeting
73
83
 
@@ -86,6 +96,11 @@ agent-device is visible 'id="anchor"'
86
96
 
87
97
  ```bash
88
98
  agent-device appstate
99
+ agent-device clipboard read
100
+ agent-device clipboard write "token"
101
+ agent-device perf --json
102
+ agent-device network dump [limit] [summary|headers|body|all]
103
+ agent-device push <bundle|package> <payload.json|inline-json>
89
104
  agent-device get text @e1
90
105
  agent-device screenshot out.png
91
106
  agent-device settings permission grant notifications
@@ -100,6 +115,11 @@ agent-device trace stop ./trace.log
100
115
  agent-device batch --steps-file /tmp/batch-steps.json --json
101
116
  ```
102
117
 
118
+ ### Performance Check
119
+
120
+ - Use `agent-device perf --json` (or `metrics --json`) after `open`.
121
+ - For detailed metric semantics, caveats, and interpretation guidance, see [references/perf-metrics.md](references/perf-metrics.md).
122
+
103
123
  ## Guardrails (High Value Only)
104
124
 
105
125
  - Re-snapshot after UI mutations (navigation/modal/list changes).
@@ -107,7 +127,13 @@ agent-device batch --steps-file /tmp/batch-steps.json --json
107
127
  - Use refs for discovery, selectors for replay/assertions.
108
128
  - Use `fill` for clear-then-type semantics; use `type` for focused append typing.
109
129
  - iOS `appstate` is session-scoped; Android `appstate` is live foreground state.
110
- - iOS settings helpers are simulator-only; use faceid `match|nonmatch|enroll|unenroll`.
130
+ - Clipboard helpers: `clipboard read` / `clipboard write <text>` are supported on Android and iOS simulators; iOS physical devices are not supported yet.
131
+ - `network dump` is best-effort and parses HTTP(s) entries from the session app log file.
132
+ - iOS settings helpers are simulator-only; use `appearance light|dark|toggle` and faceid `match|nonmatch|enroll|unenroll`.
133
+ - For AndroidTV/tvOS selection, always pair `--target` with `--platform` (`ios`, `android`, or `apple` alias); target-only selection is invalid.
134
+ - `push` simulates notification delivery:
135
+ - iOS simulator uses APNs-style payload JSON.
136
+ - Android uses broadcast action + typed extras (string/boolean/number).
111
137
  - Permission settings are app-scoped and require an active session app:
112
138
  `settings permission <grant|deny|reset> <camera|microphone|photos|contacts|notifications> [full|limited]`
113
139
  - `full|limited` mode applies only to iOS `photos`; other targets reject mode.
@@ -138,3 +164,4 @@ agent-device batch --steps-file /tmp/batch-steps.json --json
138
164
  - [references/video-recording.md](references/video-recording.md)
139
165
  - [references/coordinate-system.md](references/coordinate-system.md)
140
166
  - [references/batching.md](references/batching.md)
167
+ - [references/perf-metrics.md](references/perf-metrics.md)
@@ -1,6 +1,7 @@
1
1
  # Logs (Token-Efficient Debugging)
2
2
 
3
3
  Logging is off by default in normal flows. Enable it on demand for debugging windows. App output is written to a session-scoped file so agents can grep it instead of loading full logs into context.
4
+ `network dump` parses recent HTTP(s) entries from this same session app log file.
4
5
 
5
6
  ## Data Handling
6
7
 
@@ -23,6 +24,7 @@ Logging is off by default in normal flows. Enable it on demand for debugging win
23
24
  ```bash
24
25
  agent-device open MyApp --platform ios # or --platform android
25
26
  agent-device logs clear --restart # Preferred: stop stream, clear logs, and start streaming again
27
+ agent-device network dump 25 # Parse latest HTTP(s) requests (method/url/status) from app.log
26
28
  agent-device logs path # Print path, e.g. ~/.agent-device/sessions/default/app.log
27
29
  agent-device logs doctor # Check tool/runtime readiness for current session/device
28
30
  agent-device logs mark "before tap" # Insert a timeline marker into app.log
@@ -41,10 +43,13 @@ Precondition: `logs clear --restart` requires an active app session (`open <app>
41
43
  - `logs clear --restart`: convenience reset for repro loops (stop stream, clear files, restart stream).
42
44
  - `logs doctor`: reports backend/tool checks and readiness notes for troubleshooting.
43
45
  - `logs mark`: writes a timestamped marker line to the session log.
46
+ - `network dump [limit] [summary|headers|body|all]`: parses recent HTTP(s) lines from the session app log and returns request summaries.
47
+ - `network log ...`: alias for `network dump`.
44
48
 
45
49
  ## Behavior and Limits
46
50
 
47
51
  - `logs start` appends to `app.log` and rotates to `app.log.1` when `app.log` exceeds 5 MB.
52
+ - `network dump` scans the last 4000 app-log lines, returns up to 200 entries, and truncates payload/header fields at 2048 characters.
48
53
  - Android log streaming automatically rebinds to the app PID after process restarts.
49
54
  - iOS log capture relies on Unified Logging signals (for example `os_log`); plain stdout/stderr output may be limited depending on app/runtime.
50
55
  - Retention knobs:
@@ -0,0 +1,53 @@
1
+ # Performance Metrics (`perf` / `metrics`)
2
+
3
+ Use this reference when you need to measure launch performance in agent workflows.
4
+
5
+ ## Quick flow
6
+
7
+ ```bash
8
+ agent-device open Settings --platform ios
9
+ agent-device perf --json
10
+ ```
11
+
12
+ Alias:
13
+
14
+ ```bash
15
+ agent-device metrics --json
16
+ ```
17
+
18
+ ## What is measured today
19
+
20
+ - Session-scoped `startup` timing only.
21
+ - Sampling method: `open-command-roundtrip`.
22
+ - Unit: milliseconds (`ms`).
23
+ - Source: elapsed wall-clock time around each session `open` command dispatch for the active app target.
24
+
25
+ ## Output fields to use
26
+
27
+ - `metrics.startup.lastDurationMs`: most recent startup sample.
28
+ - `metrics.startup.lastMeasuredAt`: ISO timestamp of most recent sample.
29
+ - `metrics.startup.sampleCount`: number of retained samples.
30
+ - `metrics.startup.samples[]`: recent startup history for the current session.
31
+ - `sampling.startup.method`: current sampling method identifier.
32
+
33
+ ## Platform support (current)
34
+
35
+ - iOS simulator: supported for startup sampling.
36
+ - iOS physical device: supported for startup sampling.
37
+ - Android emulator/device: supported for startup sampling.
38
+ - `fps`, `memory`, and `cpu`: currently placeholders (`available: false`).
39
+
40
+ ## Interpretation guidance
41
+
42
+ - Treat startup values as command round-trip timing, not true app first-frame or first-interactive telemetry.
43
+ - Compare like-for-like runs:
44
+ - same device target
45
+ - same app build
46
+ - same workflow/session steps
47
+ - Use multiple runs and compare trend/median, not one-off samples.
48
+
49
+ ## Common pitfalls
50
+
51
+ - Running `perf` before any `open` in the session yields no startup sample yet.
52
+ - Comparing values across different devices/runtimes introduces large noise.
53
+ - Interpreting current `startup` as CPU/FPS/memory would be incorrect.
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: dogfood
3
+ description: Systematically explore and test a mobile app on iOS/Android with agent-device to find bugs, UX issues, and other problems. Use when asked to "dogfood", "QA", "exploratory test", "find issues", "bug hunt", or "test this app" on mobile. Produces a structured report with reproducible evidence: screenshots, optional repro videos, and detailed steps for every issue.
4
+ allowed-tools: Bash(agent-device:*), Bash(npx agent-device:*)
5
+ ---
6
+
7
+ # Dogfood (agent-device)
8
+
9
+ Systematically explore a mobile app, find issues, and produce a report with full reproduction evidence for every finding.
10
+
11
+ ## Setup
12
+
13
+ Only the **Target app** is required. Everything else has sensible defaults.
14
+
15
+ | Parameter | Default | Example override |
16
+ |-----------|---------|-----------------|
17
+ | **Target app** | _(required)_ | `Settings`, `com.example.app`, deep link URL |
18
+ | **Platform** | Infer from user context; otherwise ask (`ios` or `android`) | `--platform ios` |
19
+ | **Session name** | Slugified app/platform (for example `settings-ios`) | `--session my-session` |
20
+ | **Output directory** | `./dogfood-output/` | `Output directory: /tmp/mobile-qa` |
21
+ | **Scope** | Full app | `Focus on onboarding and profile` |
22
+ | **Authentication** | None | `Sign in to user@example.com` |
23
+
24
+ If the user gives enough context to start, begin immediately with defaults. Ask follow-up only when a required detail is missing (for example platform or credentials).
25
+
26
+ Prefer direct `agent-device` binary when available.
27
+
28
+ ## Workflow
29
+
30
+ ```
31
+ 1. Initialize Set up session, output dirs, report file
32
+ 2. Launch/Auth Open app and sign in if needed
33
+ 3. Orient Capture initial snapshot and map navigation
34
+ 4. Explore Systematically test flows and states
35
+ 5. Document Record reproducible evidence per issue
36
+ 6. Wrap up Reconcile summary, close session
37
+ ```
38
+
39
+ ### 1. Initialize
40
+
41
+ ```bash
42
+ mkdir -p {OUTPUT_DIR}/screenshots {OUTPUT_DIR}/videos
43
+ cp {SKILL_DIR}/templates/dogfood-report-template.md {OUTPUT_DIR}/report.md
44
+ ```
45
+
46
+ ### 2. Launch/Auth
47
+
48
+ Start a named session and launch target app:
49
+
50
+ ```bash
51
+ agent-device --session {SESSION} open {TARGET_APP} --platform {PLATFORM}
52
+ agent-device --session {SESSION} snapshot -i
53
+ ```
54
+
55
+ If login is required:
56
+
57
+ ```bash
58
+ agent-device --session {SESSION} snapshot -i
59
+ agent-device --session {SESSION} fill @e1 "{EMAIL}"
60
+ agent-device --session {SESSION} fill @e2 "{PASSWORD}"
61
+ agent-device --session {SESSION} press @e3
62
+ agent-device --session {SESSION} wait 1000
63
+ agent-device --session {SESSION} snapshot -i
64
+ ```
65
+
66
+ For OTP/email codes: ask the user, wait for input, then continue.
67
+
68
+ ### 3. Orient
69
+
70
+ Capture initial evidence and navigation anchors:
71
+
72
+ ```bash
73
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/initial.png
74
+ agent-device --session {SESSION} snapshot -i
75
+ ```
76
+
77
+ Map top-level navigation, tabs, and key workflows before deep testing.
78
+
79
+ ### 4. Explore
80
+
81
+ Read [references/issue-taxonomy.md](references/issue-taxonomy.md) for severity/category calibration.
82
+
83
+ Strategy:
84
+
85
+ - Move through each major app area (tabs, drawers, settings pages).
86
+ - Test core journeys end-to-end (create, edit, delete, submit, recover).
87
+ - Validate edge states (empty/error/loading/offline/permissions denied).
88
+ - Use `diff snapshot -i` after UI transitions to avoid stale refs.
89
+ - Periodically capture `logs path` and inspect the app log when behavior looks suspicious.
90
+
91
+ Useful commands per screen:
92
+
93
+ ```bash
94
+ agent-device --session {SESSION} snapshot -i
95
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/{screen-name}.png
96
+ agent-device --session {SESSION} appstate
97
+ agent-device --session {SESSION} logs path
98
+ ```
99
+
100
+ ### 5. Document Issues (Repro-First)
101
+
102
+ Explore and document in one pass. When you find an issue, stop and fully capture evidence before continuing.
103
+
104
+ #### Interactive/behavioral issues
105
+
106
+ Use video + step screenshots:
107
+
108
+ 1. Start recording:
109
+
110
+ ```bash
111
+ agent-device --session {SESSION} record start {OUTPUT_DIR}/videos/issue-{NNN}-repro.mp4
112
+ ```
113
+
114
+ 2. Reproduce with visible pacing. Capture each step:
115
+
116
+ ```bash
117
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-step-1.png
118
+ sleep 1
119
+ # perform action
120
+ sleep 1
121
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-step-2.png
122
+ ```
123
+
124
+ 3. Capture final broken state:
125
+
126
+ ```bash
127
+ sleep 2
128
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-result.png
129
+ ```
130
+
131
+ 4. Stop recording:
132
+
133
+ ```bash
134
+ agent-device --session {SESSION} record stop
135
+ ```
136
+
137
+ 5. Append issue immediately to report with numbered steps and screenshot references.
138
+
139
+ #### Static/on-load issues
140
+
141
+ Single screenshot is sufficient; no video required:
142
+
143
+ ```bash
144
+ agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}.png
145
+ ```
146
+
147
+ Set **Repro Video** to `N/A` in the report.
148
+
149
+ ### 6. Wrap Up
150
+
151
+ Target 5-10 well-evidenced issues, then finish:
152
+
153
+ 1. Reconcile summary severity counts in `report.md`.
154
+ 2. Close session:
155
+
156
+ ```bash
157
+ agent-device --session {SESSION} close
158
+ ```
159
+
160
+ 3. Report total issues, severity breakdown, and highest-risk findings.
161
+
162
+ ## Guidance
163
+
164
+ - Repro quality matters more than issue count.
165
+ - Use refs (`@eN`) for fast exploration, selectors for deterministic replay assertions when needed.
166
+ - Re-snapshot after any mutation (navigation, modal, list update, form submit).
167
+ - Use `fill` for clear-then-type semantics; use `type` for incremental typing behavior checks.
168
+ - Keep logs optional and targeted: enable/read app logs only when useful for diagnosis.
169
+ - Never read source code of the app under test; findings must come from observed runtime behavior.
170
+ - Write each issue immediately to avoid losing evidence.
171
+ - Never delete screenshots/videos/report artifacts during a session.
172
+
173
+ ## References
174
+
175
+ | Reference | When to Read |
176
+ |-----------|--------------|
177
+ | [references/issue-taxonomy.md](references/issue-taxonomy.md) | Start of session; severity/categories/checklist |
178
+
179
+ ## Templates
180
+
181
+ | Template | Purpose |
182
+ |----------|---------|
183
+ | [templates/dogfood-report-template.md](templates/dogfood-report-template.md) | Copy into output directory as the report file |