@exodus/xqa 1.9.0 → 1.10.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.
Files changed (2) hide show
  1. package/dist/xqa.cjs +699 -332
  2. package/package.json +3 -3
package/dist/xqa.cjs CHANGED
@@ -1621,9 +1621,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1621
1621
  if (fn) {
1622
1622
  this._exitCallback = fn;
1623
1623
  } else {
1624
- this._exitCallback = (err18) => {
1625
- if (err18.code !== "commander.executeSubCommandAsync") {
1626
- throw err18;
1624
+ this._exitCallback = (err19) => {
1625
+ if (err19.code !== "commander.executeSubCommandAsync") {
1626
+ throw err19;
1627
1627
  } else {
1628
1628
  }
1629
1629
  };
@@ -1699,12 +1699,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
1699
1699
  _callParseArg(target, value, previous, invalidArgumentMessage) {
1700
1700
  try {
1701
1701
  return target.parseArg(value, previous);
1702
- } catch (err18) {
1703
- if (err18.code === "commander.invalidArgument") {
1704
- const message = `${invalidArgumentMessage} ${err18.message}`;
1705
- this.error(message, { exitCode: err18.exitCode, code: err18.code });
1702
+ } catch (err19) {
1703
+ if (err19.code === "commander.invalidArgument") {
1704
+ const message = `${invalidArgumentMessage} ${err19.message}`;
1705
+ this.error(message, { exitCode: err19.exitCode, code: err19.code });
1706
1706
  }
1707
- throw err18;
1707
+ throw err19;
1708
1708
  }
1709
1709
  }
1710
1710
  /**
@@ -2293,14 +2293,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
2293
2293
  );
2294
2294
  }
2295
2295
  });
2296
- proc.on("error", (err18) => {
2297
- if (err18.code === "ENOENT") {
2296
+ proc.on("error", (err19) => {
2297
+ if (err19.code === "ENOENT") {
2298
2298
  this._checkForMissingExecutable(
2299
2299
  executableFile,
2300
2300
  executableDir,
2301
2301
  subcommand._name
2302
2302
  );
2303
- } else if (err18.code === "EACCES") {
2303
+ } else if (err19.code === "EACCES") {
2304
2304
  throw new Error(`'${executableFile}' not executable`);
2305
2305
  }
2306
2306
  if (!exitCallback) {
@@ -2311,7 +2311,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2311
2311
  "commander.executeSubCommandAsync",
2312
2312
  "(error)"
2313
2313
  );
2314
- wrappedError.nestedError = err18;
2314
+ wrappedError.nestedError = err19;
2315
2315
  exitCallback(wrappedError);
2316
2316
  }
2317
2317
  });
@@ -3728,8 +3728,8 @@ var require_index_cjs = __commonJS({
3728
3728
  function okAsync8(value) {
3729
3729
  return new ResultAsync12(Promise.resolve(new Ok(value)));
3730
3730
  }
3731
- function errAsync8(err19) {
3732
- return new ResultAsync12(Promise.resolve(new Err(err19)));
3731
+ function errAsync8(err20) {
3732
+ return new ResultAsync12(Promise.resolve(new Err(err20)));
3733
3733
  }
3734
3734
  var fromPromise = ResultAsync12.fromPromise;
3735
3735
  var fromSafePromise2 = ResultAsync12.fromSafePromise;
@@ -3738,7 +3738,7 @@ var require_index_cjs = __commonJS({
3738
3738
  let acc = ok19([]);
3739
3739
  for (const result of resultList) {
3740
3740
  if (result.isErr()) {
3741
- acc = err18(result.error);
3741
+ acc = err19(result.error);
3742
3742
  break;
3743
3743
  } else {
3744
3744
  acc.map((list) => list.push(result.value));
@@ -3753,7 +3753,7 @@ var require_index_cjs = __commonJS({
3753
3753
  if (result.isErr() && acc.isErr()) {
3754
3754
  acc.error.push(result.error);
3755
3755
  } else if (result.isErr() && acc.isOk()) {
3756
- acc = err18([result.error]);
3756
+ acc = err19([result.error]);
3757
3757
  } else if (result.isOk() && acc.isOk()) {
3758
3758
  acc.value.push(result.value);
3759
3759
  }
@@ -3763,17 +3763,17 @@ var require_index_cjs = __commonJS({
3763
3763
  var combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync12.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
3764
3764
  exports2.Result = void 0;
3765
3765
  (function(Result3) {
3766
- function fromThrowable14(fn, errorFn) {
3766
+ function fromThrowable16(fn, errorFn) {
3767
3767
  return (...args) => {
3768
3768
  try {
3769
3769
  const result = fn(...args);
3770
3770
  return ok19(result);
3771
3771
  } catch (e3) {
3772
- return err18(errorFn ? errorFn(e3) : e3);
3772
+ return err19(errorFn ? errorFn(e3) : e3);
3773
3773
  }
3774
3774
  };
3775
3775
  }
3776
- Result3.fromThrowable = fromThrowable14;
3776
+ Result3.fromThrowable = fromThrowable16;
3777
3777
  function combine(resultList) {
3778
3778
  return combineResultList(resultList);
3779
3779
  }
@@ -3786,8 +3786,8 @@ var require_index_cjs = __commonJS({
3786
3786
  function ok19(value) {
3787
3787
  return new Ok(value);
3788
3788
  }
3789
- function err18(err19) {
3790
- return new Err(err19);
3789
+ function err19(err20) {
3790
+ return new Err(err20);
3791
3791
  }
3792
3792
  function safeTry(body) {
3793
3793
  const n3 = body().next();
@@ -3882,27 +3882,27 @@ var require_index_cjs = __commonJS({
3882
3882
  }
3883
3883
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
3884
3884
  map(_f) {
3885
- return err18(this.error);
3885
+ return err19(this.error);
3886
3886
  }
3887
3887
  mapErr(f6) {
3888
- return err18(f6(this.error));
3888
+ return err19(f6(this.error));
3889
3889
  }
3890
3890
  andThrough(_f) {
3891
- return err18(this.error);
3891
+ return err19(this.error);
3892
3892
  }
3893
3893
  andTee(_f) {
3894
- return err18(this.error);
3894
+ return err19(this.error);
3895
3895
  }
3896
3896
  orTee(f6) {
3897
3897
  try {
3898
3898
  f6(this.error);
3899
3899
  } catch (e3) {
3900
3900
  }
3901
- return err18(this.error);
3901
+ return err19(this.error);
3902
3902
  }
3903
3903
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
3904
3904
  andThen(_f) {
3905
- return err18(this.error);
3905
+ return err19(this.error);
3906
3906
  }
3907
3907
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
3908
3908
  orElse(f6) {
@@ -3922,13 +3922,13 @@ var require_index_cjs = __commonJS({
3922
3922
  unwrapOr(v2) {
3923
3923
  return v2;
3924
3924
  }
3925
- match(_ok, err19) {
3926
- return err19(this.error);
3925
+ match(_ok, err20) {
3926
+ return err20(this.error);
3927
3927
  }
3928
3928
  safeUnwrap() {
3929
3929
  const error48 = this.error;
3930
3930
  return (function* () {
3931
- yield err18(error48);
3931
+ yield err19(error48);
3932
3932
  throw new Error("Do not use this generator out of `safeTry`");
3933
3933
  })();
3934
3934
  }
@@ -3944,16 +3944,16 @@ var require_index_cjs = __commonJS({
3944
3944
  return self2;
3945
3945
  }
3946
3946
  };
3947
- var fromThrowable13 = exports2.Result.fromThrowable;
3947
+ var fromThrowable15 = exports2.Result.fromThrowable;
3948
3948
  exports2.Err = Err;
3949
3949
  exports2.Ok = Ok;
3950
3950
  exports2.ResultAsync = ResultAsync12;
3951
- exports2.err = err18;
3951
+ exports2.err = err19;
3952
3952
  exports2.errAsync = errAsync8;
3953
3953
  exports2.fromAsyncThrowable = fromAsyncThrowable10;
3954
3954
  exports2.fromPromise = fromPromise;
3955
3955
  exports2.fromSafePromise = fromSafePromise2;
3956
- exports2.fromThrowable = fromThrowable13;
3956
+ exports2.fromThrowable = fromThrowable15;
3957
3957
  exports2.ok = ok19;
3958
3958
  exports2.okAsync = okAsync8;
3959
3959
  exports2.safeTry = safeTry;
@@ -11117,12 +11117,12 @@ var require_lib2 = __commonJS({
11117
11117
  var detectFile = (filepath, opts = {}) => new Promise((resolve, reject) => {
11118
11118
  let fd;
11119
11119
  const fs3 = (0, node_1.default)();
11120
- const handler = (err18, buffer) => {
11120
+ const handler = (err19, buffer) => {
11121
11121
  if (fd) {
11122
11122
  fs3.closeSync(fd);
11123
11123
  }
11124
- if (err18) {
11125
- reject(err18);
11124
+ if (err19) {
11125
+ reject(err19);
11126
11126
  } else if (buffer) {
11127
11127
  resolve((0, exports2.detect)(buffer));
11128
11128
  } else {
@@ -11133,9 +11133,9 @@ var require_lib2 = __commonJS({
11133
11133
  if (sampleSize > 0) {
11134
11134
  fd = fs3.openSync(filepath, "r");
11135
11135
  let sample = Buffer.allocUnsafe(sampleSize);
11136
- fs3.read(fd, sample, 0, sampleSize, opts.offset, (err18, bytesRead) => {
11137
- if (err18) {
11138
- handler(err18, null);
11136
+ fs3.read(fd, sample, 0, sampleSize, opts.offset, (err19, bytesRead) => {
11137
+ if (err19) {
11138
+ handler(err19, null);
11139
11139
  } else {
11140
11140
  if (bytesRead < sampleSize) {
11141
11141
  sample = sample.subarray(0, bytesRead);
@@ -15057,9 +15057,9 @@ var require_main = __commonJS({
15057
15057
  options.path = vaultPath;
15058
15058
  const result = DotenvModule.configDotenv(options);
15059
15059
  if (!result.parsed) {
15060
- const err18 = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
15061
- err18.code = "MISSING_DATA";
15062
- throw err18;
15060
+ const err19 = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
15061
+ err19.code = "MISSING_DATA";
15062
+ throw err19;
15063
15063
  }
15064
15064
  const keys = _dotenvKey(options).split(",");
15065
15065
  const length = keys.length;
@@ -15102,30 +15102,30 @@ var require_main = __commonJS({
15102
15102
  uri = new URL(dotenvKey);
15103
15103
  } catch (error48) {
15104
15104
  if (error48.code === "ERR_INVALID_URL") {
15105
- const err18 = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
15106
- err18.code = "INVALID_DOTENV_KEY";
15107
- throw err18;
15105
+ const err19 = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
15106
+ err19.code = "INVALID_DOTENV_KEY";
15107
+ throw err19;
15108
15108
  }
15109
15109
  throw error48;
15110
15110
  }
15111
15111
  const key = uri.password;
15112
15112
  if (!key) {
15113
- const err18 = new Error("INVALID_DOTENV_KEY: Missing key part");
15114
- err18.code = "INVALID_DOTENV_KEY";
15115
- throw err18;
15113
+ const err19 = new Error("INVALID_DOTENV_KEY: Missing key part");
15114
+ err19.code = "INVALID_DOTENV_KEY";
15115
+ throw err19;
15116
15116
  }
15117
15117
  const environment = uri.searchParams.get("environment");
15118
15118
  if (!environment) {
15119
- const err18 = new Error("INVALID_DOTENV_KEY: Missing environment part");
15120
- err18.code = "INVALID_DOTENV_KEY";
15121
- throw err18;
15119
+ const err19 = new Error("INVALID_DOTENV_KEY: Missing environment part");
15120
+ err19.code = "INVALID_DOTENV_KEY";
15121
+ throw err19;
15122
15122
  }
15123
15123
  const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
15124
15124
  const ciphertext = result.parsed[environmentKey];
15125
15125
  if (!ciphertext) {
15126
- const err18 = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
15127
- err18.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
15128
- throw err18;
15126
+ const err19 = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
15127
+ err19.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
15128
+ throw err19;
15129
15129
  }
15130
15130
  return { ciphertext, key };
15131
15131
  }
@@ -15255,13 +15255,13 @@ var require_main = __commonJS({
15255
15255
  const invalidKeyLength = error48.message === "Invalid key length";
15256
15256
  const decryptionFailed = error48.message === "Unsupported state or unable to authenticate data";
15257
15257
  if (isRange || invalidKeyLength) {
15258
- const err18 = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
15259
- err18.code = "INVALID_DOTENV_KEY";
15260
- throw err18;
15258
+ const err19 = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
15259
+ err19.code = "INVALID_DOTENV_KEY";
15260
+ throw err19;
15261
15261
  } else if (decryptionFailed) {
15262
- const err18 = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
15263
- err18.code = "DECRYPTION_FAILED";
15264
- throw err18;
15262
+ const err19 = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
15263
+ err19.code = "DECRYPTION_FAILED";
15264
+ throw err19;
15265
15265
  } else {
15266
15266
  throw error48;
15267
15267
  }
@@ -15271,9 +15271,9 @@ var require_main = __commonJS({
15271
15271
  const debug = Boolean(options && options.debug);
15272
15272
  const override = Boolean(options && options.override);
15273
15273
  if (typeof parsed !== "object") {
15274
- const err18 = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
15275
- err18.code = "OBJECT_REQUIRED";
15276
- throw err18;
15274
+ const err19 = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
15275
+ err19.code = "OBJECT_REQUIRED";
15276
+ throw err19;
15277
15277
  }
15278
15278
  for (const key of Object.keys(parsed)) {
15279
15279
  if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
@@ -18414,8 +18414,8 @@ var require_picomatch = __commonJS({
18414
18414
  try {
18415
18415
  const opts = options || {};
18416
18416
  return new RegExp(source, opts.flags || (opts.nocase ? "i" : ""));
18417
- } catch (err18) {
18418
- if (options && options.debug === true) throw err18;
18417
+ } catch (err19) {
18418
+ if (options && options.debug === true) throw err19;
18419
18419
  return /$^/;
18420
18420
  }
18421
18421
  };
@@ -18822,8 +18822,8 @@ var require_merge2 = __commonJS({
18822
18822
  }
18823
18823
  next();
18824
18824
  }
18825
- function onerror(err18) {
18826
- mergedStream.emit("error", err18);
18825
+ function onerror(err19) {
18826
+ mergedStream.emit("error", err19);
18827
18827
  }
18828
18828
  if (stream._readableState.endEmitted) {
18829
18829
  return next();
@@ -19187,8 +19187,8 @@ var require_out = __commonJS({
19187
19187
  var require_queue_microtask = __commonJS({
19188
19188
  "../../node_modules/.pnpm/queue-microtask@1.2.3/node_modules/queue-microtask/index.js"(exports2, module2) {
19189
19189
  var promise2;
19190
- module2.exports = typeof queueMicrotask === "function" ? queueMicrotask.bind(typeof window !== "undefined" ? window : global) : (cb2) => (promise2 || (promise2 = Promise.resolve())).then(cb2).catch((err18) => setTimeout(() => {
19191
- throw err18;
19190
+ module2.exports = typeof queueMicrotask === "function" ? queueMicrotask.bind(typeof window !== "undefined" ? window : global) : (cb2) => (promise2 || (promise2 = Promise.resolve())).then(cb2).catch((err19) => setTimeout(() => {
19191
+ throw err19;
19192
19192
  }, 0));
19193
19193
  }
19194
19194
  });
@@ -19209,32 +19209,32 @@ var require_run_parallel = __commonJS({
19209
19209
  results = {};
19210
19210
  pending = keys.length;
19211
19211
  }
19212
- function done(err18) {
19212
+ function done(err19) {
19213
19213
  function end() {
19214
- if (cb2) cb2(err18, results);
19214
+ if (cb2) cb2(err19, results);
19215
19215
  cb2 = null;
19216
19216
  }
19217
19217
  if (isSync) queueMicrotask2(end);
19218
19218
  else end();
19219
19219
  }
19220
- function each(i3, err18, result) {
19220
+ function each(i3, err19, result) {
19221
19221
  results[i3] = result;
19222
- if (--pending === 0 || err18) {
19223
- done(err18);
19222
+ if (--pending === 0 || err19) {
19223
+ done(err19);
19224
19224
  }
19225
19225
  }
19226
19226
  if (!pending) {
19227
19227
  done(null);
19228
19228
  } else if (keys) {
19229
19229
  keys.forEach(function(key) {
19230
- tasks[key](function(err18, result) {
19231
- each(key, err18, result);
19230
+ tasks[key](function(err19, result) {
19231
+ each(key, err19, result);
19232
19232
  });
19233
19233
  });
19234
19234
  } else {
19235
19235
  tasks.forEach(function(task, i3) {
19236
- task(function(err18, result) {
19237
- each(i3, err18, result);
19236
+ task(function(err19, result) {
19237
+ each(i3, err19, result);
19238
19238
  });
19239
19239
  });
19240
19240
  }
@@ -19806,16 +19806,16 @@ var require_queue = __commonJS({
19806
19806
  this.context = null;
19807
19807
  this.errorHandler = null;
19808
19808
  var self2 = this;
19809
- this.worked = function worked(err18, result) {
19809
+ this.worked = function worked(err19, result) {
19810
19810
  var callback = self2.callback;
19811
19811
  var errorHandler = self2.errorHandler;
19812
19812
  var val = self2.value;
19813
19813
  self2.value = null;
19814
19814
  self2.callback = noop2;
19815
19815
  if (self2.errorHandler) {
19816
- errorHandler(err18, val);
19816
+ errorHandler(err19, val);
19817
19817
  }
19818
- callback.call(self2.context, err18, result);
19818
+ callback.call(self2.context, err19, result);
19819
19819
  self2.release(self2);
19820
19820
  };
19821
19821
  }
@@ -19839,9 +19839,9 @@ var require_queue = __commonJS({
19839
19839
  return queue;
19840
19840
  function push(value) {
19841
19841
  var p = new Promise(function(resolve, reject) {
19842
- pushCb(value, function(err18, result) {
19843
- if (err18) {
19844
- reject(err18);
19842
+ pushCb(value, function(err19, result) {
19843
+ if (err19) {
19844
+ reject(err19);
19845
19845
  return;
19846
19846
  }
19847
19847
  resolve(result);
@@ -19852,9 +19852,9 @@ var require_queue = __commonJS({
19852
19852
  }
19853
19853
  function unshift(value) {
19854
19854
  var p = new Promise(function(resolve, reject) {
19855
- unshiftCb(value, function(err18, result) {
19856
- if (err18) {
19857
- reject(err18);
19855
+ unshiftCb(value, function(err19, result) {
19856
+ if (err19) {
19857
+ reject(err19);
19858
19858
  return;
19859
19859
  }
19860
19860
  resolve(result);
@@ -21075,7 +21075,7 @@ var {
21075
21075
  } = import_index.default;
21076
21076
 
21077
21077
  // src/commands/analyse-command.ts
21078
- var import_promises16 = require("node:fs/promises");
21078
+ var import_promises17 = require("node:fs/promises");
21079
21079
 
21080
21080
  // ../../packages/pipeline/dist/index.js
21081
21081
  var import_node_fs2 = require("node:fs");
@@ -51989,6 +51989,7 @@ var import_node_path = __toESM(require("node:path"), 1);
51989
51989
  var import_neverthrow5 = __toESM(require_index_cjs(), 1);
51990
51990
  var import_sharp = __toESM(require("sharp"), 1);
51991
51991
  var import_neverthrow6 = __toESM(require_index_cjs(), 1);
51992
+ var import_promises6 = require("node:timers/promises");
51992
51993
  var import_node_crypto = require("node:crypto");
51993
51994
  var import_node_child_process2 = require("node:child_process");
51994
51995
  var import_node_events = require("node:events");
@@ -52052,11 +52053,33 @@ function resolveUdid(udid) {
52052
52053
  return (0, import_neverthrow3.err)(result);
52053
52054
  });
52054
52055
  }
52056
+ var parseIdbTree = (0, import_neverthrow.fromThrowable)(
52057
+ (raw) => JSON.parse(raw),
52058
+ (cause) => ({ type: "SNAPSHOT_PARSE_FAILED", cause })
52059
+ );
52055
52060
  var FULLSCREEN_THRESHOLD = 0.9;
52056
52061
  var DEFAULT_SCREEN_WIDTH = 375;
52057
52062
  var DEFAULT_SCREEN_HEIGHT = 812;
52058
- var STABILITY_POLL_ATTEMPTS = 5;
52059
- var STABILITY_POLL_DELAY_MS = 300;
52063
+ var STABILITY_POLL_ATTEMPTS = 10;
52064
+ var STABILITY_POLL_DELAY_MS = 500;
52065
+ function structuralSignature(elements) {
52066
+ const lines = [];
52067
+ const walk = (nodes) => {
52068
+ for (const element of nodes) {
52069
+ const frame = element.frame ?? { x: 0, y: 0, width: 0, height: 0 };
52070
+ const type2 = element.type ?? "unknown";
52071
+ const childCount = (element.children ?? []).length;
52072
+ lines.push(
52073
+ `${type2}|${String(frame.x)},${String(frame.y)},${String(frame.width)},${String(frame.height)}|${String(childCount)}`
52074
+ );
52075
+ if (element.children) {
52076
+ walk(element.children);
52077
+ }
52078
+ }
52079
+ };
52080
+ walk(elements);
52081
+ return lines.join("\n");
52082
+ }
52060
52083
  function isInViewport(frame, screen) {
52061
52084
  return frame.width > 0 && frame.height > 0 && frame.x + frame.width > 0 && frame.x < screen.width && frame.y + frame.height > 0 && frame.y < screen.height;
52062
52085
  }
@@ -52168,36 +52191,39 @@ function formatAccessibilityElements(elements) {
52168
52191
 
52169
52192
  ${elementList}` : elementList;
52170
52193
  }
52194
+ function fetchSnapshot(udid) {
52195
+ return runCommand("idb", ["ui", "describe-all", "--udid", udid, "--json", "--nested"]);
52196
+ }
52171
52197
  async function pollUntilStable(udid) {
52172
- let previous;
52198
+ let previousSignature;
52199
+ let previousRaw = "";
52173
52200
  for (let index = 0; index < STABILITY_POLL_ATTEMPTS; index++) {
52174
- const result = await runCommand("idb", [
52175
- "ui",
52176
- "describe-all",
52177
- "--udid",
52178
- udid,
52179
- "--json",
52180
- "--nested"
52181
- ]);
52201
+ const result = await fetchSnapshot(udid);
52182
52202
  if (result.isErr()) {
52183
52203
  return result;
52184
52204
  }
52185
- const current = result.value;
52186
- if (current === previous) {
52187
- return (0, import_neverthrow.ok)(current);
52205
+ const raw = result.value;
52206
+ const parsed = parseIdbTree(raw);
52207
+ if (parsed.isErr()) {
52208
+ return (0, import_neverthrow.err)(parsed.error);
52209
+ }
52210
+ const signature = structuralSignature(parsed.value);
52211
+ if (signature === previousSignature) {
52212
+ return (0, import_neverthrow.ok)(raw);
52188
52213
  }
52189
- previous = current;
52214
+ previousSignature = signature;
52215
+ previousRaw = raw;
52190
52216
  if (index < STABILITY_POLL_ATTEMPTS - 1) {
52191
52217
  await (0, import_promises4.setTimeout)(STABILITY_POLL_DELAY_MS);
52192
52218
  }
52193
52219
  }
52194
- return (0, import_neverthrow.ok)(previous ?? "");
52220
+ return (0, import_neverthrow.ok)(previousRaw);
52195
52221
  }
52196
52222
  function waitForStableSnapshot(udid) {
52197
52223
  return new import_neverthrow.ResultAsync(pollUntilStable(udid));
52198
52224
  }
52199
52225
  function captureAccessibilityTree(udid = "booted") {
52200
- return resolveUdid(udid).andThen((resolvedUdid) => waitForStableSnapshot(resolvedUdid)).map((raw) => JSON.parse(raw));
52226
+ return resolveUdid(udid).andThen((resolvedUdid) => waitForStableSnapshot(resolvedUdid)).andThen((raw) => parseIdbTree(raw));
52201
52227
  }
52202
52228
  function captureAccessibilitySnapshot(udid = "booted") {
52203
52229
  return captureAccessibilityTree(udid).map((elements) => formatAccessibilityElements(elements));
@@ -52590,6 +52616,37 @@ function createScreenshotTool(udid = "booted") {
52590
52616
  });
52591
52617
  }
52592
52618
  var screenshotTool = createScreenshotTool();
52619
+ var MIN_WAIT_SECONDS = 1;
52620
+ var MAX_WAIT_SECONDS = 10;
52621
+ var MS_PER_SECOND2 = 1e3;
52622
+ var SECONDS_SCHEMA = external_exports.number().int().min(MIN_WAIT_SECONDS).max(MAX_WAIT_SECONDS);
52623
+ var WAIT_SCHEMA = {
52624
+ seconds: SECONDS_SCHEMA
52625
+ };
52626
+ function createWaitSecondsTool() {
52627
+ return _x(
52628
+ "wait_seconds",
52629
+ "Pause for N seconds (1-10) when expected UI content may still be loading silently. Call view_ui again after waiting.",
52630
+ WAIT_SCHEMA,
52631
+ async ({ seconds }) => {
52632
+ const parsed = SECONDS_SCHEMA.safeParse(seconds);
52633
+ if (!parsed.success) {
52634
+ return {
52635
+ content: [
52636
+ {
52637
+ type: "text",
52638
+ text: `wait_seconds expected an integer between ${String(MIN_WAIT_SECONDS)} and ${String(MAX_WAIT_SECONDS)}, got ${String(seconds)}`
52639
+ }
52640
+ ],
52641
+ isError: true
52642
+ };
52643
+ }
52644
+ await (0, import_promises6.setTimeout)(parsed.data * MS_PER_SECOND2);
52645
+ return { content: [{ type: "text", text: `Waited ${String(parsed.data)}s` }] };
52646
+ }
52647
+ );
52648
+ }
52649
+ var WAIT_SECONDS_TOOL_NAME = "mcp__mobile-ios__wait_seconds";
52593
52650
  var LABEL_SET_CAP = 50;
52594
52651
  var MID_DEPTH_THRESHOLD = 5;
52595
52652
  var DEFAULT_SCREEN_HEIGHT2 = 812;
@@ -52744,6 +52801,7 @@ function createMobileIosServer(udid = "booted", extraTools = []) {
52744
52801
  createLaunchAppTool(udid),
52745
52802
  createTerminateAppTool(udid),
52746
52803
  createListAppsTool(udid),
52804
+ createWaitSecondsTool(),
52747
52805
  ...extraTools
52748
52806
  ]
52749
52807
  });
@@ -52765,7 +52823,7 @@ var findingSchema = external_exports.object({
52765
52823
  "motion-regression",
52766
52824
  "interaction-regression",
52767
52825
  "continuity-regression",
52768
- "step-skipped"
52826
+ "missing-content"
52769
52827
  ]),
52770
52828
  flow: external_exports.string(),
52771
52829
  steps: external_exports.array(external_exports.string()),
@@ -52810,10 +52868,10 @@ function dismissalsPath(baseDirectory, override) {
52810
52868
 
52811
52869
  // ../../packages/pipeline/dist/index.js
52812
52870
  var import_neverthrow30 = __toESM(require_index_cjs(), 1);
52813
- var import_promises14 = require("node:timers/promises");
52871
+ var import_promises15 = require("node:timers/promises");
52814
52872
 
52815
52873
  // ../../agents/analyser/dist/index.js
52816
- var import_promises6 = require("node:fs/promises");
52874
+ var import_promises7 = require("node:fs/promises");
52817
52875
 
52818
52876
  // ../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
52819
52877
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -53639,9 +53697,9 @@ function render(state, previous) {
53639
53697
  function dashboardEqual(left, right) {
53640
53698
  return JSON.stringify(left) === JSON.stringify(right);
53641
53699
  }
53642
- var MS_PER_SECOND2 = 1e3;
53700
+ var MS_PER_SECOND3 = 1e3;
53643
53701
  function formatDuration(ms) {
53644
- return `${String(Math.round(ms / MS_PER_SECOND2))}s`;
53702
+ return `${String(Math.round(ms / MS_PER_SECOND3))}s`;
53645
53703
  }
53646
53704
  function normalizeItemQueued(event) {
53647
53705
  return [{ kind: "text", style: "default", text: `[${event.item.name}] QUEUED` }];
@@ -54477,7 +54535,7 @@ function safeHandler(handler) {
54477
54535
 
54478
54536
  // ../../agents/analyser/dist/index.js
54479
54537
  var import_neverthrow10 = __toESM(require_index_cjs(), 1);
54480
- var import_promises7 = require("node:timers/promises");
54538
+ var import_promises8 = require("node:timers/promises");
54481
54539
 
54482
54540
  // ../../node_modules/.pnpm/@google+generative-ai@0.24.1/node_modules/@google/generative-ai/dist/index.mjs
54483
54541
  var SchemaType;
@@ -54680,15 +54738,15 @@ async function makeRequest(url2, fetchOptions, fetchFn = fetch) {
54680
54738
  return response;
54681
54739
  }
54682
54740
  function handleResponseError(e3, url2) {
54683
- let err18 = e3;
54684
- if (err18.name === "AbortError") {
54685
- err18 = new GoogleGenerativeAIAbortError(`Request aborted when fetching ${url2.toString()}: ${e3.message}`);
54686
- err18.stack = e3.stack;
54741
+ let err19 = e3;
54742
+ if (err19.name === "AbortError") {
54743
+ err19 = new GoogleGenerativeAIAbortError(`Request aborted when fetching ${url2.toString()}: ${e3.message}`);
54744
+ err19.stack = e3.stack;
54687
54745
  } else if (!(e3 instanceof GoogleGenerativeAIFetchError || e3 instanceof GoogleGenerativeAIRequestInputError)) {
54688
- err18 = new GoogleGenerativeAIError(`Error fetching from ${url2.toString()}: ${e3.message}`);
54689
- err18.stack = e3.stack;
54746
+ err19 = new GoogleGenerativeAIError(`Error fetching from ${url2.toString()}: ${e3.message}`);
54747
+ err19.stack = e3.stack;
54690
54748
  }
54691
- throw err18;
54749
+ throw err19;
54692
54750
  }
54693
54751
  async function handleResponseNotOk(response, url2) {
54694
54752
  let message = "";
@@ -54935,14 +54993,14 @@ function getResponseStream(inputStream) {
54935
54993
  }
54936
54994
  return pump();
54937
54995
  }).catch((e3) => {
54938
- let err18 = e3;
54939
- err18.stack = e3.stack;
54940
- if (err18.name === "AbortError") {
54941
- err18 = new GoogleGenerativeAIAbortError("Request aborted when reading from the stream");
54996
+ let err19 = e3;
54997
+ err19.stack = e3.stack;
54998
+ if (err19.name === "AbortError") {
54999
+ err19 = new GoogleGenerativeAIAbortError("Request aborted when reading from the stream");
54942
55000
  } else {
54943
- err18 = new GoogleGenerativeAIError("Error reading from the stream");
55001
+ err19 = new GoogleGenerativeAIError("Error reading from the stream");
54944
55002
  }
54945
- throw err18;
55003
+ throw err19;
54946
55004
  });
54947
55005
  }
54948
55006
  }
@@ -55538,15 +55596,15 @@ async function makeRequest2(url2, fetchOptions, fetchFn = fetch) {
55538
55596
  return response;
55539
55597
  }
55540
55598
  function handleResponseError2(e3, url2) {
55541
- let err18 = e3;
55542
- if (err18.name === "AbortError") {
55543
- err18 = new GoogleGenerativeAIAbortError2(`Request aborted when fetching ${url2.toString()}: ${e3.message}`);
55544
- err18.stack = e3.stack;
55599
+ let err19 = e3;
55600
+ if (err19.name === "AbortError") {
55601
+ err19 = new GoogleGenerativeAIAbortError2(`Request aborted when fetching ${url2.toString()}: ${e3.message}`);
55602
+ err19.stack = e3.stack;
55545
55603
  } else if (!(e3 instanceof GoogleGenerativeAIFetchError2 || e3 instanceof GoogleGenerativeAIRequestInputError2)) {
55546
- err18 = new GoogleGenerativeAIError2(`Error fetching from ${url2.toString()}: ${e3.message}`);
55547
- err18.stack = e3.stack;
55604
+ err19 = new GoogleGenerativeAIError2(`Error fetching from ${url2.toString()}: ${e3.message}`);
55605
+ err19.stack = e3.stack;
55548
55606
  }
55549
- throw err18;
55607
+ throw err19;
55550
55608
  }
55551
55609
  async function handleResponseNotOk2(response, url2) {
55552
55610
  let message = "";
@@ -55906,7 +55964,7 @@ async function runPollLoop(checkState, onTick) {
55906
55964
  if (state === FileState.FAILED) {
55907
55965
  return "failed";
55908
55966
  }
55909
- await (0, import_promises7.setTimeout)(POLL_INTERVAL_MS);
55967
+ await (0, import_promises8.setTimeout)(POLL_INTERVAL_MS);
55910
55968
  }
55911
55969
  return "timeout";
55912
55970
  }
@@ -56036,7 +56094,7 @@ var DEFAULT_MODEL = "gemini-2.5-pro";
56036
56094
  var BYTES_PER_MB = 1048576;
56037
56095
  var MAX_POLL_ATTEMPTS2 = 30;
56038
56096
  async function getFileSize(filePath) {
56039
- const fileStats = await (0, import_promises6.stat)(filePath);
56097
+ const fileStats = await (0, import_promises7.stat)(filePath);
56040
56098
  return fileStats.size;
56041
56099
  }
56042
56100
  function emitStageEnd(onEvent, start) {
@@ -56142,34 +56200,34 @@ var uuid42 = function() {
56142
56200
  };
56143
56201
 
56144
56202
  // ../../node_modules/.pnpm/@anthropic-ai+sdk@0.87.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/internal/errors.mjs
56145
- function isAbortError(err18) {
56146
- return typeof err18 === "object" && err18 !== null && // Spec-compliant fetch implementations
56147
- ("name" in err18 && err18.name === "AbortError" || // Expo fetch
56148
- "message" in err18 && String(err18.message).includes("FetchRequestCanceledException"));
56149
- }
56150
- var castToError = (err18) => {
56151
- if (err18 instanceof Error)
56152
- return err18;
56153
- if (typeof err18 === "object" && err18 !== null) {
56203
+ function isAbortError(err19) {
56204
+ return typeof err19 === "object" && err19 !== null && // Spec-compliant fetch implementations
56205
+ ("name" in err19 && err19.name === "AbortError" || // Expo fetch
56206
+ "message" in err19 && String(err19.message).includes("FetchRequestCanceledException"));
56207
+ }
56208
+ var castToError = (err19) => {
56209
+ if (err19 instanceof Error)
56210
+ return err19;
56211
+ if (typeof err19 === "object" && err19 !== null) {
56154
56212
  try {
56155
- if (Object.prototype.toString.call(err18) === "[object Error]") {
56156
- const error48 = new Error(err18.message, err18.cause ? { cause: err18.cause } : {});
56157
- if (err18.stack)
56158
- error48.stack = err18.stack;
56159
- if (err18.cause && !error48.cause)
56160
- error48.cause = err18.cause;
56161
- if (err18.name)
56162
- error48.name = err18.name;
56213
+ if (Object.prototype.toString.call(err19) === "[object Error]") {
56214
+ const error48 = new Error(err19.message, err19.cause ? { cause: err19.cause } : {});
56215
+ if (err19.stack)
56216
+ error48.stack = err19.stack;
56217
+ if (err19.cause && !error48.cause)
56218
+ error48.cause = err19.cause;
56219
+ if (err19.name)
56220
+ error48.name = err19.name;
56163
56221
  return error48;
56164
56222
  }
56165
56223
  } catch {
56166
56224
  }
56167
56225
  try {
56168
- return new Error(JSON.stringify(err18));
56226
+ return new Error(JSON.stringify(err19));
56169
56227
  } catch {
56170
56228
  }
56171
56229
  }
56172
- return new Error(err18);
56230
+ return new Error(err19);
56173
56231
  };
56174
56232
 
56175
56233
  // ../../node_modules/.pnpm/@anthropic-ai+sdk@0.87.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/core/error.mjs
@@ -56299,13 +56357,13 @@ var validatePositiveInteger = (name, n3) => {
56299
56357
  var safeJSON = (text) => {
56300
56358
  try {
56301
56359
  return JSON.parse(text);
56302
- } catch (err18) {
56360
+ } catch (err19) {
56303
56361
  return void 0;
56304
56362
  }
56305
56363
  };
56306
56364
 
56307
56365
  // ../../node_modules/.pnpm/@anthropic-ai+sdk@0.87.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs
56308
- var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
56366
+ var sleep4 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
56309
56367
 
56310
56368
  // ../../node_modules/.pnpm/@anthropic-ai+sdk@0.87.0_zod@4.3.6/node_modules/@anthropic-ai/sdk/version.mjs
56311
56369
  var VERSION = "0.87.0";
@@ -56863,8 +56921,8 @@ var Stream = class _Stream {
56863
56921
  return ctrl.close();
56864
56922
  const bytes = encodeUTF8(JSON.stringify(value) + "\n");
56865
56923
  ctrl.enqueue(bytes);
56866
- } catch (err18) {
56867
- ctrl.error(err18);
56924
+ } catch (err19) {
56925
+ ctrl.error(err19);
56868
56926
  }
56869
56927
  },
56870
56928
  async cancel() {
@@ -58771,8 +58829,8 @@ var BetaMessageStream = class _BetaMessageStream {
58771
58829
  if (jsonBuf) {
58772
58830
  try {
58773
58831
  newContent.input = partialParse(jsonBuf);
58774
- } catch (err18) {
58775
- const error48 = new AnthropicError(`Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: ${err18}. JSON: ${jsonBuf}`);
58832
+ } catch (err19) {
58833
+ const error48 = new AnthropicError(`Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: ${err19}. JSON: ${jsonBuf}`);
58776
58834
  __classPrivateFieldGet(this, _BetaMessageStream_handleError, "f").call(this, error48);
58777
58835
  }
58778
58836
  }
@@ -58834,17 +58892,17 @@ var BetaMessageStream = class _BetaMessageStream {
58834
58892
  }
58835
58893
  readQueue.length = 0;
58836
58894
  });
58837
- this.on("abort", (err18) => {
58895
+ this.on("abort", (err19) => {
58838
58896
  done = true;
58839
58897
  for (const reader of readQueue) {
58840
- reader.reject(err18);
58898
+ reader.reject(err19);
58841
58899
  }
58842
58900
  readQueue.length = 0;
58843
58901
  });
58844
- this.on("error", (err18) => {
58902
+ this.on("error", (err19) => {
58845
58903
  done = true;
58846
58904
  for (const reader of readQueue) {
58847
- reader.reject(err18);
58905
+ reader.reject(err19);
58848
58906
  }
58849
58907
  readQueue.length = 0;
58850
58908
  });
@@ -61087,17 +61145,17 @@ var MessageStream = class _MessageStream {
61087
61145
  }
61088
61146
  readQueue.length = 0;
61089
61147
  });
61090
- this.on("abort", (err18) => {
61148
+ this.on("abort", (err19) => {
61091
61149
  done = true;
61092
61150
  for (const reader of readQueue) {
61093
- reader.reject(err18);
61151
+ reader.reject(err19);
61094
61152
  }
61095
61153
  readQueue.length = 0;
61096
61154
  });
61097
- this.on("error", (err18) => {
61155
+ this.on("error", (err19) => {
61098
61156
  done = true;
61099
61157
  for (const reader of readQueue) {
61100
- reader.reject(err18);
61158
+ reader.reject(err19);
61101
61159
  }
61102
61160
  readQueue.length = 0;
61103
61161
  });
@@ -61677,7 +61735,7 @@ var BaseAnthropic = class {
61677
61735
  }
61678
61736
  const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`;
61679
61737
  loggerFor(this).info(`${responseInfo} - ${retryMessage}`);
61680
- const errText = await response.text().catch((err19) => castToError(err19).message);
61738
+ const errText = await response.text().catch((err20) => castToError(err20).message);
61681
61739
  const errJSON = safeJSON(errText);
61682
61740
  const errMessage = errJSON ? void 0 : errText;
61683
61741
  loggerFor(this).debug(`[${requestLogID}] response error (${retryMessage})`, formatRequestDetails({
@@ -61688,8 +61746,8 @@ var BaseAnthropic = class {
61688
61746
  message: errMessage,
61689
61747
  durationMs: Date.now() - startTime
61690
61748
  }));
61691
- const err18 = this.makeStatusError(response.status, errJSON, errMessage, response.headers);
61692
- throw err18;
61749
+ const err19 = this.makeStatusError(response.status, errJSON, errMessage, response.headers);
61750
+ throw err19;
61693
61751
  }
61694
61752
  loggerFor(this).info(responseInfo);
61695
61753
  loggerFor(this).debug(`[${requestLogID}] response start`, formatRequestDetails({
@@ -61768,7 +61826,7 @@ var BaseAnthropic = class {
61768
61826
  const maxRetries = options.maxRetries ?? this.maxRetries;
61769
61827
  timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
61770
61828
  }
61771
- await sleep3(timeoutMillis);
61829
+ await sleep4(timeoutMillis);
61772
61830
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
61773
61831
  }
61774
61832
  calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
@@ -62132,25 +62190,25 @@ var import_neverthrow31 = __toESM(require_index_cjs(), 1);
62132
62190
  var import_neverthrow32 = __toESM(require_index_cjs(), 1);
62133
62191
  var import_node_fs3 = require("node:fs");
62134
62192
  var import_neverthrow33 = __toESM(require_index_cjs(), 1);
62135
- var import_promises15 = require("node:timers/promises");
62193
+ var import_promises16 = require("node:timers/promises");
62136
62194
 
62137
62195
  // ../../agents/explorer/dist/index.js
62138
62196
  var import_node_child_process3 = require("node:child_process");
62139
62197
  var import_node_fs = require("node:fs");
62140
62198
  var import_node_path4 = __toESM(require("node:path"), 1);
62141
62199
  var import_neverthrow15 = __toESM(require_index_cjs(), 1);
62142
- var import_promises8 = require("node:timers/promises");
62200
+ var import_promises9 = require("node:timers/promises");
62143
62201
  var import_neverthrow16 = __toESM(require_index_cjs(), 1);
62144
62202
  var import_neverthrow17 = __toESM(require_index_cjs(), 1);
62145
- var import_promises9 = require("node:fs/promises");
62203
+ var import_promises10 = require("node:fs/promises");
62146
62204
  var import_node_path5 = __toESM(require("node:path"), 1);
62147
62205
  var import_neverthrow18 = __toESM(require_index_cjs(), 1);
62148
62206
  var import_node_child_process4 = require("node:child_process");
62149
- var import_promises10 = require("node:fs/promises");
62207
+ var import_promises11 = require("node:fs/promises");
62150
62208
  var import_neverthrow19 = __toESM(require_index_cjs(), 1);
62151
62209
  var import_neverthrow20 = __toESM(require_index_cjs(), 1);
62152
62210
  var import_neverthrow21 = __toESM(require_index_cjs(), 1);
62153
- var import_promises11 = require("node:fs/promises");
62211
+ var import_promises12 = require("node:fs/promises");
62154
62212
  var import_node_path6 = __toESM(require("node:path"), 1);
62155
62213
  var import_neverthrow22 = __toESM(require_index_cjs(), 1);
62156
62214
  async function runFfmpeg(arguments_) {
@@ -62266,7 +62324,7 @@ function deriveScreenLabel(tree, stepIndex) {
62266
62324
  async function persistScreenshot(params) {
62267
62325
  const screenshotPath = import_node_path5.default.join(params.screenshotsDirectory, `${params.screenLabel}.png`);
62268
62326
  const safeWriteFile = (0, import_neverthrow18.fromAsyncThrowable)(
62269
- import_promises9.writeFile,
62327
+ import_promises10.writeFile,
62270
62328
  (cause) => ({ type: "WRITE_FAILED", cause })
62271
62329
  );
62272
62330
  const writeResult = safeWriteFile(screenshotPath, Buffer.from(params.data, "base64"));
@@ -62331,7 +62389,7 @@ function buildSnapshotRecord(rawElements, { stepIndex, context }) {
62331
62389
  async function handleScreenshotData(data, params) {
62332
62390
  if (params.context.screenshotsDir !== void 0) {
62333
62391
  const safeWrite2 = (0, import_neverthrow18.fromAsyncThrowable)(
62334
- import_promises9.writeFile,
62392
+ import_promises10.writeFile,
62335
62393
  (cause) => ({ type: "WRITE_FAILED", cause })
62336
62394
  );
62337
62395
  const snapshotPath = import_node_path5.default.join(params.context.screenshotsDir, `${params.screenLabel}.txt`);
@@ -62668,7 +62726,8 @@ async function setupQuery(config3, outputTools) {
62668
62726
  const allowedTools = [
62669
62727
  ...config3.allowedTools ?? [],
62670
62728
  ...outputTools.allowedToolNames,
62671
- VIEW_UI_TOOL_NAME
62729
+ VIEW_UI_TOOL_NAME,
62730
+ WAIT_SECONDS_TOOL_NAME
62672
62731
  ];
62673
62732
  const disallowedTools = await resolveDisallowedTools(allowedTools);
62674
62733
  const inputQueue = new MessageQueue();
@@ -62744,7 +62803,7 @@ function startTimeout(timeoutMs, context) {
62744
62803
  var EXPLORER_FINDING_SCHEMA = findingSchema.omit({ agent: true });
62745
62804
  var INTERRUPT_DRAIN_TIMEOUT_MS = 1e4;
62746
62805
  async function interruptOrTimeout(queryRunner) {
62747
- await Promise.race([queryRunner.interrupt(), (0, import_promises8.setTimeout)(INTERRUPT_DRAIN_TIMEOUT_MS)]);
62806
+ await Promise.race([queryRunner.interrupt(), (0, import_promises9.setTimeout)(INTERRUPT_DRAIN_TIMEOUT_MS)]);
62748
62807
  }
62749
62808
  var safeInterruptOrTimeout = import_neverthrow16.ResultAsync.fromThrowable(
62750
62809
  interruptOrTimeout,
@@ -62904,9 +62963,11 @@ At every reasoning step, maintain a mental ledger:
62904
62963
  - PATH: your current navigation stack from root (e.g. Home > Settings > Privacy)
62905
62964
 
62906
62965
  Consult the ledger before every action. Always prefer navigating to a QUEUE screen over a VISITED one.`;
62966
+ var SESSION_START_RULE = `Before taking any other action \u2014 including initializing the Working State ledger or emitting findings \u2014 call \`view_ui\` once to observe the starting screen`;
62907
62967
  var BACK_NAV_RULE = `After navigating forward to any new screen: tap back, call \`view_ui\`, confirm you returned to the expected parent in PATH \u2014 if not, emit a \`back-nav-failure\` finding, then navigate forward again to continue`;
62908
62968
  var STUCK_LOOP_RULE = `Stuck loop: emit a \`stuck-loop\` finding when any of these occur: (1) \`view_ui\` returns the same screen state 3 or more consecutive steps, (2) the same element has been tapped more than twice with no screen change, (3) PATH shows the same screen at two non-adjacent positions \u2014 before emitting, try one alternative action (scroll, long-press, swipe) to rule out a gesture mismatch`;
62909
62969
  var LOADING_STATE_RULE = `Transient loading state: when \`view_ui\` reveals spinners, skeleton screens, progress bars, "Loading..." text, or placeholder content NOT described in spec or app context, wait and retry \`view_ui\` up to 3 times before treating screen as final \u2014 if loading persists after 3 retries, proceed with what is visible; if spec or app context explicitly describes a loading screen as a step, do not retry \u2014 inspect and assert normally`;
62970
+ var EXPECTED_CONTENT_MISSING_RULE = `Expected content missing: when \`view_ui\` shows no loading indicator yet omits an element named or strongly implied by spec or app context \u2014 and its absence is not semantically consistent with the current screen \u2014 call \`wait_seconds\` with 2\u20135 seconds and retry \`view_ui\` up to 2 times; if element remains absent, emit a \`missing-content\` finding stating what was expected and what was observed`;
62910
62971
  var CLIPPED_ELEMENT_RULE = `Never tap an element tagged \`[clipped-top]\`, \`[clipped-bottom]\`, \`[clipped-left]\`, or \`[clipped-right]\` \u2014 scroll to fully reveal it first, then re-call \`view_ui\` before tapping`;
62911
62972
  var WHAT_TO_TEST_SECTION = `## What to Test
62912
62973
 
@@ -62980,11 +63041,13 @@ ${initialState2}` : void 0,
62980
63041
  }
62981
63042
  var SPEC_RULES_SECTION = `## Rules
62982
63043
 
63044
+ - ${SESSION_START_RULE}
62983
63045
  - ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
62984
63046
  - ${BACK_NAV_RULE}
62985
63047
  - Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
62986
63048
  - ${STUCK_LOOP_RULE}
62987
63049
  - ${LOADING_STATE_RULE}
63050
+ - ${EXPECTED_CONTENT_MISSING_RULE}
62988
63051
  - ${CLIPPED_ELEMENT_RULE}
62989
63052
  - Each item in \`**Assertions**\` is a mandatory pass/fail check \u2014 verify using \`view_ui\`; if the accessibility tree cannot confirm, emit a \`spec-deviation\` finding based on what is observable
62990
63053
  - Flag crash dialogs, unexpected system errors, or navigation failures that occur as a direct result of executing a spec step; if you observe a visibly broken element in passing while navigating, note it without interacting with it`;
@@ -62993,7 +63056,7 @@ function buildSpecModeBody({
62993
63056
  contextBlock,
62994
63057
  environmentSection
62995
63058
  }) {
62996
- return `You are a spec execution agent. Your role is to follow the provided spec exactly \u2014 execute each step in sequence, verify each assertion, and report deviations. Observe and flag obvious breakage encountered in transit, but do not explore or interact with anything outside the spec.
63059
+ return `You are a spec execution agent. Your first action MUST be a \`view_ui\` call. Your role is to follow the provided spec exactly \u2014 execute each step in sequence, verify each assertion, and report deviations. Observe and flag obvious breakage encountered in transit, but do not explore or interact with anything outside the spec.
62997
63060
 
62998
63061
  Verify app against specs below.
62999
63062
 
@@ -63034,17 +63097,19 @@ var FREESTYLE_TEMPLATE = (options) => {
63034
63097
  const environmentSection = buildEnv === "dev" ? `
63035
63098
 
63036
63099
  ${DEV_ENVIRONMENT_SECTION}` : "";
63037
- return `You are a navigation and interaction testing agent. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
63100
+ return `You are a navigation and interaction testing agent. Your first action MUST be a \`view_ui\` call. Your role is to find broken navigation flows and non-functional interactive elements. Do not report content bugs, copy errors, or visual style issues unless they directly prevent a navigation action from completing.
63038
63101
 
63039
63102
  ${contextBlock}
63040
63103
 
63041
63104
  ## Rules
63042
63105
 
63106
+ - ${SESSION_START_RULE}
63043
63107
  - ALWAYS call \`view_ui\` after every action before deciding what to do next \u2014 it is your only way to observe the screen
63044
63108
  - ${BACK_NAV_RULE}
63045
63109
  - Before selecting any action, prefer navigating to a QUEUE screen over re-exploring a VISITED one
63046
63110
  - ${STUCK_LOOP_RULE}
63047
63111
  - ${LOADING_STATE_RULE}
63112
+ - ${EXPECTED_CONTENT_MISSING_RULE}
63048
63113
  - ${CLIPPED_ELEMENT_RULE}
63049
63114
 
63050
63115
  ## Exploration Strategy
@@ -63259,7 +63324,7 @@ function direntToSpecEntry(directory, entry) {
63259
63324
  }
63260
63325
  function scanDirectory(directory) {
63261
63326
  const safeReaddir3 = (0, import_neverthrow22.fromAsyncThrowable)(
63262
- async () => (0, import_promises11.readdir)(directory, { withFileTypes: true }),
63327
+ async () => (0, import_promises12.readdir)(directory, { withFileTypes: true }),
63263
63328
  (cause) => ({ type: "DIR_READ_FAILED", dir: directory, cause })
63264
63329
  );
63265
63330
  return safeReaddir3().orElse((error48) => isNotFound(error48.cause) ? (0, import_neverthrow22.okAsync)([]) : (0, import_neverthrow22.errAsync)(error48)).map(
@@ -63273,7 +63338,7 @@ function readEntries(entries) {
63273
63338
  }
63274
63339
  function readEntry(entry) {
63275
63340
  const safeReadFile6 = (0, import_neverthrow22.fromAsyncThrowable)(
63276
- async () => (0, import_promises11.readFile)(entry.path, "utf8"),
63341
+ async () => (0, import_promises12.readFile)(entry.path, "utf8"),
63277
63342
  (cause) => ({ type: "FILE_READ_FAILED", path: entry.path, cause })
63278
63343
  );
63279
63344
  return safeReadFile6().map((content) => [{ name: entry.name, content }]).orElse((error48) => entry.required ? (0, import_neverthrow22.errAsync)(error48) : (0, import_neverthrow22.okAsync)([]));
@@ -63398,7 +63463,7 @@ function runExplorer(config3) {
63398
63463
  };
63399
63464
  safeConfig.onEvent?.({ type: "STAGE_START", agent: "explorer" });
63400
63465
  return import_neverthrow19.ResultAsync.fromSafePromise(
63401
- (0, import_promises10.mkdir)(runPaths.screenshotsDir, { recursive: true }).catch(() => null)
63466
+ (0, import_promises11.mkdir)(runPaths.screenshotsDir, { recursive: true }).catch(() => null)
63402
63467
  ).andThen(() => runPipeline({ safeConfig, runPaths, start }));
63403
63468
  }
63404
63469
 
@@ -63407,14 +63472,14 @@ var import_neverthrow34 = __toESM(require_index_cjs(), 1);
63407
63472
 
63408
63473
  // ../../agents/inspector/dist/index.js
63409
63474
  var import_neverthrow23 = __toESM(require_index_cjs(), 1);
63410
- var import_promises12 = require("node:fs/promises");
63475
+ var import_promises13 = require("node:fs/promises");
63411
63476
  var import_neverthrow24 = __toESM(require_index_cjs(), 1);
63412
63477
  var import_neverthrow25 = __toESM(require_index_cjs(), 1);
63413
63478
  var import_neverthrow26 = __toESM(require_index_cjs(), 1);
63414
63479
  var import_neverthrow27 = __toESM(require_index_cjs(), 1);
63415
63480
  var import_sharp2 = __toESM(require("sharp"), 1);
63416
63481
  var import_neverthrow28 = __toESM(require_index_cjs(), 1);
63417
- var import_promises13 = require("node:fs/promises");
63482
+ var import_promises14 = require("node:fs/promises");
63418
63483
  var import_node_path7 = __toESM(require("node:path"), 1);
63419
63484
 
63420
63485
  // ../../node_modules/.pnpm/js-yaml@4.1.1/node_modules/js-yaml/dist/js-yaml.mjs
@@ -64380,7 +64445,7 @@ var directiveHandlers = {
64380
64445
  }
64381
64446
  try {
64382
64447
  prefix = decodeURIComponent(prefix);
64383
- } catch (err18) {
64448
+ } catch (err19) {
64384
64449
  throwError(state, "tag prefix is malformed: " + prefix);
64385
64450
  }
64386
64451
  state.tagMap[handle] = prefix;
@@ -65061,7 +65126,7 @@ function readTagProperty(state) {
65061
65126
  }
65062
65127
  try {
65063
65128
  tagName = decodeURIComponent(tagName);
65064
- } catch (err18) {
65129
+ } catch (err19) {
65065
65130
  throwError(state, "tag name is malformed: " + tagName);
65066
65131
  }
65067
65132
  if (isVerbatim) {
@@ -66730,7 +66795,7 @@ async function initArtboardNames({ designStore, config: config3, state }) {
66730
66795
  }
66731
66796
  function readScreenshot(screenshotPath, stepIndex) {
66732
66797
  return import_neverthrow24.ResultAsync.fromThrowable(
66733
- import_promises12.readFile,
66798
+ import_promises13.readFile,
66734
66799
  (cause) => ({ type: "SCREENSHOT_READ_FAILED", stepIndex, cause })
66735
66800
  )(screenshotPath);
66736
66801
  }
@@ -66825,7 +66890,7 @@ function parseMeta(raw) {
66825
66890
  return {};
66826
66891
  }
66827
66892
  async function readAndParseSidecar(sidecarPath) {
66828
- const raw = await (0, import_promises13.readFile)(sidecarPath, "utf8");
66893
+ const raw = await (0, import_promises14.readFile)(sidecarPath, "utf8");
66829
66894
  return parseMeta(raw);
66830
66895
  }
66831
66896
  function readSidecarFile(sidecarPath) {
@@ -66856,7 +66921,7 @@ var FsDesignStore = class {
66856
66921
  }
66857
66922
  listArtboards() {
66858
66923
  return (0, import_neverthrow29.fromAsyncThrowable)(
66859
- import_promises13.readdir,
66924
+ import_promises14.readdir,
66860
66925
  wrapFsError
66861
66926
  )(this.designsDirectory).orElse((fsError) => {
66862
66927
  if (fsError.type === "FS_ERROR" && isEnoent(fsError.cause)) {
@@ -66871,7 +66936,7 @@ var FsDesignStore = class {
66871
66936
  const pngPath = import_node_path7.default.join(this.designsDirectory, `${filename}.png`);
66872
66937
  const sidecarPath = import_node_path7.default.join(this.designsDirectory, `${filename}.meta.yaml`);
66873
66938
  return (0, import_neverthrow29.fromAsyncThrowable)(
66874
- import_promises13.readFile,
66939
+ import_promises14.readFile,
66875
66940
  wrapFsError
66876
66941
  )(pngPath).orElse((fsError) => {
66877
66942
  if (fsError.type === "FS_ERROR" && isEnoent(fsError.cause)) {
@@ -66929,7 +66994,7 @@ function runAnalyserWithRetry(params) {
66929
66994
  ),
66930
66995
  {
66931
66996
  config: { maxAttempts: RETRY_MAX_ATTEMPTS, baseDelayMs: RETRY_BASE_DELAY_MS },
66932
- delayFunction: import_promises14.setTimeout,
66997
+ delayFunction: import_promises15.setTimeout,
66933
66998
  onRetry: ({ attempt, maxAttempts, delayMs, error: error48 }) => {
66934
66999
  onEvent?.({
66935
67000
  type: "AGENT_RETRY",
@@ -67118,7 +67183,7 @@ function runExplorerWithRetry(options) {
67118
67183
  const { explorerConfig, udid, onEvent } = options;
67119
67184
  return withRetry(() => runExplorerWithTeardown(explorerConfig, udid), {
67120
67185
  config: { maxAttempts: RETRY_MAX_ATTEMPTS, baseDelayMs: RETRY_BASE_DELAY_MS },
67121
- delayFunction: import_promises15.setTimeout,
67186
+ delayFunction: import_promises16.setTimeout,
67122
67187
  onRetry: ({ attempt, maxAttempts, delayMs, error: error48 }) => {
67123
67188
  onEvent?.({
67124
67189
  type: "AGENT_RETRY",
@@ -67278,7 +67343,7 @@ function runPipeline2(config3) {
67278
67343
  }
67279
67344
 
67280
67345
  // src/commands/analyse-command.ts
67281
- var import_neverthrow35 = __toESM(require_index_cjs(), 1);
67346
+ var import_neverthrow36 = __toESM(require_index_cjs(), 1);
67282
67347
 
67283
67348
  // src/commands/item-events.ts
67284
67349
  function emitItemStart(identity, at) {
@@ -67313,6 +67378,272 @@ function emitItemFailed(input) {
67313
67378
  });
67314
67379
  }
67315
67380
 
67381
+ // src/shell/debug-agent-events.ts
67382
+ var import_neverthrow35 = __toESM(require_index_cjs(), 1);
67383
+
67384
+ // src/constants.ts
67385
+ var MOBILE_IOS_TOOLS = [
67386
+ "mcp__mobile-ios__tap",
67387
+ "mcp__mobile-ios__double_tap",
67388
+ "mcp__mobile-ios__long_press",
67389
+ "mcp__mobile-ios__swipe",
67390
+ "mcp__mobile-ios__type_text",
67391
+ "mcp__mobile-ios__press_button",
67392
+ "mcp__mobile-ios__launch_app",
67393
+ "mcp__mobile-ios__terminate_app",
67394
+ "mcp__mobile-ios__list_apps",
67395
+ "mcp__mobile-ios__screenshot",
67396
+ "mcp__mobile-ios__accessibility_snapshot"
67397
+ ];
67398
+ var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
67399
+ var MS_PER_SECOND4 = 1e3;
67400
+ var DEFAULT_ABORT_EXIT_CODE = 130;
67401
+ var IDB_INSTALL_MESSAGE = "idb is not installed. Run:\n brew install idb-companion\n pipx install fb-idb\n";
67402
+ var FFMPEG_INSTALL_MESSAGE = "ffmpeg is not installed. Run:\n brew install ffmpeg\n";
67403
+
67404
+ // src/shell/debug-logger-core.ts
67405
+ var ELAPSED_PAD = 8;
67406
+ function formatElapsed(elapsedMs) {
67407
+ const seconds = (elapsedMs / MS_PER_SECOND4).toFixed(2);
67408
+ return `+${seconds}s`.padStart(ELAPSED_PAD, " ");
67409
+ }
67410
+ function createCallTracker() {
67411
+ const starts = /* @__PURE__ */ new Map();
67412
+ return {
67413
+ mark: (key, at) => {
67414
+ const queue = starts.get(key) ?? [];
67415
+ queue.push(at);
67416
+ starts.set(key, queue);
67417
+ },
67418
+ pop: (key) => {
67419
+ const queue = starts.get(key);
67420
+ const start = queue?.shift();
67421
+ if (queue?.length === 0) {
67422
+ starts.delete(key);
67423
+ }
67424
+ return start;
67425
+ }
67426
+ };
67427
+ }
67428
+ function buildLogger(options) {
67429
+ const now = options.now ?? Date.now;
67430
+ const write = options.write ?? ((line) => process.stderr.write(line));
67431
+ const startedAt = now();
67432
+ const tracker = createCallTracker();
67433
+ return {
67434
+ log: (tag, detail) => {
67435
+ const elapsed = now() - startedAt;
67436
+ write(`[debug] ${formatElapsed(elapsed)} ${tag} ${detail}
67437
+ `);
67438
+ },
67439
+ markCall: (key) => {
67440
+ tracker.mark(key, now());
67441
+ },
67442
+ elapsedSince: (key) => {
67443
+ const start = tracker.pop(key);
67444
+ return start === void 0 ? void 0 : now() - start;
67445
+ }
67446
+ };
67447
+ }
67448
+ function formatDurationSuffix(duration3) {
67449
+ if (duration3 === void 0) {
67450
+ return "";
67451
+ }
67452
+ return ` duration=${String(duration3)}ms`;
67453
+ }
67454
+ function formatOptionalNumber(value) {
67455
+ return value === void 0 ? "?" : String(value);
67456
+ }
67457
+
67458
+ // src/shell/debug-agent-events.ts
67459
+ var TOOL_INPUT_MAX = 120;
67460
+ var TOOL_ERROR_MAX = 120;
67461
+ var UNSERIALIZABLE = "[unserializable]";
67462
+ function tryStringify(value) {
67463
+ const result = JSON.stringify(value);
67464
+ return result ?? UNSERIALIZABLE;
67465
+ }
67466
+ var safeStringify = (0, import_neverthrow35.fromThrowable)(tryStringify, () => ({
67467
+ type: "STRINGIFY_FAILED"
67468
+ }));
67469
+ function toolKey(scope, event) {
67470
+ const prefix = scope === void 0 ? "" : `${scope}:`;
67471
+ return `${prefix}${event.agent}:${event.toolName}`;
67472
+ }
67473
+ function stringifyToolInput(input) {
67474
+ if (input === void 0 || input === null) {
67475
+ return "";
67476
+ }
67477
+ if (typeof input === "string") {
67478
+ return input;
67479
+ }
67480
+ return safeStringify(input).unwrapOr(UNSERIALIZABLE);
67481
+ }
67482
+ function truncate(value, max) {
67483
+ return value.length > max ? `${value.slice(0, max)}\u2026` : value;
67484
+ }
67485
+ function handleStageStart(context, event) {
67486
+ context.logger.log(
67487
+ "STAGE_START",
67488
+ `${event.agent} steps=${formatOptionalNumber(event.totalSteps)}`
67489
+ );
67490
+ }
67491
+ function handleStageEnd(context, event) {
67492
+ context.logger.log("STAGE_END", `${event.agent} duration=${String(event.durationMs)}ms`);
67493
+ }
67494
+ function handleToolCall(context, event) {
67495
+ context.logger.markCall(toolKey(context.scope, event));
67496
+ const input = truncate(stringifyToolInput(event.input), TOOL_INPUT_MAX);
67497
+ context.logger.log("TOOL_CALL", `${event.agent} ${event.toolName} ${input}`);
67498
+ }
67499
+ function handleToolResult(context, event) {
67500
+ const suffix = formatDurationSuffix(context.logger.elapsedSince(toolKey(context.scope, event)));
67501
+ context.logger.log("TOOL_RESULT", `${event.agent} ${event.toolName}${suffix}`);
67502
+ }
67503
+ function handleToolError(context, event) {
67504
+ const suffix = formatDurationSuffix(context.logger.elapsedSince(toolKey(context.scope, event)));
67505
+ const error48 = truncate(event.error, TOOL_ERROR_MAX);
67506
+ context.logger.log("TOOL_ERROR", `${event.agent} ${event.toolName}${suffix} ${error48}`);
67507
+ }
67508
+ function handleAgentRetry(context, event) {
67509
+ context.logger.log(
67510
+ "AGENT_RETRY",
67511
+ `${event.agent} attempt=${String(event.attempt)}/${String(event.maxAttempts)} delay=${String(event.delayMs)}ms`
67512
+ );
67513
+ }
67514
+ function handleAgentFailed(context, event) {
67515
+ context.logger.log(
67516
+ "AGENT_FAILED_NON_CRITICAL",
67517
+ `${event.agent} attempts=${String(event.attempts)}`
67518
+ );
67519
+ }
67520
+ function handleTimeoutGrace(context, event) {
67521
+ context.logger.log(
67522
+ "TIMEOUT_GRACE_ENTERED",
67523
+ `${event.agent} grace=${String(event.gracePeriodMs)}ms`
67524
+ );
67525
+ }
67526
+ function handleInspectorStepStart(context, event) {
67527
+ context.logger.log(
67528
+ "INSPECTOR_STEP_START",
67529
+ `step=${String(event.stepIndex)}/${formatOptionalNumber(event.totalSteps)} screen=${event.screenLabel}`
67530
+ );
67531
+ }
67532
+ function handleInspectorStep(context, event) {
67533
+ context.logger.log(
67534
+ "INSPECTOR_STEP",
67535
+ `step=${String(event.stepIndex)} screen=${event.screenLabel} outcome=${event.outcome}`
67536
+ );
67537
+ }
67538
+ function handleVisualStep(context, event) {
67539
+ context.logger.log("VISUAL_STEP", `step=${String(event.stepIndex)} screen=${event.screenLabel}`);
67540
+ }
67541
+ function handleError(context, event) {
67542
+ context.logger.log("ERROR", `${event.agent} ${event.message}`);
67543
+ }
67544
+ var AGENT_EVENT_HANDLERS = {
67545
+ STAGE_START: handleStageStart,
67546
+ STAGE_END: handleStageEnd,
67547
+ TOOL_CALL: handleToolCall,
67548
+ TOOL_RESULT: handleToolResult,
67549
+ TOOL_ERROR: handleToolError,
67550
+ AGENT_RETRY: handleAgentRetry,
67551
+ AGENT_FAILED_NON_CRITICAL: handleAgentFailed,
67552
+ TIMEOUT_GRACE_ENTERED: handleTimeoutGrace,
67553
+ INSPECTOR_STEP_START: handleInspectorStepStart,
67554
+ INSPECTOR_STEP: handleInspectorStep,
67555
+ VISUAL_STEP: handleVisualStep,
67556
+ ERROR: handleError
67557
+ };
67558
+ function handleAgentEvent(context, event) {
67559
+ const handler = AGENT_EVENT_HANDLERS[event.type];
67560
+ if (handler === void 0) {
67561
+ return;
67562
+ }
67563
+ handler(context, event);
67564
+ }
67565
+
67566
+ // src/shell/debug-suite-events.ts
67567
+ function handleItemQueued(logger, event) {
67568
+ logger.log("ITEM_QUEUED", `id=${event.item.id} name=${event.item.name}`);
67569
+ }
67570
+ function handleItemStarted(logger, event) {
67571
+ logger.log(
67572
+ "ITEM_STARTED",
67573
+ `id=${event.itemId} name=${event.itemName} sim=${event.simulatorUdid}`
67574
+ );
67575
+ }
67576
+ function handleItemCompleted(logger, event) {
67577
+ logger.log(
67578
+ "ITEM_COMPLETED",
67579
+ `id=${event.itemId} findings=${String(event.findingsCount)} duration=${String(event.durationMs)}ms`
67580
+ );
67581
+ }
67582
+ function handleItemFailed(logger, event) {
67583
+ logger.log(
67584
+ "ITEM_FAILED",
67585
+ `id=${event.itemId} error=${event.error} duration=${String(event.durationMs)}ms`
67586
+ );
67587
+ }
67588
+ function handleItemTimeout(logger, event) {
67589
+ logger.log("ITEM_TIMEOUT", `id=${event.itemId} duration=${String(event.durationMs)}ms`);
67590
+ }
67591
+ function handleSimulatorUnhealthy(logger, event) {
67592
+ logger.log(
67593
+ "SIMULATOR_UNHEALTHY",
67594
+ `sim=${event.simulatorUdid} failures=${String(event.consecutiveFailures)}`
67595
+ );
67596
+ }
67597
+ function handleSuiteCompleted(logger, event) {
67598
+ logger.log(
67599
+ "SUITE_COMPLETED",
67600
+ `items=${String(event.totalItems)} findings=${String(event.totalFindings)} duration=${String(event.durationMs)}ms aborted=${String(event.aborted)}`
67601
+ );
67602
+ }
67603
+ var SUITE_EVENT_HANDLERS = {
67604
+ ITEM_QUEUED: handleItemQueued,
67605
+ ITEM_STARTED: handleItemStarted,
67606
+ ITEM_COMPLETED: handleItemCompleted,
67607
+ ITEM_FAILED: handleItemFailed,
67608
+ ITEM_TIMEOUT: handleItemTimeout,
67609
+ SIMULATOR_UNHEALTHY: handleSimulatorUnhealthy,
67610
+ SUITE_COMPLETED: handleSuiteCompleted
67611
+ };
67612
+ function handleSuiteEvent(logger, event) {
67613
+ const handler = SUITE_EVENT_HANDLERS[event.type];
67614
+ if (handler === void 0) {
67615
+ return;
67616
+ }
67617
+ handler(logger, event);
67618
+ }
67619
+
67620
+ // src/shell/debug-logger.ts
67621
+ function passthroughAgent(handler) {
67622
+ return handler;
67623
+ }
67624
+ function passthroughSuite(handler) {
67625
+ return handler;
67626
+ }
67627
+ function createDebugLogger(options) {
67628
+ if (options.enabled !== true) {
67629
+ return {
67630
+ wrapAgentHandler: passthroughAgent,
67631
+ wrapSuiteHandler: passthroughSuite
67632
+ };
67633
+ }
67634
+ const logger = buildLogger(options);
67635
+ return {
67636
+ wrapAgentHandler: (handler, scope) => (event) => {
67637
+ handleAgentEvent({ logger, scope }, event);
67638
+ handler(event);
67639
+ },
67640
+ wrapSuiteHandler: (handler) => (event) => {
67641
+ handleSuiteEvent(logger, event);
67642
+ handler(event);
67643
+ }
67644
+ };
67645
+ }
67646
+
67316
67647
  // src/shell/trigger-abort.ts
67317
67648
  function triggerAbort() {
67318
67649
  process.kill(process.pid, "SIGINT");
@@ -67353,7 +67684,7 @@ function buildArtifacts(videoPath) {
67353
67684
  return { videoPath, videoPath2x: "", videoPath4x: videoPath, findings: [], snapshots: [] };
67354
67685
  }
67355
67686
  async function checkVideoPathExists(videoPath) {
67356
- const safeAccess = (0, import_neverthrow35.fromAsyncThrowable)(import_promises16.access, () => ({ type: "FILE_NOT_FOUND" }));
67687
+ const safeAccess = (0, import_neverthrow36.fromAsyncThrowable)(import_promises17.access, () => ({ type: "FILE_NOT_FOUND" }));
67357
67688
  const result = await safeAccess(videoPath);
67358
67689
  return result.isOk();
67359
67690
  }
@@ -67374,20 +67705,32 @@ function handleAnalyseError(state, errorType) {
67374
67705
  `);
67375
67706
  process.exit(1);
67376
67707
  }
67377
- async function runAnalysisAndExit(artifacts, apiKey) {
67378
- const identity = {
67708
+ function buildAnalyseIdentity() {
67709
+ return {
67379
67710
  display: createSoloDisplay(),
67380
67711
  itemId: ITEM_ID,
67381
67712
  itemName: ITEM_NAME,
67382
67713
  simulatorUdid: ""
67383
67714
  };
67384
- const startedAt = Date.now();
67385
- emitItemStart(identity, startedAt);
67386
- const onEvent = (event) => {
67715
+ }
67716
+ function buildAnalyseEventHandler(identity, debug) {
67717
+ const debugLogger = createDebugLogger({ enabled: debug });
67718
+ const baseHandler = (event) => {
67387
67719
  identity.display.onEvent({ ...event, itemId: ITEM_ID });
67388
67720
  };
67721
+ return debugLogger.wrapAgentHandler(baseHandler);
67722
+ }
67723
+ async function runAnalysisAndExit(input) {
67724
+ const identity = buildAnalyseIdentity();
67725
+ const startedAt = Date.now();
67726
+ emitItemStart(identity, startedAt);
67727
+ const onEvent = buildAnalyseEventHandler(identity, input.debug);
67389
67728
  const state = { identity, startedAt };
67390
- const result = await runAnalysis(artifacts, { apiKey, videoVariant: "1x", onEvent });
67729
+ const result = await runAnalysis(input.artifacts, {
67730
+ apiKey: input.apiKey,
67731
+ videoVariant: "1x",
67732
+ onEvent
67733
+ });
67391
67734
  result.match(
67392
67735
  (findings) => {
67393
67736
  handleAnalyseSuccess(state, findings);
@@ -67397,13 +67740,16 @@ async function runAnalysisAndExit(artifacts, apiKey) {
67397
67740
  }
67398
67741
  );
67399
67742
  }
67400
- async function runAnalyseCommand(videoPath, config3) {
67743
+ function ensureApiKey(config3) {
67401
67744
  const apiKey = config3.GOOGLE_GENERATIVE_AI_API_KEY ?? "";
67402
67745
  if (!apiKey) {
67403
67746
  process.stderr.write("GOOGLE_GENERATIVE_AI_API_KEY is not set\n");
67404
67747
  process.exit(1);
67405
67748
  return;
67406
67749
  }
67750
+ return apiKey;
67751
+ }
67752
+ async function resolveVideoPath2(videoPath) {
67407
67753
  if (videoPath === void 0) {
67408
67754
  process.stderr.write('A video path is required. Pass the path printed by "xqa explore".\n');
67409
67755
  process.exit(1);
@@ -67415,11 +67761,26 @@ async function runAnalyseCommand(videoPath, config3) {
67415
67761
  process.exit(1);
67416
67762
  return;
67417
67763
  }
67418
- await runAnalysisAndExit(buildArtifacts(videoPath), apiKey);
67764
+ return videoPath;
67765
+ }
67766
+ async function runAnalyseCommand(input) {
67767
+ const apiKey = ensureApiKey(input.config);
67768
+ if (apiKey === void 0) {
67769
+ return;
67770
+ }
67771
+ const videoPath = await resolveVideoPath2(input.videoPath);
67772
+ if (videoPath === void 0) {
67773
+ return;
67774
+ }
67775
+ await runAnalysisAndExit({
67776
+ artifacts: buildArtifacts(videoPath),
67777
+ apiKey,
67778
+ debug: input.debug
67779
+ });
67419
67780
  }
67420
67781
 
67421
67782
  // src/core/completion-generator.ts
67422
- var import_neverthrow36 = __toESM(require_index_cjs(), 1);
67783
+ var import_neverthrow37 = __toESM(require_index_cjs(), 1);
67423
67784
  function extractLongFlags(flags) {
67424
67785
  return flags.split(/[\s,]+/).filter((token) => token.startsWith("--"));
67425
67786
  }
@@ -67509,9 +67870,9 @@ complete -F _xqa_completion xqa`;
67509
67870
  }
67510
67871
  function generateCompletion(commands, shell) {
67511
67872
  if (shell !== "bash" && shell !== "zsh") {
67512
- return (0, import_neverthrow36.err)({ type: "UNSUPPORTED_SHELL", shell });
67873
+ return (0, import_neverthrow37.err)({ type: "UNSUPPORTED_SHELL", shell });
67513
67874
  }
67514
- return (0, import_neverthrow36.ok)(shell === "zsh" ? generateZshCompletion(commands) : generateBashCompletion(commands));
67875
+ return (0, import_neverthrow37.ok)(shell === "zsh" ? generateZshCompletion(commands) : generateBashCompletion(commands));
67515
67876
  }
67516
67877
 
67517
67878
  // src/commands/completion-command.ts
@@ -67546,39 +67907,19 @@ function runCompletionCommand(program3, shell) {
67546
67907
  // src/commands/explore-command.ts
67547
67908
  var import_node_path10 = __toESM(require("node:path"), 1);
67548
67909
 
67549
- // src/constants.ts
67550
- var MOBILE_IOS_TOOLS = [
67551
- "mcp__mobile-ios__tap",
67552
- "mcp__mobile-ios__double_tap",
67553
- "mcp__mobile-ios__long_press",
67554
- "mcp__mobile-ios__swipe",
67555
- "mcp__mobile-ios__type_text",
67556
- "mcp__mobile-ios__press_button",
67557
- "mcp__mobile-ios__launch_app",
67558
- "mcp__mobile-ios__terminate_app",
67559
- "mcp__mobile-ios__list_apps",
67560
- "mcp__mobile-ios__screenshot",
67561
- "mcp__mobile-ios__accessibility_snapshot"
67562
- ];
67563
- var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
67564
- var MS_PER_SECOND3 = 1e3;
67565
- var DEFAULT_ABORT_EXIT_CODE = 130;
67566
- var IDB_INSTALL_MESSAGE = "idb is not installed. Run:\n brew install idb-companion\n pipx install fb-idb\n";
67567
- var FFMPEG_INSTALL_MESSAGE = "ffmpeg is not installed. Run:\n brew install ffmpeg\n";
67568
-
67569
67910
  // src/core/last-path.ts
67570
67911
  var import_node_fs4 = require("node:fs");
67571
67912
  var import_node_path8 = __toESM(require("node:path"), 1);
67572
- var import_neverthrow37 = __toESM(require_index_cjs(), 1);
67913
+ var import_neverthrow38 = __toESM(require_index_cjs(), 1);
67573
67914
  function resolveLastPath(argument, stateContent) {
67574
67915
  if (argument !== void 0) {
67575
- return (0, import_neverthrow37.ok)(argument);
67916
+ return (0, import_neverthrow38.ok)(argument);
67576
67917
  }
67577
67918
  const trimmed = stateContent?.trim();
67578
67919
  if (trimmed) {
67579
- return (0, import_neverthrow37.ok)(trimmed);
67920
+ return (0, import_neverthrow38.ok)(trimmed);
67580
67921
  }
67581
- return (0, import_neverthrow37.err)({ type: "NO_ARG_AND_NO_STATE" });
67922
+ return (0, import_neverthrow38.err)({ type: "NO_ARG_AND_NO_STATE" });
67582
67923
  }
67583
67924
  function lastPathFilePath(xqaDirectoryectory) {
67584
67925
  return import_node_path8.default.join(xqaDirectoryectory, "last-findings-path");
@@ -67588,9 +67929,9 @@ function writeLastPath(xqaDirectory, findingsPath) {
67588
67929
  }
67589
67930
 
67590
67931
  // src/shell/app-context.ts
67591
- var import_promises17 = require("node:fs/promises");
67932
+ var import_promises18 = require("node:fs/promises");
67592
67933
  var import_node_path9 = __toESM(require("node:path"), 1);
67593
- var import_neverthrow38 = __toESM(require_index_cjs(), 1);
67934
+ var import_neverthrow39 = __toESM(require_index_cjs(), 1);
67594
67935
  var HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
67595
67936
  function isEnoentError(value) {
67596
67937
  return value !== null && typeof value === "object" && "code" in value && value.code === "ENOENT";
@@ -67600,10 +67941,10 @@ function toAppContextError(cause) {
67600
67941
  }
67601
67942
  function absentContext() {
67602
67943
  const absent = void 0;
67603
- return (0, import_neverthrow38.ok)(absent);
67944
+ return (0, import_neverthrow39.ok)(absent);
67604
67945
  }
67605
- var safeReadFile2 = import_neverthrow38.ResultAsync.fromThrowable(
67606
- async (filePath) => (0, import_promises17.readFile)(filePath, "utf8"),
67946
+ var safeReadFile2 = import_neverthrow39.ResultAsync.fromThrowable(
67947
+ async (filePath) => (0, import_promises18.readFile)(filePath, "utf8"),
67607
67948
  toAppContextError
67608
67949
  );
67609
67950
  function stripAndNormalize(content) {
@@ -67616,7 +67957,7 @@ function readContextFile(xqaDirectory, filename) {
67616
67957
  if (isEnoentError(error48.cause)) {
67617
67958
  return absentContext();
67618
67959
  }
67619
- return (0, import_neverthrow38.err)(error48);
67960
+ return (0, import_neverthrow39.err)(error48);
67620
67961
  });
67621
67962
  }
67622
67963
  function readAppContext(xqaDirectory) {
@@ -67628,7 +67969,7 @@ function readExploreContext(xqaDirectory) {
67628
67969
 
67629
67970
  // src/shell/ffmpeg-check.ts
67630
67971
  var import_node_child_process5 = require("node:child_process");
67631
- var import_neverthrow39 = __toESM(require_index_cjs(), 1);
67972
+ var import_neverthrow40 = __toESM(require_index_cjs(), 1);
67632
67973
  async function whichFfmpeg() {
67633
67974
  const { promise: promise2, resolve, reject } = Promise.withResolvers();
67634
67975
  (0, import_node_child_process5.execFile)("which", ["ffmpeg"], (error48, stdout) => {
@@ -67640,7 +67981,7 @@ async function whichFfmpeg() {
67640
67981
  });
67641
67982
  return promise2;
67642
67983
  }
67643
- var safeWhichFfmpeg = (0, import_neverthrow39.fromAsyncThrowable)(
67984
+ var safeWhichFfmpeg = (0, import_neverthrow40.fromAsyncThrowable)(
67644
67985
  whichFfmpeg,
67645
67986
  () => ({ type: "FFMPEG_NOT_FOUND" })
67646
67987
  );
@@ -67677,7 +68018,7 @@ function buildExplorerConfig2({
67677
68018
  mode: "freestyle",
67678
68019
  mcpServers: createDefaultMcpServers(),
67679
68020
  allowedTools: ALLOWED_TOOLS,
67680
- timeoutMs: timeoutSeconds === void 0 ? void 0 : timeoutSeconds * MS_PER_SECOND3,
68021
+ timeoutMs: timeoutSeconds === void 0 ? void 0 : timeoutSeconds * MS_PER_SECOND4,
67681
68022
  appContext,
67682
68023
  initialState: resolvedStartingState,
67683
68024
  buildEnv: config3.QA_BUILD_ENV
@@ -67778,9 +68119,11 @@ function buildExploreRunState({
67778
68119
  simulatorUdid
67779
68120
  }) {
67780
68121
  const { config: config3, xqaDirectory } = options;
67781
- const onEvent = (event) => {
68122
+ const debugLogger = createDebugLogger({ enabled: input.debug ?? false });
68123
+ const baseHandler = (event) => {
67782
68124
  identity.display.onEvent({ ...event, itemId: ITEM_ID2 });
67783
68125
  };
68126
+ const onEvent = debugLogger.wrapAgentHandler(baseHandler);
67784
68127
  const onSuccess = buildSuccessHandler({ input, xqaDirectory, identity, startedAt });
67785
68128
  const onError = buildErrorHandler(identity, startedAt);
67786
68129
  return { input, config: config3, xqaDirectory, onEvent, onSuccess, onError, simulatorUdid };
@@ -67929,7 +68272,7 @@ function runInitCommand() {
67929
68272
  // src/commands/review-command.ts
67930
68273
  var import_node_fs7 = require("node:fs");
67931
68274
  var import_node_path14 = __toESM(require("node:path"), 1);
67932
- var import_neverthrow41 = __toESM(require_index_cjs(), 1);
68275
+ var import_neverthrow42 = __toESM(require_index_cjs(), 1);
67933
68276
 
67934
68277
  // ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
67935
68278
  var isUpKey = (key, keybindings = []) => (
@@ -69311,9 +69654,9 @@ var RemoveFileError = class extends Error {
69311
69654
  // ../../node_modules/.pnpm/@inquirer+external-editor@1.0.3_@types+node@22.19.15/node_modules/@inquirer/external-editor/dist/esm/index.js
69312
69655
  function editAsync(text = "", callback, fileOptions) {
69313
69656
  const editor = new ExternalEditor(text, fileOptions);
69314
- editor.runAsync((err18, result) => {
69315
- if (err18) {
69316
- setImmediate(callback, err18, void 0);
69657
+ editor.runAsync((err19, result) => {
69658
+ if (err19) {
69659
+ setImmediate(callback, err19, void 0);
69317
69660
  } else {
69318
69661
  try {
69319
69662
  editor.cleanup();
@@ -70334,7 +70677,7 @@ var esm_default11 = createPrompt((config3, done) => {
70334
70677
  });
70335
70678
 
70336
70679
  // src/review-session.ts
70337
- var import_neverthrow40 = __toESM(require_index_cjs(), 1);
70680
+ var import_neverthrow41 = __toESM(require_index_cjs(), 1);
70338
70681
  var CONFIDENCE_PERCENT = 100;
70339
70682
  var FLOW_COL_WIDTH = 35;
70340
70683
  var TRIGGER_COL_WIDTH = 16;
@@ -70487,15 +70830,15 @@ async function runInteractiveLoop(findings, existing) {
70487
70830
  }
70488
70831
  return { staged: state.staged, undoneKeys: state.undoneKeys };
70489
70832
  }
70490
- var safeRunInteractiveLoop = (0, import_neverthrow40.fromAsyncThrowable)(
70833
+ var safeRunInteractiveLoop = (0, import_neverthrow41.fromAsyncThrowable)(
70491
70834
  runInteractiveLoop,
70492
70835
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
70493
70836
  );
70494
70837
 
70495
70838
  // src/commands/review-command.ts
70496
- var safeReadFile3 = (0, import_neverthrow41.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
70497
- var safeParseJson3 = (0, import_neverthrow41.fromThrowable)(JSON.parse);
70498
- var safeWrite = (0, import_neverthrow41.fromThrowable)((filePath, content) => {
70839
+ var safeReadFile3 = (0, import_neverthrow42.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
70840
+ var safeParseJson3 = (0, import_neverthrow42.fromThrowable)(JSON.parse);
70841
+ var safeWrite = (0, import_neverthrow42.fromThrowable)((filePath, content) => {
70499
70842
  (0, import_node_fs7.writeFileSync)(filePath, content);
70500
70843
  });
70501
70844
  function readLastPath(xqaDirectory) {
@@ -70512,13 +70855,13 @@ function isPipelineOutput(data) {
70512
70855
  function readFindings(filePath) {
70513
70856
  const readResult = safeReadFile3(filePath);
70514
70857
  if (readResult.isErr()) {
70515
- return (0, import_neverthrow41.err)("not-found");
70858
+ return (0, import_neverthrow42.err)("not-found");
70516
70859
  }
70517
70860
  return safeParseJson3(readResult.value).mapErr(() => "invalid").andThen((data) => {
70518
70861
  if (!isPipelineOutput(data)) {
70519
- return (0, import_neverthrow41.err)("invalid");
70862
+ return (0, import_neverthrow42.err)("invalid");
70520
70863
  }
70521
- return (0, import_neverthrow41.ok)(data);
70864
+ return (0, import_neverthrow42.ok)(data);
70522
70865
  });
70523
70866
  }
70524
70867
  function loadExistingDismissals(filePath) {
@@ -70591,7 +70934,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70591
70934
  "No findings path provided and no last path found. Run: xqa review <findings-path>\n"
70592
70935
  );
70593
70936
  process.exit(1);
70594
- return (0, import_neverthrow41.err)();
70937
+ return (0, import_neverthrow42.err)();
70595
70938
  }
70596
70939
  const resolvedPath = resolvedPathResult.value;
70597
70940
  const findingsResult = readFindings(resolvedPath);
@@ -70604,9 +70947,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70604
70947
  `);
70605
70948
  }
70606
70949
  process.exit(1);
70607
- return (0, import_neverthrow41.err)();
70950
+ return (0, import_neverthrow42.err)();
70608
70951
  }
70609
- return (0, import_neverthrow41.ok)({ resolvedPath, output: findingsResult.value });
70952
+ return (0, import_neverthrow42.ok)({ resolvedPath, output: findingsResult.value });
70610
70953
  }
70611
70954
  async function runReviewLoop({
70612
70955
  findings,
@@ -70680,40 +71023,40 @@ function stripExtensions(filename) {
70680
71023
  // src/commands/spec-resolver.ts
70681
71024
  var import_node_fs8 = require("node:fs");
70682
71025
  var import_node_path16 = __toESM(require("node:path"), 1);
70683
- var import_neverthrow43 = __toESM(require_index_cjs(), 1);
71026
+ var import_neverthrow44 = __toESM(require_index_cjs(), 1);
70684
71027
 
70685
71028
  // src/spec-frontmatter.ts
70686
- var import_neverthrow42 = __toESM(require_index_cjs(), 1);
71029
+ var import_neverthrow43 = __toESM(require_index_cjs(), 1);
70687
71030
  var FRONTMATTER_OPEN_LEN = 4;
70688
71031
  var FRONTMATTER_MARKER_LEN = 3;
70689
71032
  function extractFrontmatterBlock(content) {
70690
71033
  const normalized = content.replaceAll("\r\n", "\n");
70691
71034
  if (!normalized.startsWith("---")) {
70692
- return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71035
+ return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
70693
71036
  }
70694
71037
  const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
70695
71038
  if (end === -1) {
70696
- return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71039
+ return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
70697
71040
  }
70698
- return (0, import_neverthrow42.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
71041
+ return (0, import_neverthrow43.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
70699
71042
  }
70700
71043
  function parseTimeout(fields) {
70701
71044
  const raw = fields.get("timeout");
70702
71045
  if (raw === void 0) {
70703
- return (0, import_neverthrow42.ok)(raw);
71046
+ return (0, import_neverthrow43.ok)(raw);
70704
71047
  }
70705
71048
  const parsed = Number(raw);
70706
71049
  if (Number.isNaN(parsed) || parsed <= 0) {
70707
- return (0, import_neverthrow42.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
71050
+ return (0, import_neverthrow43.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
70708
71051
  }
70709
- return (0, import_neverthrow42.ok)(parsed);
71052
+ return (0, import_neverthrow43.ok)(parsed);
70710
71053
  }
70711
71054
  function parseSpecFrontmatter(content) {
70712
71055
  return extractFrontmatterBlock(content).andThen((block) => {
70713
71056
  const fields = parseYamlFields(block);
70714
71057
  const feature = fields.get("feature");
70715
71058
  if (feature === void 0) {
70716
- return (0, import_neverthrow42.err)({ type: "MISSING_FIELD", field: "feature" });
71059
+ return (0, import_neverthrow43.err)({ type: "MISSING_FIELD", field: "feature" });
70717
71060
  }
70718
71061
  return parseTimeout(fields).map((timeout) => ({ feature, timeout }));
70719
71062
  });
@@ -70735,12 +71078,12 @@ function parseYamlFields(block) {
70735
71078
  }
70736
71079
 
70737
71080
  // src/commands/spec-resolver.ts
70738
- var safeReadFile4 = (0, import_neverthrow43.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
70739
- var safeReaddir = (0, import_neverthrow43.fromThrowable)(
71081
+ var safeReadFile4 = (0, import_neverthrow44.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
71082
+ var safeReaddir = (0, import_neverthrow44.fromThrowable)(
70740
71083
  (directory) => (0, import_node_fs8.readdirSync)(directory, { recursive: true, encoding: "utf8" })
70741
71084
  );
70742
71085
  var CANCEL = "xqa:cancel";
70743
- var safeSelect = import_neverthrow43.ResultAsync.fromThrowable(
71086
+ var safeSelect = import_neverthrow44.ResultAsync.fromThrowable(
70744
71087
  esm_default11,
70745
71088
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
70746
71089
  );
@@ -70805,7 +71148,7 @@ function buildSpecExplorer(input, context) {
70805
71148
  specFiles: [context.absolutePath],
70806
71149
  mcpServers: createDefaultMcpServers(),
70807
71150
  allowedTools: ALLOWED_TOOLS,
70808
- timeoutMs: context.timeout === void 0 ? void 0 : context.timeout * MS_PER_SECOND3,
71151
+ timeoutMs: context.timeout === void 0 ? void 0 : context.timeout * MS_PER_SECOND4,
70809
71152
  appContext: context.appContext,
70810
71153
  buildEnv: context.config.QA_BUILD_ENV
70811
71154
  };
@@ -70864,9 +71207,11 @@ async function executeSpec(input, context) {
70864
71207
  };
70865
71208
  const startedAt = Date.now();
70866
71209
  emitItemStart(identity, startedAt);
70867
- const onEvent = (event) => {
71210
+ const debugLogger = createDebugLogger({ enabled: input.debug ?? false });
71211
+ const baseHandler = (event) => {
70868
71212
  identity.display.onEvent({ ...event, itemId: identity.itemId });
70869
71213
  };
71214
+ const onEvent = debugLogger.wrapAgentHandler(baseHandler);
70870
71215
  const { onSuccess, onError } = handleSpecResult({ identity, startedAt }, context);
70871
71216
  const result = await runPipeline2(buildPipelineConfig2({ input, context, onEvent }));
70872
71217
  result.match(onSuccess, onError);
@@ -70928,7 +71273,7 @@ function runUpdateCommand() {
70928
71273
  var import_node_path18 = __toESM(require("node:path"), 1);
70929
71274
  var import_node_url2 = require("node:url");
70930
71275
  var import_dotenv = __toESM(require_main(), 1);
70931
- var import_neverthrow44 = __toESM(require_index_cjs(), 1);
71276
+ var import_neverthrow45 = __toESM(require_index_cjs(), 1);
70932
71277
 
70933
71278
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
70934
71279
  var external_exports2 = {};
@@ -71668,8 +72013,8 @@ var ZodType2 = class {
71668
72013
  } : {
71669
72014
  issues: ctx.common.issues
71670
72015
  };
71671
- } catch (err18) {
71672
- if (err18?.message?.toLowerCase()?.includes("encountered")) {
72016
+ } catch (err19) {
72017
+ if (err19?.message?.toLowerCase()?.includes("encountered")) {
71673
72018
  this["~standard"].async = true;
71674
72019
  }
71675
72020
  ctx.common = {
@@ -74989,10 +75334,10 @@ function loadConfig() {
74989
75334
  const messages = result.error.issues.map(
74990
75335
  (issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
74991
75336
  );
74992
- return (0, import_neverthrow44.err)({ type: "INVALID_CONFIG", message: `Configuration error:
75337
+ return (0, import_neverthrow45.err)({ type: "INVALID_CONFIG", message: `Configuration error:
74993
75338
  ${messages.join("\n")}` });
74994
75339
  }
74995
- return (0, import_neverthrow44.ok)(result.data);
75340
+ return (0, import_neverthrow45.ok)(result.data);
74996
75341
  }
74997
75342
 
74998
75343
  // src/core/parse-timeout-seconds.ts
@@ -75025,7 +75370,7 @@ function parseVerboseOption(value) {
75025
75370
 
75026
75371
  // src/pid-lock.ts
75027
75372
  var import_node_fs9 = require("node:fs");
75028
- var import_neverthrow45 = __toESM(require_index_cjs(), 1);
75373
+ var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75029
75374
  var PID_FILE = "/tmp/xqa.pid";
75030
75375
  var SIGINT_EXIT_CODE = 130;
75031
75376
  var SIGTERM_EXIT_CODE = 143;
@@ -75034,7 +75379,7 @@ var HARD_TIMEOUT_MS = 1e4;
75034
75379
  var cleanup = () => {
75035
75380
  (0, import_node_fs9.rmSync)(PID_FILE, { force: true });
75036
75381
  };
75037
- var checkProcessRunning = (0, import_neverthrow45.fromThrowable)(
75382
+ var checkProcessRunning = (0, import_neverthrow46.fromThrowable)(
75038
75383
  (pid) => {
75039
75384
  process.kill(pid, 0);
75040
75385
  return true;
@@ -75100,27 +75445,27 @@ function acquireLock() {
75100
75445
  // src/shell/xqa-directory.ts
75101
75446
  var import_node_fs10 = require("node:fs");
75102
75447
  var import_node_path19 = __toESM(require("node:path"), 1);
75103
- var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75448
+ var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75104
75449
  function findXqaDirectory(startDirectory) {
75105
75450
  let current = startDirectory;
75106
75451
  for (; ; ) {
75107
75452
  const candidate = import_node_path19.default.join(current, ".xqa");
75108
75453
  if ((0, import_node_fs10.existsSync)(candidate)) {
75109
- return (0, import_neverthrow46.ok)(candidate);
75454
+ return (0, import_neverthrow47.ok)(candidate);
75110
75455
  }
75111
75456
  const parent = import_node_path19.default.dirname(current);
75112
75457
  if (parent === current) {
75113
- return (0, import_neverthrow46.err)({ type: "XQA_NOT_INITIALIZED" });
75458
+ return (0, import_neverthrow47.err)({ type: "XQA_NOT_INITIALIZED" });
75114
75459
  }
75115
75460
  current = parent;
75116
75461
  }
75117
75462
  }
75118
75463
 
75119
75464
  // src/suite/commands/run-command.ts
75120
- var import_promises19 = __toESM(require("node:fs/promises"), 1);
75465
+ var import_promises20 = __toESM(require("node:fs/promises"), 1);
75121
75466
  var import_node_path22 = __toESM(require("node:path"), 1);
75122
75467
  var import_fast_glob = __toESM(require_out4(), 1);
75123
- var import_neverthrow51 = __toESM(require_index_cjs(), 1);
75468
+ var import_neverthrow52 = __toESM(require_index_cjs(), 1);
75124
75469
 
75125
75470
  // src/suite/core/run-id.ts
75126
75471
  var RUN_ID_PAD_LENGTH2 = 4;
@@ -75143,8 +75488,8 @@ function computeNextRunId(existingDirectories) {
75143
75488
  }
75144
75489
 
75145
75490
  // src/suite/core/suite-config-parser.ts
75146
- var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75147
75491
  var import_neverthrow48 = __toESM(require_index_cjs(), 1);
75492
+ var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75148
75493
  var freestyleEntrySchema = external_exports2.object({
75149
75494
  prompt: external_exports2.string().optional(),
75150
75495
  timeoutSeconds: external_exports2.number().int().positive()
@@ -75163,7 +75508,7 @@ var suiteConfigSchema = external_exports2.object({
75163
75508
  specs: external_exports2.array(external_exports2.string()).nonempty(),
75164
75509
  freestyle: freestyleSchema
75165
75510
  });
75166
- var safeJsonParse4 = (0, import_neverthrow47.fromThrowable)(
75511
+ var safeJsonParse4 = (0, import_neverthrow48.fromThrowable)(
75167
75512
  JSON.parse,
75168
75513
  (cause) => ({
75169
75514
  type: "INVALID_SUITE_CONFIG",
@@ -75174,9 +75519,9 @@ function parseSuiteConfig(raw) {
75174
75519
  return safeJsonParse4(raw).andThen((data) => {
75175
75520
  const parsed = suiteConfigSchema.safeParse(data);
75176
75521
  if (!parsed.success) {
75177
- return (0, import_neverthrow48.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75522
+ return (0, import_neverthrow49.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75178
75523
  }
75179
- return (0, import_neverthrow48.ok)({ specs: parsed.data.specs, freestyle: parsed.data.freestyle });
75524
+ return (0, import_neverthrow49.ok)({ specs: parsed.data.specs, freestyle: parsed.data.freestyle });
75180
75525
  });
75181
75526
  }
75182
75527
 
@@ -75240,9 +75585,9 @@ function buildFreestyleItems(entries) {
75240
75585
  }
75241
75586
 
75242
75587
  // src/suite/shell/suite-findings-writer.ts
75243
- var import_promises18 = __toESM(require("node:fs/promises"), 1);
75588
+ var import_promises19 = __toESM(require("node:fs/promises"), 1);
75244
75589
  var import_node_path20 = __toESM(require("node:path"), 1);
75245
- var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75590
+ var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75246
75591
  var INDENT_SPACES = 2;
75247
75592
  function writeSuiteFindings(findings, options) {
75248
75593
  const directory = import_node_path20.default.join(
@@ -75254,11 +75599,11 @@ function writeSuiteFindings(findings, options) {
75254
75599
  );
75255
75600
  const finalPath = import_node_path20.default.join(directory, "findings.json");
75256
75601
  const temporaryPath = `${finalPath}.tmp`;
75257
- const safeWriteAtomically = import_neverthrow49.ResultAsync.fromThrowable(
75602
+ const safeWriteAtomically = import_neverthrow50.ResultAsync.fromThrowable(
75258
75603
  async () => {
75259
- await import_promises18.default.mkdir(directory, { recursive: true });
75260
- await import_promises18.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
75261
- await import_promises18.default.rename(temporaryPath, finalPath);
75604
+ await import_promises19.default.mkdir(directory, { recursive: true });
75605
+ await import_promises19.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
75606
+ await import_promises19.default.rename(temporaryPath, finalPath);
75262
75607
  return finalPath;
75263
75608
  },
75264
75609
  (cause) => ({ type: "FINDINGS_WRITE_FAILED", cause })
@@ -75267,7 +75612,7 @@ function writeSuiteFindings(findings, options) {
75267
75612
  }
75268
75613
 
75269
75614
  // src/suite/shell/worker-pool.ts
75270
- var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75615
+ var import_neverthrow51 = __toESM(require_index_cjs(), 1);
75271
75616
 
75272
75617
  // src/suite/shell/item-result-builder.ts
75273
75618
  function getErrorMessage(cause) {
@@ -75495,7 +75840,7 @@ function runWorkerPool(config3) {
75495
75840
  const queue = setupQueue(config3);
75496
75841
  const results = [];
75497
75842
  const suiteStartMs = Date.now();
75498
- const safeRun = import_neverthrow50.ResultAsync.fromThrowable(
75843
+ const safeRun = import_neverthrow51.ResultAsync.fromThrowable(
75499
75844
  async () => runAllWorkers({ config: config3, queue, results, suiteStartMs }),
75500
75845
  (cause) => ({ type: "WORKER_POOL_FAILED", cause })
75501
75846
  );
@@ -75535,7 +75880,7 @@ function buildFreestylePipelineConfig(input) {
75535
75880
  date: date5,
75536
75881
  mcpServers: createDefaultMcpServers(simulatorUdid),
75537
75882
  allowedTools: ALLOWED_TOOLS,
75538
- timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND3,
75883
+ timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
75539
75884
  appContext: item.prompt ? `${buildDeviceInstruction(simulatorUdid)}
75540
75885
 
75541
75886
  ${item.prompt}` : buildDeviceInstruction(simulatorUdid),
@@ -75582,15 +75927,15 @@ function makeExecuteItem(context) {
75582
75927
 
75583
75928
  // src/suite/commands/run-command.ts
75584
75929
  var ISO_DATE_LENGTH3 = 10;
75585
- var safeReaddir2 = import_neverthrow51.ResultAsync.fromThrowable(
75930
+ var safeReaddir2 = import_neverthrow52.ResultAsync.fromThrowable(
75586
75931
  async (directoryPath) => {
75587
- const entries = await import_promises19.default.readdir(directoryPath, { withFileTypes: true });
75932
+ const entries = await import_promises20.default.readdir(directoryPath, { withFileTypes: true });
75588
75933
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
75589
75934
  },
75590
75935
  () => "READDIR_FAILED"
75591
75936
  );
75592
- var safeReadFile5 = import_neverthrow51.ResultAsync.fromThrowable(
75593
- async (filePath) => import_promises19.default.readFile(filePath, "utf8"),
75937
+ var safeReadFile5 = import_neverthrow52.ResultAsync.fromThrowable(
75938
+ async (filePath) => import_promises20.default.readFile(filePath, "utf8"),
75594
75939
  () => "READ_FAILED"
75595
75940
  );
75596
75941
  async function listRunDirectories(input) {
@@ -75662,17 +76007,21 @@ async function discoverSimulators() {
75662
76007
  return { simulators: result.value, exitCode: 0 };
75663
76008
  }
75664
76009
  async function runPool(input) {
75665
- const { context, items, simulatorUdids, signal } = input;
76010
+ const { context, items, simulatorUdids, debug, signal } = input;
75666
76011
  const display = createSuiteDisplay();
76012
+ const debugLogger = createDebugLogger({ enabled: debug });
76013
+ const baseObserver = (event) => {
76014
+ display.onEvent(event);
76015
+ };
76016
+ const observer = debugLogger.wrapSuiteHandler(baseObserver);
76017
+ const buildItemHandler = (itemId) => debugLogger.wrapAgentHandler((event) => {
76018
+ display.onEvent({ ...event, itemId });
76019
+ }, itemId);
75667
76020
  const poolResult = await runWorkerPool({
75668
76021
  items,
75669
76022
  simulatorUdids,
75670
- observer: (event) => {
75671
- display.onEvent(event);
75672
- },
75673
- onEvent: (itemId) => (event) => {
75674
- display.onEvent({ ...event, itemId });
75675
- },
76023
+ observer,
76024
+ onEvent: buildItemHandler,
75676
76025
  executeItem: makeExecuteItem(context),
75677
76026
  signal
75678
76027
  });
@@ -75710,22 +76059,31 @@ async function buildSuiteRunContext(input) {
75710
76059
  };
75711
76060
  return { suiteId, date: date5, outputDirectory, runId, context };
75712
76061
  }
76062
+ async function executePool(input, runContext) {
76063
+ const { items, simulatorUdids, debug, signal } = input;
76064
+ return runPool({
76065
+ context: runContext.context,
76066
+ items,
76067
+ simulatorUdids,
76068
+ debug,
76069
+ signal
76070
+ });
76071
+ }
75713
76072
  async function runSuite(input) {
75714
- const { items, simulatorUdids, signal } = input;
75715
- const { suiteId, date: date5, outputDirectory, runId, context } = await buildSuiteRunContext(input);
75716
- const poolResult = await runPool({ context, items, simulatorUdids, signal });
76073
+ const runContext = await buildSuiteRunContext(input);
76074
+ const poolResult = await executePool(input, runContext);
75717
76075
  if (poolResult.isErr()) {
75718
76076
  process.stderr.write(`Suite runner failed: ${String(poolResult.error.cause)}
75719
76077
  `);
75720
76078
  return 1;
75721
76079
  }
75722
76080
  return writeAndReport({
75723
- suiteId,
75724
- runId,
75725
- outputDirectory,
75726
- date: date5,
76081
+ suiteId: runContext.suiteId,
76082
+ runId: runContext.runId,
76083
+ outputDirectory: runContext.outputDirectory,
76084
+ date: runContext.date,
75727
76085
  items: poolResult.value,
75728
- aborted: signal.aborted
76086
+ aborted: input.signal.aborted
75729
76087
  });
75730
76088
  }
75731
76089
  async function runSuiteCommand(input) {
@@ -75771,7 +76129,7 @@ function resolveXqaDirectory() {
75771
76129
  return result.value;
75772
76130
  }
75773
76131
  var program2 = new Command();
75774
- program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.9.0"}${false ? ` (dev build +${"0938687"})` : ""}`);
76132
+ program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.10.0"}${false ? ` (dev build +${"e25bb70"})` : ""}`);
75775
76133
  program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
75776
76134
  runInitCommand();
75777
76135
  });
@@ -75786,20 +76144,23 @@ program2.command("explore").description("Run the explorer agent; omit prompt for
75786
76144
  "-t, --timeout <seconds>",
75787
76145
  "Explorer timeout in seconds (overrides QA_EXPLORE_TIMEOUT_SECONDS)",
75788
76146
  parseTimeoutSeconds
75789
- ).action((prompt, options) => {
75790
- const xqaDirectory = resolveXqaDirectory();
75791
- runExploreCommand(
75792
- {
75793
- prompt,
75794
- verbose: options.verbose,
75795
- timeoutSeconds: options.timeout,
75796
- signal: controller.signal
75797
- },
75798
- { config: config2, xqaDirectory }
75799
- );
75800
- });
75801
- program2.command("analyse").description("Analyse a session recording with Gemini").argument("[video-path]", "Path to video file").action((videoPath) => {
75802
- void runAnalyseCommand(videoPath, config2);
76147
+ ).option("--debug", "Log timing and event details to stderr").action(
76148
+ (prompt, options) => {
76149
+ const xqaDirectory = resolveXqaDirectory();
76150
+ runExploreCommand(
76151
+ {
76152
+ prompt,
76153
+ verbose: options.verbose,
76154
+ timeoutSeconds: options.timeout,
76155
+ debug: options.debug ?? false,
76156
+ signal: controller.signal
76157
+ },
76158
+ { config: config2, xqaDirectory }
76159
+ );
76160
+ }
76161
+ );
76162
+ program2.command("analyse").description("Analyse a session recording with Gemini").argument("[video-path]", "Path to video file").option("--debug", "Log timing and event details to stderr").action((videoPath, options) => {
76163
+ void runAnalyseCommand({ videoPath, config: config2, debug: options.debug ?? false });
75803
76164
  });
75804
76165
  program2.command("completion").description("Output shell completion script").argument("<shell>", "Shell to generate completion for (bash, zsh)").action((shell) => {
75805
76166
  runCompletionCommand(program2, shell);
@@ -75812,14 +76173,19 @@ program2.command("spec").description("Run the explorer agent against a spec file
75812
76173
  "-v, --verbose [categories]",
75813
76174
  "Verbose output [prompt,tools,screen,memory] (default: all)",
75814
76175
  parseVerboseOption
75815
- ).action((specFile, options) => {
76176
+ ).option("--debug", "Log timing and event details to stderr").action((specFile, options) => {
75816
76177
  const xqaDirectory = resolveXqaDirectory();
75817
76178
  void runSpecCommand(
75818
- { specFile, verbose: options.verbose, signal: controller.signal },
76179
+ {
76180
+ specFile,
76181
+ verbose: options.verbose,
76182
+ debug: options.debug ?? false,
76183
+ signal: controller.signal
76184
+ },
75819
76185
  { config: config2, xqaDirectory }
75820
76186
  );
75821
76187
  });
75822
- program2.command("run").description("Run a test suite or a set of spec files").option("--suite <name>", "Name of the suite to run").option("--spec <globs...>", "Glob patterns matching spec files to run").action(async (options) => {
76188
+ program2.command("run").description("Run a test suite or a set of spec files").option("--suite <name>", "Name of the suite to run").option("--spec <globs...>", "Glob patterns matching spec files to run").option("--debug", "Log timing and event details to stderr").action(async (options) => {
75823
76189
  if (options.suite === void 0 === (options.spec === void 0)) {
75824
76190
  process.stderr.write("Exactly one of --suite or --spec must be provided\n");
75825
76191
  process.exit(1);
@@ -75830,6 +76196,7 @@ program2.command("run").description("Run a test suite or a set of spec files").o
75830
76196
  mode,
75831
76197
  xqaDirectory,
75832
76198
  config: config2,
76199
+ debug: options.debug ?? false,
75833
76200
  signal: controller.signal
75834
76201
  });
75835
76202
  process.exit(exitCode);