@yoqa/cli 0.3.27 → 0.3.28

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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/main.js +117 -17
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @yoqa/cli
2
2
 
3
- CLI for [Yoqa](https://yoqa.mintlify.app/docs/overview) — drive a local runner over HTTP from your terminal or CI.
3
+ CLI for [Yoqa](https://yoqa.arisn.dev/docs/overview) — drive a local runner over HTTP from your terminal or CI.
4
4
 
5
5
  ```bash
6
6
  npm install -g @yoqa/cli
@@ -18,4 +18,4 @@ yoqa serve
18
18
 
19
19
  Disable auto-start with `YOQA_NO_AUTOSTART=1`. Override the URL with `YOQA_RUNNER_HOST` / `YOQA_RUNNER_PORT` / `YOQA_RUNNER_URL`.
20
20
 
21
- See [CLI docs](https://yoqa.mintlify.app/docs/cli) and [GitHub Actions](https://yoqa.mintlify.app/docs/github-actions).
21
+ See [CLI docs](https://yoqa.arisn.dev/docs/cli) and [GitHub Actions](https://yoqa.arisn.dev/docs/github-actions).
package/dist/main.js CHANGED
@@ -6263,6 +6263,7 @@ var catalogAppSchema = exports_external.object({
6263
6263
  name: exports_external.string().min(1),
6264
6264
  prefix: exports_external.string().min(1),
6265
6265
  context: exports_external.string(),
6266
+ knowledge: exports_external.string(),
6266
6267
  iosBundleId: exports_external.string(),
6267
6268
  iosAppStoreId: exports_external.string(),
6268
6269
  androidApplicationId: exports_external.string(),
@@ -6278,6 +6279,7 @@ var updateAppRequestSchema = exports_external.object({
6278
6279
  name: exports_external.string().min(1).optional(),
6279
6280
  prefix: exports_external.string().min(1).optional(),
6280
6281
  context: exports_external.string().optional(),
6282
+ knowledge: exports_external.string().optional(),
6281
6283
  iosBundleId: exports_external.string().optional(),
6282
6284
  iosAppStoreId: exports_external.string().optional(),
6283
6285
  androidApplicationId: exports_external.string().optional(),
@@ -6591,7 +6593,8 @@ var probeProviderResponseSchema = exports_external.object({
6591
6593
  var providerModelSchema = exports_external.object({
6592
6594
  id: exports_external.string().min(1),
6593
6595
  name: exports_external.string().min(1),
6594
- provider: exports_external.string().min(1).optional()
6596
+ provider: exports_external.string().min(1).optional(),
6597
+ vision: exports_external.boolean().optional()
6595
6598
  });
6596
6599
  var listProviderModelsResponseSchema = exports_external.object({
6597
6600
  models: exports_external.array(providerModelSchema),
@@ -6620,6 +6623,7 @@ var runExecutionModeSchema = exports_external.union([
6620
6623
  exports_external.literal("script"),
6621
6624
  exports_external.literal("agent")
6622
6625
  ]);
6626
+ var runScreenModeSchema = exports_external.union([exports_external.literal("vision"), exports_external.literal("tree")]);
6623
6627
  var createRunRequestSchema = exports_external.object({
6624
6628
  appId: exports_external.string().min(1),
6625
6629
  caseIds: exports_external.array(exports_external.string().min(1)).min(1),
@@ -6627,7 +6631,17 @@ var createRunRequestSchema = exports_external.object({
6627
6631
  platform: devicePlatformSchema,
6628
6632
  buildId: exports_external.string().min(1).optional(),
6629
6633
  buildPath: exports_external.string().min(1).optional(),
6630
- executionMode: runExecutionModeSchema.optional()
6634
+ executionMode: runExecutionModeSchema.optional(),
6635
+ screenMode: runScreenModeSchema.optional()
6636
+ });
6637
+ var stepPhasesSchema = exports_external.object({
6638
+ captureMs: exports_external.number().nonnegative(),
6639
+ screenMs: exports_external.number().nonnegative(),
6640
+ prepareMs: exports_external.number().nonnegative(),
6641
+ decideMs: exports_external.number().nonnegative(),
6642
+ actionMs: exports_external.number().nonnegative(),
6643
+ settleMs: exports_external.number().nonnegative(),
6644
+ decideRetries: exports_external.number().int().nonnegative()
6631
6645
  });
6632
6646
  var runStepSchema = exports_external.object({
6633
6647
  id: exports_external.string().min(1),
@@ -6637,6 +6651,7 @@ var runStepSchema = exports_external.object({
6637
6651
  screenshotUri: exports_external.string().nullable(),
6638
6652
  ok: exports_external.boolean(),
6639
6653
  latencyMs: exports_external.number().nonnegative(),
6654
+ phases: stepPhasesSchema.nullable().optional(),
6640
6655
  detail: exports_external.string().nullable(),
6641
6656
  command: exports_external.string().nullable().optional(),
6642
6657
  createdAt: exports_external.number().int().nonnegative()
@@ -6661,6 +6676,7 @@ var runSchema = exports_external.object({
6661
6676
  buildId: exports_external.string().nullable(),
6662
6677
  status: runStatusSchema,
6663
6678
  executionMode: runExecutionModeSchema,
6679
+ screenMode: runScreenModeSchema.optional(),
6664
6680
  error: exports_external.string().nullable(),
6665
6681
  createdAt: exports_external.number().int().nonnegative(),
6666
6682
  startedAt: exports_external.number().int().nonnegative().nullable(),
@@ -6746,7 +6762,16 @@ var actionRequestSchema = exports_external.object({
6746
6762
  appId: exports_external.string().optional(),
6747
6763
  url: exports_external.string().optional(),
6748
6764
  alertAction: exports_external.union([exports_external.literal("accept"), exports_external.literal("dismiss")]).optional(),
6749
- seconds: exports_external.number().optional()
6765
+ seconds: exports_external.number().optional(),
6766
+ screenshot: exports_external.boolean().optional(),
6767
+ settleMs: exports_external.number().int().min(0).max(1e4).optional()
6768
+ });
6769
+ var actionResultScreenshotSchema = exports_external.object({
6770
+ path: exports_external.string().min(1),
6771
+ annotatedPath: exports_external.string().min(1).optional(),
6772
+ settled: exports_external.boolean(),
6773
+ waitedMs: exports_external.number(),
6774
+ changed: exports_external.boolean().nullable()
6750
6775
  });
6751
6776
  var actionResponseSchema = exports_external.object({
6752
6777
  ok: exports_external.literal(true),
@@ -6754,7 +6779,8 @@ var actionResponseSchema = exports_external.object({
6754
6779
  resolved: exports_external.object({
6755
6780
  x: exports_external.number().optional(),
6756
6781
  y: exports_external.number().optional()
6757
- }).optional()
6782
+ }).optional(),
6783
+ screenshot: actionResultScreenshotSchema.optional()
6758
6784
  });
6759
6785
  var yoqaStatusResponseSchema = exports_external.object({
6760
6786
  runner: exports_external.object({
@@ -7309,10 +7335,28 @@ async function runYoqaShellScript(client, text, options = {}) {
7309
7335
  }
7310
7336
 
7311
7337
  // ../runner-client/src/run-report.ts
7312
- function actionSummary(action) {
7313
- if (!action || typeof action !== "object")
7314
- return "Step";
7315
- const record = action;
7338
+ function readStepCycle(action) {
7339
+ if (!action || typeof action !== "object" || !("cycle" in action))
7340
+ return null;
7341
+ const cycle = action.cycle;
7342
+ if (!cycle || typeof cycle !== "object")
7343
+ return null;
7344
+ const record = cycle;
7345
+ const asRecord = (value) => value && typeof value === "object" ? value : null;
7346
+ const verify = asRecord(record.verify);
7347
+ if (!verify || typeof verify.type !== "string" || !verify.type)
7348
+ return null;
7349
+ return {
7350
+ action: asRecord(record.action),
7351
+ decision: asRecord(record.decision),
7352
+ verify: {
7353
+ type: verify.type,
7354
+ reason: typeof verify.reason === "string" ? verify.reason : null,
7355
+ thoughts: typeof verify.thoughts === "string" ? verify.thoughts : null
7356
+ }
7357
+ };
7358
+ }
7359
+ function summarizeActionRecord(record) {
7316
7360
  const type = typeof record.type === "string" ? record.type : "step";
7317
7361
  if (type === "tap") {
7318
7362
  if (typeof record.label === "string" && record.label.trim())
@@ -7382,6 +7426,16 @@ function actionSummary(action) {
7382
7426
  }
7383
7427
  return type.charAt(0).toUpperCase() + type.slice(1);
7384
7428
  }
7429
+ function actionSummary(action) {
7430
+ if (!action || typeof action !== "object")
7431
+ return "Step";
7432
+ const cycle = readStepCycle(action);
7433
+ if (cycle?.action)
7434
+ return summarizeActionRecord(cycle.action);
7435
+ if (cycle?.decision)
7436
+ return summarizeActionRecord(cycle.decision);
7437
+ return summarizeActionRecord(action);
7438
+ }
7385
7439
  function optionalTrimmedString(value) {
7386
7440
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
7387
7441
  }
@@ -7489,6 +7543,20 @@ function toReportStatus(status) {
7489
7543
  return status;
7490
7544
  return null;
7491
7545
  }
7546
+ function formatStepPhases(phases) {
7547
+ const parts = [
7548
+ `capture ${Math.round(phases.captureMs)}ms`,
7549
+ `screen ${Math.round(phases.screenMs)}ms`,
7550
+ `image ${Math.round(phases.prepareMs)}ms`,
7551
+ `decide ${Math.round(phases.decideMs)}ms`,
7552
+ `action ${Math.round(phases.actionMs)}ms`,
7553
+ `settle ${Math.round(phases.settleMs)}ms`
7554
+ ];
7555
+ if (phases.decideRetries > 0) {
7556
+ parts.push(`retries ${phases.decideRetries}`);
7557
+ }
7558
+ return parts.join(" · ");
7559
+ }
7492
7560
  function mapCatalogStep(step, screenshotsByStepId) {
7493
7561
  const { reason, thoughts } = stepReasoning(step);
7494
7562
  return {
@@ -7497,6 +7565,7 @@ function mapCatalogStep(step, screenshotsByStepId) {
7497
7565
  summary: actionSummary(step.action),
7498
7566
  ok: step.ok,
7499
7567
  latencyMs: step.latencyMs,
7568
+ phases: step.phases ?? null,
7500
7569
  detail: step.detail,
7501
7570
  reason,
7502
7571
  thoughts,
@@ -7530,6 +7599,7 @@ function buildRunReportFromCatalogRun(run, meta = {}, screenshotsByStepId = {})
7530
7599
  deviceLabel: meta.deviceLabel ?? null,
7531
7600
  platform: run.platform,
7532
7601
  executionMode: run.executionMode,
7602
+ screenMode: run.screenMode ?? null,
7533
7603
  error: run.error,
7534
7604
  createdAt: run.createdAt,
7535
7605
  startedAt: run.startedAt,
@@ -7577,6 +7647,7 @@ function pngDataUri(base64) {
7577
7647
  }
7578
7648
  function formatRunReportHtml(doc) {
7579
7649
  const accent = statusAccent(doc.status);
7650
+ const screenRows = doc.screenMode ? [["Screen", doc.screenMode]] : [];
7580
7651
  const metaRows = [
7581
7652
  ["Status", accent.label],
7582
7653
  ["Source", doc.source === "catalog" ? "Catalog run" : "Manual Inspector"],
@@ -7584,6 +7655,7 @@ function formatRunReportHtml(doc) {
7584
7655
  ["Device", doc.deviceLabel ?? "—"],
7585
7656
  ["Platform", doc.platform ?? "—"],
7586
7657
  ["Mode", doc.executionMode ?? "—"],
7658
+ ...screenRows,
7587
7659
  ["Started", formatWhen(doc.startedAt ?? doc.createdAt)],
7588
7660
  ["Finished", formatWhen(doc.finishedAt)],
7589
7661
  ["Duration", formatDuration(doc.startedAt ?? doc.createdAt, doc.finishedAt)],
@@ -7599,6 +7671,7 @@ function formatRunReportHtml(doc) {
7599
7671
  const detailBlock = step.detail && step.detail !== step.reason ? `<p class="detail">${escapeHtml(step.detail)}</p>` : "";
7600
7672
  const shot = step.screenshotBase64 ? `<img class="shot" alt="Step ${step.index} screenshot" src="${pngDataUri(step.screenshotBase64)}" />` : `<p class="muted">No screenshot</p>`;
7601
7673
  const latency = step.latencyMs != null ? `<span class="muted">${step.latencyMs}ms</span>` : "";
7674
+ const phases = step.phases ? `<p class="muted phases">Phases: ${escapeHtml(formatStepPhases(step.phases))}</p>` : "";
7602
7675
  const commandBlock = step.command ? `<pre class="command"><code>${escapeHtml(step.command)}</code></pre>` : "";
7603
7676
  return `
7604
7677
  <article class="step ${step.ok ? "ok" : "fail"}">
@@ -7610,6 +7683,7 @@ function formatRunReportHtml(doc) {
7610
7683
  </div>
7611
7684
  </header>
7612
7685
  ${commandBlock}
7686
+ ${phases}
7613
7687
  ${reasonBlock}
7614
7688
  ${detailBlock}
7615
7689
  ${thoughtsBlock}
@@ -7707,6 +7781,7 @@ function formatRunReportMarkdown(doc) {
7707
7781
  `| Device | ${escapeMd(doc.deviceLabel ?? "—")} |`,
7708
7782
  `| Platform | ${escapeMd(doc.platform ?? "—")} |`,
7709
7783
  `| Mode | ${escapeMd(doc.executionMode ?? "—")} |`,
7784
+ ...doc.screenMode ? [`| Screen | ${escapeMd(doc.screenMode)} |`] : [],
7710
7785
  `| Started | ${escapeMd(formatWhen(doc.startedAt ?? doc.createdAt))} |`,
7711
7786
  `| Finished | ${escapeMd(formatWhen(doc.finishedAt))} |`,
7712
7787
  `| Duration | ${escapeMd(formatDuration(doc.startedAt ?? doc.createdAt, doc.finishedAt))} |`,
@@ -7735,6 +7810,8 @@ function formatRunReportMarkdown(doc) {
7735
7810
  lines.push(`- Command: \`${step.command.replace(/`/g, "'")}\``);
7736
7811
  if (step.latencyMs != null)
7737
7812
  lines.push(`- Latency: ${step.latencyMs}ms`);
7813
+ if (step.phases)
7814
+ lines.push(`- Phases: ${formatStepPhases(step.phases)}`);
7738
7815
  if (step.reason)
7739
7816
  lines.push(`- Reason: ${step.reason}`);
7740
7817
  if (step.detail && step.detail !== step.reason)
@@ -7771,6 +7848,7 @@ function formatRunReportGithubSummary(doc) {
7771
7848
  `| Device | ${escapeMd(doc.deviceLabel ?? "—")} |`,
7772
7849
  `| Platform | ${escapeMd(doc.platform ?? "—")} |`,
7773
7850
  `| Mode | ${escapeMd(doc.executionMode ?? "—")} |`,
7851
+ ...doc.screenMode ? [`| Screen | ${escapeMd(doc.screenMode)} |`] : [],
7774
7852
  `| Duration | ${escapeMd(formatDuration(doc.startedAt ?? doc.createdAt, doc.finishedAt))} |`,
7775
7853
  `| Run ID | \`${doc.id}\` |`,
7776
7854
  ""
@@ -8277,7 +8355,7 @@ var {
8277
8355
  // package.json
8278
8356
  var package_default = {
8279
8357
  name: "@yoqa/cli",
8280
- version: "0.3.27",
8358
+ version: "0.3.28",
8281
8359
  description: "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
8282
8360
  license: "UNLICENSED",
8283
8361
  type: "module",
@@ -8295,7 +8373,7 @@ var package_default = {
8295
8373
  url: "git+https://github.com/Aris-ngoy/qa-agent.git",
8296
8374
  directory: "packages/cli"
8297
8375
  },
8298
- homepage: "https://yoqa.mintlify.app/docs/cli",
8376
+ homepage: "https://yoqa.arisn.dev/docs/cli",
8299
8377
  bugs: {
8300
8378
  url: "https://github.com/Aris-ngoy/qa-agent/issues"
8301
8379
  },
@@ -8315,7 +8393,7 @@ var package_default = {
8315
8393
  health: "bun run src/main.ts health"
8316
8394
  },
8317
8395
  dependencies: {
8318
- "@yoqa/runner": "0.3.27"
8396
+ "@yoqa/runner": "0.3.28"
8319
8397
  },
8320
8398
  devDependencies: {
8321
8399
  "@types/node": "^22.15.30",
@@ -9244,7 +9322,14 @@ program2.command("screenshot").description("Capture a screenshot from the active
9244
9322
  });
9245
9323
  var action = program2.command("action").description("Perform an action on the active device");
9246
9324
  function addActionOptions(cmd) {
9247
- return cmd.option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("-d, --description <text>", "Ground target by natural language").option("--label <text>", "Tap/focus by accessibility label (cleaned screen tree)").option("--id <text>", "Tap/focus by resource-id / accessibility id").option("--x <n>", "X in 0–1000", (v) => Number(v)).option("--y <n>", "Y in 0–1000", (v) => Number(v)).option("--x2 <n>", "End X in 0–1000", (v) => Number(v)).option("--y2 <n>", "End Y in 0–1000", (v) => Number(v)).option("--duration <ms>", "Gesture duration ms (long-press for tap)", (v) => Number(v)).option("--double", "Double-tap (tap only)").option("--text <text>", "Text to type").option("--app-id <id>", "Bundle id / application id").option("--url <url>", "URL to open").option("--seconds <n>", "Background seconds", (v) => Number(v));
9325
+ return cmd.option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("-d, --description <text>", "Ground target by natural language").option("--label <text>", "Tap/focus by accessibility label (cleaned screen tree)").option("--id <text>", "Tap/focus by resource-id / accessibility id").option("--x <n>", "X in 0–1000", (v) => Number(v)).option("--y <n>", "Y in 0–1000", (v) => Number(v)).option("--x2 <n>", "End X in 0–1000", (v) => Number(v)).option("--y2 <n>", "End Y in 0–1000", (v) => Number(v)).option("--duration <ms>", "Gesture duration ms (long-press for tap)", (v) => Number(v)).option("--double", "Double-tap (tap only)").option("--text <text>", "Text to type").option("--app-id <id>", "Bundle id / application id").option("--url <url>", "URL to open").option("--seconds <n>", "Background seconds", (v) => Number(v)).option("--screenshot", "Wait for the screen to settle, then return the result screenshot (and a copy marking where the tap or swipe landed)").option("--settle <ms>", "With --screenshot: longest wait for the screen to settle (default 1500)", (v) => Number(v));
9326
+ }
9327
+ function printResultScreenshot(shot) {
9328
+ console.log(`screenshot ${shot.path}`);
9329
+ if (shot.annotatedPath)
9330
+ console.log(`marked ${shot.annotatedPath}`);
9331
+ console.log(`settled ${shot.settled} (${Math.round(shot.waitedMs)}ms)`);
9332
+ console.log(`changed ${shot.changed === null ? "unknown (screen still animating)" : shot.changed}`);
9248
9333
  }
9249
9334
  for (const kind of [
9250
9335
  "tap",
@@ -9273,7 +9358,9 @@ for (const kind of [
9273
9358
  description: options.description,
9274
9359
  appId: options.appId,
9275
9360
  url: options.url,
9276
- seconds: options.seconds
9361
+ seconds: options.seconds,
9362
+ screenshot: options.screenshot === true ? true : undefined,
9363
+ settleMs: options.settle
9277
9364
  });
9278
9365
  if (options.json) {
9279
9366
  console.log(JSON.stringify(body, null, 2));
@@ -9283,6 +9370,8 @@ for (const kind of [
9283
9370
  if (body.resolved?.x != null && body.resolved?.y != null) {
9284
9371
  console.log(`resolved ${body.resolved.x},${body.resolved.y}`);
9285
9372
  }
9373
+ if (body.screenshot)
9374
+ printResultScreenshot(body.screenshot);
9286
9375
  } catch (error) {
9287
9376
  fail(`action ${kind}`, error);
9288
9377
  }
@@ -9292,13 +9381,17 @@ addActionOptions(action.command("alert").description("Accept or dismiss a system
9292
9381
  try {
9293
9382
  const body = await client(String(options.baseUrl)).performAction({
9294
9383
  kind: "alert",
9295
- alertAction: options.dismiss ? "dismiss" : "accept"
9384
+ alertAction: options.dismiss ? "dismiss" : "accept",
9385
+ screenshot: options.screenshot === true ? true : undefined,
9386
+ settleMs: options.settle
9296
9387
  });
9297
9388
  if (options.json) {
9298
9389
  console.log(JSON.stringify(body, null, 2));
9299
9390
  return;
9300
9391
  }
9301
9392
  console.log(`ok ${body.kind}`);
9393
+ if (body.screenshot)
9394
+ printResultScreenshot(body.screenshot);
9302
9395
  } catch (error) {
9303
9396
  fail("action alert", error);
9304
9397
  }
@@ -9588,13 +9681,14 @@ appsCmd.command("get").argument("<app>", "App prefix or id").option("--base-url
9588
9681
  console.log(`prefix: ${app.prefix}`);
9589
9682
  console.log(`name: ${app.name}`);
9590
9683
  console.log(`app_context: ${app.context || "(empty)"}`);
9684
+ console.log(`app_knowledge: ${app.knowledge || "(empty)"}`);
9591
9685
  console.log(`ios_bundle_id: ${app.iosBundleId || "(empty)"}`);
9592
9686
  console.log(`android_application_id: ${app.androidApplicationId || "(empty)"}`);
9593
9687
  } catch (error) {
9594
9688
  fail("apps get", error);
9595
9689
  }
9596
9690
  });
9597
- appsCmd.command("update").argument("<app>", "App prefix or id").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--name <name>", "Display name").option("--prefix <prefix>", "CLI prefix").option("--context <text>", "App context / shared rules").option("--ios-bundle-id <id>", "iOS bundle id").option("--android-application-id <id>", "Android application id").option("--json", "Print raw JSON").action(async (appArg, options) => {
9691
+ appsCmd.command("update").argument("<app>", "App prefix or id").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--name <name>", "Display name").option("--prefix <prefix>", "CLI prefix").option("--context <text>", "App context / shared rules").option("--knowledge <text>", "App knowledge notes injected into agent runs").option("--ios-bundle-id <id>", "iOS bundle id").option("--android-application-id <id>", "Android application id").option("--json", "Print raw JSON").action(async (appArg, options) => {
9598
9692
  try {
9599
9693
  const c = client(options.baseUrl);
9600
9694
  const resolved = await resolveAppId(c, appArg);
@@ -9602,6 +9696,7 @@ appsCmd.command("update").argument("<app>", "App prefix or id").option("--base-u
9602
9696
  name: options.name,
9603
9697
  prefix: options.prefix,
9604
9698
  context: options.context,
9699
+ knowledge: options.knowledge,
9605
9700
  iosBundleId: options.iosBundleId,
9606
9701
  androidApplicationId: options.androidApplicationId
9607
9702
  });
@@ -9927,7 +10022,7 @@ runsCmd.command("delete").argument("<runId>", "Run id").option("--base-url <url>
9927
10022
  fail("runs delete", error);
9928
10023
  }
9929
10024
  });
9930
- runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("--cases <numbers>", "Comma-separated case numbers, e.g. 1,2,5").option("--device <id>", "Device id (defaults to active session)").option("--platform <platform>", "ios | android (defaults to active session)").option("--build-id <id>", "Registered build id to install before run").option("--build-path <path>", "Absolute build path to register + install").option("--mode <mode>", "auto | script | agent (default: auto)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--wait", "Wait until the run finishes (passed / errored / cancelled)").option("--timeout <seconds>", "Wait timeout in seconds (default: 1800)", String(DEFAULT_RUN_WAIT_TIMEOUT_SECONDS)).option("--github-output", "Write run_id and status to $GITHUB_OUTPUT").option("--no-github-output", "Do not write GitHub step outputs").action(async (appArg, options) => {
10025
+ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("--cases <numbers>", "Comma-separated case numbers, e.g. 1,2,5").option("--device <id>", "Device id (defaults to active session)").option("--platform <platform>", "ios | android (defaults to active session)").option("--build-id <id>", "Registered build id to install before run").option("--build-path <path>", "Absolute build path to register + install").option("--mode <mode>", "auto | script | agent (default: auto)").option("--screen-mode <mode>", "vision | tree — what the agent sees: screenshot only, or also the accessibility tree (default: vision)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--wait", "Wait until the run finishes (passed / errored / cancelled)").option("--timeout <seconds>", "Wait timeout in seconds (default: 1800)", String(DEFAULT_RUN_WAIT_TIMEOUT_SECONDS)).option("--github-output", "Write run_id and status to $GITHUB_OUTPUT").option("--no-github-output", "Do not write GitHub step outputs").action(async (appArg, options) => {
9931
10026
  try {
9932
10027
  const c = client(options.baseUrl);
9933
10028
  const resolved = await resolveAppId(c, appArg);
@@ -9959,6 +10054,10 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
9959
10054
  if (options.mode && !executionMode) {
9960
10055
  throw new Error("--mode must be auto, script, or agent");
9961
10056
  }
10057
+ const screenMode = options.screenMode === "vision" || options.screenMode === "tree" ? options.screenMode : undefined;
10058
+ if (options.screenMode && !screenMode) {
10059
+ throw new Error("--screen-mode must be vision or tree");
10060
+ }
9962
10061
  let run = await c.createRun({
9963
10062
  appId: resolved.id,
9964
10063
  caseIds,
@@ -9966,7 +10065,8 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
9966
10065
  platform,
9967
10066
  buildId: options.buildId,
9968
10067
  buildPath: options.buildPath,
9969
- executionMode
10068
+ executionMode,
10069
+ screenMode
9970
10070
  });
9971
10071
  if (options.wait) {
9972
10072
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoqa/cli",
3
- "version": "0.3.27",
3
+ "version": "0.3.28",
4
4
  "description": "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "url": "git+https://github.com/Aris-ngoy/qa-agent.git",
19
19
  "directory": "packages/cli"
20
20
  },
21
- "homepage": "https://yoqa.mintlify.app/docs/cli",
21
+ "homepage": "https://yoqa.arisn.dev/docs/cli",
22
22
  "bugs": {
23
23
  "url": "https://github.com/Aris-ngoy/qa-agent/issues"
24
24
  },
@@ -38,7 +38,7 @@
38
38
  "health": "bun run src/main.ts health"
39
39
  },
40
40
  "dependencies": {
41
- "@yoqa/runner": "0.3.27"
41
+ "@yoqa/runner": "0.3.28"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.15.30",