@exodus/xqa 1.14.2 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +0 -1
  2. package/dist/xqa.cjs +109 -172
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -301,7 +301,6 @@ src/
301
301
  app-context.ts # Read app.md and explore.md
302
302
  xqa-directory.ts # Locate .xqa directory
303
303
  preflight.ts # Environment preflight checks
304
- ffmpeg-check.ts # ffmpeg availability probe
305
304
  display-factory.ts # Solo and suite display factories
306
305
  debug-logger.ts # Debug event logging
307
306
  debug-agent-events.ts # Agent event debug formatter
package/dist/xqa.cjs CHANGED
@@ -3733,7 +3733,7 @@ var require_index_cjs = __commonJS({
3733
3733
  }
3734
3734
  var fromPromise = ResultAsync14.fromPromise;
3735
3735
  var fromSafePromise2 = ResultAsync14.fromSafePromise;
3736
- var fromAsyncThrowable10 = ResultAsync14.fromThrowable;
3736
+ var fromAsyncThrowable9 = ResultAsync14.fromThrowable;
3737
3737
  var combineResultList = (resultList) => {
3738
3738
  let acc = ok21([]);
3739
3739
  for (const result of resultList) {
@@ -3950,7 +3950,7 @@ var require_index_cjs = __commonJS({
3950
3950
  exports2.ResultAsync = ResultAsync14;
3951
3951
  exports2.err = err20;
3952
3952
  exports2.errAsync = errAsync8;
3953
- exports2.fromAsyncThrowable = fromAsyncThrowable10;
3953
+ exports2.fromAsyncThrowable = fromAsyncThrowable9;
3954
3954
  exports2.fromPromise = fromPromise;
3955
3955
  exports2.fromSafePromise = fromSafePromise2;
3956
3956
  exports2.fromThrowable = fromThrowable15;
@@ -52863,8 +52863,6 @@ function resolveRunPaths({ outputDirectory, runId, date: date5 }) {
52863
52863
  return (0, import_neverthrow8.ok)({
52864
52864
  baseDir: baseDirectory,
52865
52865
  videoPath: import_node_path2.default.join(baseDirectory, "recording.mp4"),
52866
- videoPath2x: import_node_path2.default.join(baseDirectory, "recording_2x.mp4"),
52867
- videoPath4x: import_node_path2.default.join(baseDirectory, "recording_4x.mp4"),
52868
52866
  findingsPath: import_node_path2.default.join(baseDirectory, "findings.json"),
52869
52867
  screenshotsDir: import_node_path2.default.join(baseDirectory, "screenshots")
52870
52868
  });
@@ -54301,11 +54299,27 @@ function isTerminal(item) {
54301
54299
  }
54302
54300
  return TERMINAL_STATUSES.has(item.status);
54303
54301
  }
54302
+ function isRunning(item) {
54303
+ return item?.status === "running";
54304
+ }
54304
54305
  function transitionedIds(state, previous) {
54305
54306
  return state.itemOrder.filter((id) => {
54306
54307
  return isTerminal(state.items.get(id)) && !isTerminal(previous.items.get(id));
54307
54308
  });
54308
54309
  }
54310
+ function startedIds(state, previous) {
54311
+ return state.itemOrder.filter((id) => {
54312
+ return isRunning(state.items.get(id)) && !isRunning(previous.items.get(id));
54313
+ });
54314
+ }
54315
+ function renderStarted(itemId, state) {
54316
+ const item = state.items.get(itemId);
54317
+ if (item === void 0) {
54318
+ return "";
54319
+ }
54320
+ return `[xqa] started: ${item.name}
54321
+ `;
54322
+ }
54309
54323
  function renderAnnotationLevel(level) {
54310
54324
  switch (level) {
54311
54325
  case "warning": {
@@ -54373,12 +54387,16 @@ Total: ${String(total)}, Failed: ${String(failed)}
54373
54387
  `;
54374
54388
  }
54375
54389
  function githubCiDecorator({ state, previous, filter }) {
54390
+ const started = startedIds(state, previous);
54376
54391
  const ids = transitionedIds(state, previous);
54377
54392
  const suiteJustEnded = state.suiteEnded && !previous.suiteEnded;
54378
- if (ids.length === 0 && !suiteJustEnded) {
54393
+ if (started.length === 0 && ids.length === 0 && !suiteJustEnded) {
54379
54394
  return "";
54380
54395
  }
54381
54396
  let output = "";
54397
+ for (const id of started) {
54398
+ output += renderStarted(id, state);
54399
+ }
54382
54400
  for (const id of ids) {
54383
54401
  output += renderGroup({ itemId: id, state, filter });
54384
54402
  }
@@ -56123,24 +56141,13 @@ function makePollTick(onEvent) {
56123
56141
  });
56124
56142
  };
56125
56143
  }
56126
- function resolveVideoPath(artifacts, variant) {
56127
- if (variant === "2x") {
56128
- return artifacts.videoPath2x;
56129
- }
56130
- if (variant === "4x") {
56131
- return artifacts.videoPath4x;
56132
- }
56133
- return artifacts.videoPath;
56134
- }
56135
56144
  function buildRunContext(artifacts, config3) {
56136
- const variant = config3?.videoVariant ?? "4x";
56137
56145
  return {
56138
56146
  onEvent: config3?.onEvent === void 0 ? void 0 : safeHandler(config3.onEvent),
56139
56147
  apiKey: config3?.apiKey ?? "",
56140
56148
  model: config3?.model ?? DEFAULT_MODEL,
56141
- variant,
56142
56149
  start: Date.now(),
56143
- videoPath: resolveVideoPath(artifacts, variant),
56150
+ videoPath: artifacts.videoPath,
56144
56151
  prompt: buildAnalyserPrompt(artifacts.snapshots)
56145
56152
  };
56146
56153
  }
@@ -56170,11 +56177,11 @@ function runUploadPipeline(context) {
56170
56177
  }
56171
56178
  function runAnalyser(artifacts, config3) {
56172
56179
  const context = buildRunContext(artifacts, config3);
56173
- const { onEvent, variant, start, videoPath } = context;
56180
+ const { onEvent, start, videoPath } = context;
56174
56181
  onEvent?.({ type: "STAGE_START", agent: "analyser" });
56175
56182
  if (!videoPath) {
56176
56183
  emitStageEnd(onEvent, start);
56177
- return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING", variant });
56184
+ return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING" });
56178
56185
  }
56179
56186
  return runUploadPipeline(context);
56180
56187
  }
@@ -62798,17 +62805,6 @@ function runQuery(prompt, config3) {
62798
62805
  function collectAgentOutput(prompt, config3) {
62799
62806
  return runQuery(prompt, config3).mapErr((cause) => ({ type: "QUERY_FAILED", cause }));
62800
62807
  }
62801
- async function runFfmpeg(arguments_) {
62802
- const { promise: promise2, resolve, reject } = Promise.withResolvers();
62803
- (0, import_node_child_process4.execFile)("ffmpeg", arguments_, (error48) => {
62804
- if (error48) {
62805
- reject(error48);
62806
- } else {
62807
- resolve(true);
62808
- }
62809
- });
62810
- await promise2;
62811
- }
62812
62808
  function spawnRecorder(outputPath) {
62813
62809
  const safeMkdirSync = (0, import_neverthrow20.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
62814
62810
  const safeSpawn2 = (0, import_neverthrow20.fromThrowable)(
@@ -62882,35 +62878,6 @@ function stopRecording(handle) {
62882
62878
  (cause) => ({ type: "STOP_FAILED", cause })
62883
62879
  );
62884
62880
  }
62885
- function speedUpVideo({
62886
- inputPath,
62887
- factor,
62888
- outputPath
62889
- }) {
62890
- const pts = String(1 / factor);
62891
- return (0, import_neverthrow20.fromAsyncThrowable)(
62892
- runFfmpeg,
62893
- (cause) => ({ type: "PROCESS_FAILED", cause })
62894
- )(["-i", inputPath, "-filter:v", `setpts=${pts}*PTS`, "-an", "-y", outputPath]).map(
62895
- () => outputPath
62896
- );
62897
- }
62898
- var SPEED_2X = 2;
62899
- var SPEED_4X = 4;
62900
- function applySpeedUpVariants({
62901
- result,
62902
- params,
62903
- toRecordingError
62904
- }) {
62905
- const { findings, snapshots } = result;
62906
- const { videoPath, videoPath2x, videoPath4x, signal } = params;
62907
- if (signal?.aborted) {
62908
- return (0, import_neverthrow19.okAsync)({ findings, snapshots });
62909
- }
62910
- return speedUpVideo({ inputPath: videoPath, factor: SPEED_2X, outputPath: videoPath2x }).mapErr(toRecordingError).andThen(
62911
- () => speedUpVideo({ inputPath: videoPath, factor: SPEED_4X, outputPath: videoPath4x }).mapErr(toRecordingError).map(() => ({ findings, snapshots }))
62912
- );
62913
- }
62914
62881
  function runWithRecording(handle, collectOutput) {
62915
62882
  const toRecordingError = (cause) => ({
62916
62883
  type: "RECORDING_FAILED",
@@ -62939,7 +62906,7 @@ function startAndRun(params) {
62939
62906
  }).mapErr((error48) => {
62940
62907
  signal?.removeEventListener("abort", onAbort);
62941
62908
  return error48;
62942
- }).andThen((result) => applySpeedUpVariants({ result, params, toRecordingError }));
62909
+ });
62943
62910
  });
62944
62911
  }
62945
62912
  var TOOL_SELECTION_SECTION = `## Tool Selection
@@ -63413,15 +63380,13 @@ function toArtifacts(result, runPaths) {
63413
63380
  return {
63414
63381
  findings: result.findings,
63415
63382
  videoPath: runPaths.videoPath,
63416
- videoPath2x: runPaths.videoPath2x,
63417
- videoPath4x: runPaths.videoPath4x,
63418
63383
  snapshots: result.snapshots
63419
63384
  };
63420
63385
  }
63421
63386
  function emitStageEnd3(safeConfig, start) {
63422
63387
  safeConfig.onEvent?.({ type: "STAGE_END", agent: "explorer", durationMs: Date.now() - start });
63423
63388
  }
63424
- var EMPTY_RUN_PATHS = { videoPath: "", videoPath2x: "", videoPath4x: "" };
63389
+ var EMPTY_RUN_PATHS = { videoPath: "" };
63425
63390
  function collectWithoutRecording({
63426
63391
  safeConfig,
63427
63392
  prompt,
@@ -63446,8 +63411,6 @@ function collectAndFinalize({
63446
63411
  }
63447
63412
  return startAndRun({
63448
63413
  videoPath: runPaths.videoPath,
63449
- videoPath2x: runPaths.videoPath2x,
63450
- videoPath4x: runPaths.videoPath4x,
63451
63414
  signal: safeConfig.signal,
63452
63415
  collectOutput: () => collectAgentOutput(prompt, safeConfig)
63453
63416
  }).map((result) => {
@@ -67441,7 +67404,6 @@ var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
67441
67404
  var MS_PER_SECOND4 = 1e3;
67442
67405
  var DEFAULT_ABORT_EXIT_CODE = 130;
67443
67406
  var IDB_INSTALL_MESSAGE = "idb is not installed. Run:\n brew install idb-companion\n pipx install fb-idb\n";
67444
- var FFMPEG_INSTALL_MESSAGE = "ffmpeg is not installed. Run:\n brew install ffmpeg\n";
67445
67407
 
67446
67408
  // src/shell/debug-logger-core.ts
67447
67409
  var ELAPSED_PAD = 8;
@@ -67723,7 +67685,7 @@ var JSON_INDENT = 2;
67723
67685
  var ITEM_ID = "analyse";
67724
67686
  var ITEM_NAME = "analyse";
67725
67687
  function buildArtifacts(videoPath) {
67726
- return { videoPath, videoPath2x: "", videoPath4x: videoPath, findings: [], snapshots: [] };
67688
+ return { videoPath, findings: [], snapshots: [] };
67727
67689
  }
67728
67690
  async function checkVideoPathExists(videoPath) {
67729
67691
  const safeAccess = (0, import_neverthrow36.fromAsyncThrowable)(import_promises17.access, () => ({ type: "FILE_NOT_FOUND" }));
@@ -67770,7 +67732,6 @@ async function runAnalysisAndExit(input) {
67770
67732
  const state = { identity, startedAt };
67771
67733
  const result = await runAnalysis(input.artifacts, {
67772
67734
  apiKey: input.apiKey,
67773
- videoVariant: "1x",
67774
67735
  onEvent
67775
67736
  });
67776
67737
  result.match(
@@ -67791,7 +67752,7 @@ function ensureApiKey(config3) {
67791
67752
  }
67792
67753
  return apiKey;
67793
67754
  }
67794
- async function resolveVideoPath2(videoPath) {
67755
+ async function resolveVideoPath(videoPath) {
67795
67756
  if (videoPath === void 0) {
67796
67757
  process.stderr.write('A video path is required. Pass the path printed by "xqa explore".\n');
67797
67758
  process.exit(1);
@@ -67810,7 +67771,7 @@ async function runAnalyseCommand(input) {
67810
67771
  if (apiKey === void 0) {
67811
67772
  return;
67812
67773
  }
67813
- const videoPath = await resolveVideoPath2(input.videoPath);
67774
+ const videoPath = await resolveVideoPath(input.videoPath);
67814
67775
  if (videoPath === void 0) {
67815
67776
  return;
67816
67777
  }
@@ -68009,35 +67970,8 @@ function readExploreContext(xqaDirectory) {
68009
67970
  return readContextFile(xqaDirectory, "explore.md");
68010
67971
  }
68011
67972
 
68012
- // src/shell/ffmpeg-check.ts
68013
- var import_node_child_process5 = require("node:child_process");
68014
- var import_neverthrow40 = __toESM(require_index_cjs(), 1);
68015
- async function whichFfmpeg() {
68016
- const { promise: promise2, resolve, reject } = Promise.withResolvers();
68017
- (0, import_node_child_process5.execFile)("which", ["ffmpeg"], (error48, stdout) => {
68018
- if (error48) {
68019
- reject(error48);
68020
- } else {
68021
- resolve(stdout);
68022
- }
68023
- });
68024
- return promise2;
68025
- }
68026
- var safeWhichFfmpeg = (0, import_neverthrow40.fromAsyncThrowable)(
68027
- whichFfmpeg,
68028
- () => ({ type: "FFMPEG_NOT_FOUND" })
68029
- );
68030
- function checkFfmpegAvailable() {
68031
- return safeWhichFfmpeg();
68032
- }
68033
-
68034
67973
  // src/shell/preflight.ts
68035
67974
  async function runPreflightChecks() {
68036
- const ffmpegCheck = await checkFfmpegAvailable();
68037
- if (ffmpegCheck.isErr()) {
68038
- process.stderr.write(FFMPEG_INSTALL_MESSAGE);
68039
- return 1;
68040
- }
68041
67975
  const idbCheck = await checkIdbAvailable();
68042
67976
  if (idbCheck.isErr()) {
68043
67977
  process.stderr.write(IDB_INSTALL_MESSAGE);
@@ -68225,7 +68159,7 @@ var import_node_fs6 = require("node:fs");
68225
68159
  var import_node_path12 = __toESM(require("node:path"), 1);
68226
68160
 
68227
68161
  // src/commands/install-skills.ts
68228
- var import_node_child_process6 = require("node:child_process");
68162
+ var import_node_child_process5 = require("node:child_process");
68229
68163
  var import_node_fs5 = require("node:fs");
68230
68164
  var import_node_path11 = __toESM(require("node:path"), 1);
68231
68165
  var import_node_url = require("node:url");
@@ -68236,7 +68170,7 @@ function resolveSkillsRoot() {
68236
68170
  function installSkills() {
68237
68171
  const skillsRoot = resolveSkillsRoot();
68238
68172
  for (const skill of (0, import_node_fs5.readdirSync)(skillsRoot)) {
68239
- (0, import_node_child_process6.spawnSync)("npx", ["skills", "add", import_node_path11.default.join(skillsRoot, skill), "-y"], {
68173
+ (0, import_node_child_process5.spawnSync)("npx", ["skills", "add", import_node_path11.default.join(skillsRoot, skill), "-y"], {
68240
68174
  stdio: "inherit"
68241
68175
  });
68242
68176
  }
@@ -68314,7 +68248,7 @@ function runInitCommand() {
68314
68248
  // src/commands/review-command.ts
68315
68249
  var import_node_fs7 = require("node:fs");
68316
68250
  var import_node_path14 = __toESM(require("node:path"), 1);
68317
- var import_neverthrow42 = __toESM(require_index_cjs(), 1);
68251
+ var import_neverthrow41 = __toESM(require_index_cjs(), 1);
68318
68252
 
68319
68253
  // ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
68320
68254
  var isUpKey = (key, keybindings = []) => (
@@ -70719,7 +70653,7 @@ var esm_default11 = createPrompt((config3, done) => {
70719
70653
  });
70720
70654
 
70721
70655
  // src/review-session.ts
70722
- var import_neverthrow41 = __toESM(require_index_cjs(), 1);
70656
+ var import_neverthrow40 = __toESM(require_index_cjs(), 1);
70723
70657
  var CONFIDENCE_PERCENT = 100;
70724
70658
  var FLOW_COL_WIDTH = 35;
70725
70659
  var TRIGGER_COL_WIDTH = 16;
@@ -70872,15 +70806,15 @@ async function runInteractiveLoop(findings, existing) {
70872
70806
  }
70873
70807
  return { staged: state.staged, undoneKeys: state.undoneKeys };
70874
70808
  }
70875
- var safeRunInteractiveLoop = (0, import_neverthrow41.fromAsyncThrowable)(
70809
+ var safeRunInteractiveLoop = (0, import_neverthrow40.fromAsyncThrowable)(
70876
70810
  runInteractiveLoop,
70877
70811
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
70878
70812
  );
70879
70813
 
70880
70814
  // src/commands/review-command.ts
70881
- var safeReadFile3 = (0, import_neverthrow42.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
70882
- var safeParseJson3 = (0, import_neverthrow42.fromThrowable)(JSON.parse);
70883
- var safeWrite = (0, import_neverthrow42.fromThrowable)((filePath, content) => {
70815
+ var safeReadFile3 = (0, import_neverthrow41.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
70816
+ var safeParseJson3 = (0, import_neverthrow41.fromThrowable)(JSON.parse);
70817
+ var safeWrite = (0, import_neverthrow41.fromThrowable)((filePath, content) => {
70884
70818
  (0, import_node_fs7.writeFileSync)(filePath, content);
70885
70819
  });
70886
70820
  function readLastPath(xqaDirectory) {
@@ -70897,13 +70831,13 @@ function isPipelineOutput(data) {
70897
70831
  function readFindings(filePath) {
70898
70832
  const readResult = safeReadFile3(filePath);
70899
70833
  if (readResult.isErr()) {
70900
- return (0, import_neverthrow42.err)("not-found");
70834
+ return (0, import_neverthrow41.err)("not-found");
70901
70835
  }
70902
70836
  return safeParseJson3(readResult.value).mapErr(() => "invalid").andThen((data) => {
70903
70837
  if (!isPipelineOutput(data)) {
70904
- return (0, import_neverthrow42.err)("invalid");
70838
+ return (0, import_neverthrow41.err)("invalid");
70905
70839
  }
70906
- return (0, import_neverthrow42.ok)(data);
70840
+ return (0, import_neverthrow41.ok)(data);
70907
70841
  });
70908
70842
  }
70909
70843
  function loadExistingDismissals(filePath) {
@@ -70976,7 +70910,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70976
70910
  "No findings path provided and no last path found. Run: xqa review <findings-path>\n"
70977
70911
  );
70978
70912
  process.exit(1);
70979
- return (0, import_neverthrow42.err)();
70913
+ return (0, import_neverthrow41.err)();
70980
70914
  }
70981
70915
  const resolvedPath = resolvedPathResult.value;
70982
70916
  const findingsResult = readFindings(resolvedPath);
@@ -70989,9 +70923,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70989
70923
  `);
70990
70924
  }
70991
70925
  process.exit(1);
70992
- return (0, import_neverthrow42.err)();
70926
+ return (0, import_neverthrow41.err)();
70993
70927
  }
70994
- return (0, import_neverthrow42.ok)({ resolvedPath, output: findingsResult.value });
70928
+ return (0, import_neverthrow41.ok)({ resolvedPath, output: findingsResult.value });
70995
70929
  }
70996
70930
  async function runReviewLoop({
70997
70931
  findings,
@@ -71065,40 +70999,40 @@ function stripExtensions(filename) {
71065
70999
  // src/commands/spec-resolver.ts
71066
71000
  var import_node_fs8 = require("node:fs");
71067
71001
  var import_node_path16 = __toESM(require("node:path"), 1);
71068
- var import_neverthrow44 = __toESM(require_index_cjs(), 1);
71002
+ var import_neverthrow43 = __toESM(require_index_cjs(), 1);
71069
71003
 
71070
71004
  // src/spec-frontmatter.ts
71071
- var import_neverthrow43 = __toESM(require_index_cjs(), 1);
71005
+ var import_neverthrow42 = __toESM(require_index_cjs(), 1);
71072
71006
  var FRONTMATTER_OPEN_LEN = 4;
71073
71007
  var FRONTMATTER_MARKER_LEN = 3;
71074
71008
  function extractFrontmatterBlock(content) {
71075
71009
  const normalized = content.replaceAll("\r\n", "\n");
71076
71010
  if (!normalized.startsWith("---")) {
71077
- return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
71011
+ return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71078
71012
  }
71079
71013
  const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
71080
71014
  if (end === -1) {
71081
- return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
71015
+ return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71082
71016
  }
71083
- return (0, import_neverthrow43.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
71017
+ return (0, import_neverthrow42.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
71084
71018
  }
71085
71019
  function parseTimeout(fields) {
71086
71020
  const raw = fields.get("timeout");
71087
71021
  if (raw === void 0) {
71088
- return (0, import_neverthrow43.ok)(raw);
71022
+ return (0, import_neverthrow42.ok)(raw);
71089
71023
  }
71090
71024
  const parsed = Number(raw);
71091
71025
  if (Number.isNaN(parsed) || parsed <= 0) {
71092
- return (0, import_neverthrow43.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
71026
+ return (0, import_neverthrow42.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
71093
71027
  }
71094
- return (0, import_neverthrow43.ok)(parsed);
71028
+ return (0, import_neverthrow42.ok)(parsed);
71095
71029
  }
71096
71030
  function parseSpecFrontmatter(content) {
71097
71031
  return extractFrontmatterBlock(content).andThen((block) => {
71098
71032
  const fields = parseYamlFields(block);
71099
71033
  const feature = fields.get("feature");
71100
71034
  if (feature === void 0) {
71101
- return (0, import_neverthrow43.err)({ type: "MISSING_FIELD", field: "feature" });
71035
+ return (0, import_neverthrow42.err)({ type: "MISSING_FIELD", field: "feature" });
71102
71036
  }
71103
71037
  return parseTimeout(fields).map((timeout) => ({ feature, timeout }));
71104
71038
  });
@@ -71120,12 +71054,12 @@ function parseYamlFields(block) {
71120
71054
  }
71121
71055
 
71122
71056
  // src/commands/spec-resolver.ts
71123
- var safeReadFile4 = (0, import_neverthrow44.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
71124
- var safeReaddir = (0, import_neverthrow44.fromThrowable)(
71057
+ var safeReadFile4 = (0, import_neverthrow43.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
71058
+ var safeReaddir = (0, import_neverthrow43.fromThrowable)(
71125
71059
  (directory) => (0, import_node_fs8.readdirSync)(directory, { recursive: true, encoding: "utf8" })
71126
71060
  );
71127
71061
  var CANCEL = "xqa:cancel";
71128
- var safeSelect = import_neverthrow44.ResultAsync.fromThrowable(
71062
+ var safeSelect = import_neverthrow43.ResultAsync.fromThrowable(
71129
71063
  esm_default11,
71130
71064
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
71131
71065
  );
@@ -71315,7 +71249,7 @@ function runUpdateCommand() {
71315
71249
  var import_node_path18 = __toESM(require("node:path"), 1);
71316
71250
  var import_node_url2 = require("node:url");
71317
71251
  var import_dotenv = __toESM(require_main(), 1);
71318
- var import_neverthrow45 = __toESM(require_index_cjs(), 1);
71252
+ var import_neverthrow44 = __toESM(require_index_cjs(), 1);
71319
71253
 
71320
71254
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
71321
71255
  var external_exports2 = {};
@@ -75376,10 +75310,10 @@ function loadConfig() {
75376
75310
  const messages = result.error.issues.map(
75377
75311
  (issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
75378
75312
  );
75379
- return (0, import_neverthrow45.err)({ type: "INVALID_CONFIG", message: `Configuration error:
75313
+ return (0, import_neverthrow44.err)({ type: "INVALID_CONFIG", message: `Configuration error:
75380
75314
  ${messages.join("\n")}` });
75381
75315
  }
75382
- return (0, import_neverthrow45.ok)(result.data);
75316
+ return (0, import_neverthrow44.ok)(result.data);
75383
75317
  }
75384
75318
 
75385
75319
  // src/core/parse-timeout-seconds.ts
@@ -75412,7 +75346,7 @@ function parseVerboseOption(value) {
75412
75346
 
75413
75347
  // src/pid-lock.ts
75414
75348
  var import_node_fs9 = require("node:fs");
75415
- var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75349
+ var import_neverthrow45 = __toESM(require_index_cjs(), 1);
75416
75350
  var PID_FILE = "/tmp/xqa.pid";
75417
75351
  var SIGINT_EXIT_CODE = 130;
75418
75352
  var SIGTERM_EXIT_CODE = 143;
@@ -75421,7 +75355,7 @@ var HARD_TIMEOUT_MS = 1e4;
75421
75355
  var cleanup = () => {
75422
75356
  (0, import_node_fs9.rmSync)(PID_FILE, { force: true });
75423
75357
  };
75424
- var checkProcessRunning = (0, import_neverthrow46.fromThrowable)(
75358
+ var checkProcessRunning = (0, import_neverthrow45.fromThrowable)(
75425
75359
  (pid) => {
75426
75360
  process.kill(pid, 0);
75427
75361
  return true;
@@ -75487,17 +75421,17 @@ function acquireLock() {
75487
75421
  // src/shell/xqa-directory.ts
75488
75422
  var import_node_fs10 = require("node:fs");
75489
75423
  var import_node_path19 = __toESM(require("node:path"), 1);
75490
- var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75424
+ var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75491
75425
  function findXqaDirectory(startDirectory) {
75492
75426
  let current = startDirectory;
75493
75427
  for (; ; ) {
75494
75428
  const candidate = import_node_path19.default.join(current, ".xqa");
75495
75429
  if ((0, import_node_fs10.existsSync)(candidate)) {
75496
- return (0, import_neverthrow47.ok)(candidate);
75430
+ return (0, import_neverthrow46.ok)(candidate);
75497
75431
  }
75498
75432
  const parent = import_node_path19.default.dirname(current);
75499
75433
  if (parent === current) {
75500
- return (0, import_neverthrow47.err)({ type: "XQA_NOT_INITIALIZED" });
75434
+ return (0, import_neverthrow46.err)({ type: "XQA_NOT_INITIALIZED" });
75501
75435
  }
75502
75436
  current = parent;
75503
75437
  }
@@ -75507,11 +75441,11 @@ function findXqaDirectory(startDirectory) {
75507
75441
  var import_promises21 = __toESM(require("node:fs/promises"), 1);
75508
75442
  var import_node_path23 = __toESM(require("node:path"), 1);
75509
75443
  var import_fast_glob = __toESM(require_out4(), 1);
75510
- var import_neverthrow55 = __toESM(require_index_cjs(), 1);
75444
+ var import_neverthrow54 = __toESM(require_index_cjs(), 1);
75511
75445
 
75512
75446
  // src/suite/core/suite-config-parser.ts
75447
+ var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75513
75448
  var import_neverthrow48 = __toESM(require_index_cjs(), 1);
75514
- var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75515
75449
  var RESERVED_HOOK_ENV_KEYS = [
75516
75450
  "XQA_SIM_UDID",
75517
75451
  "XQA_ITEM_ID",
@@ -75563,7 +75497,7 @@ var suiteConfigSchema = external_exports2.object({
75563
75497
  }).refine((data) => data.specs.length > 0 || data.freestyle.length > 0, {
75564
75498
  message: "Suite must declare at least one spec or freestyle entry"
75565
75499
  });
75566
- var safeJsonParse4 = (0, import_neverthrow48.fromThrowable)(
75500
+ var safeJsonParse4 = (0, import_neverthrow47.fromThrowable)(
75567
75501
  JSON.parse,
75568
75502
  (cause) => ({
75569
75503
  type: "INVALID_SUITE_CONFIG",
@@ -75597,16 +75531,16 @@ function parseSuiteConfig(raw) {
75597
75531
  return safeJsonParse4(raw).andThen((data) => {
75598
75532
  const parsed = suiteConfigSchema.safeParse(data);
75599
75533
  if (!parsed.success) {
75600
- return (0, import_neverthrow49.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75534
+ return (0, import_neverthrow48.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75601
75535
  }
75602
75536
  const hooks = normalizeHooks(parsed.data.hooks);
75603
75537
  if (hooks === void 0) {
75604
- return (0, import_neverthrow49.ok)({
75538
+ return (0, import_neverthrow48.ok)({
75605
75539
  specs: parsed.data.specs,
75606
75540
  freestyle: parsed.data.freestyle
75607
75541
  });
75608
75542
  }
75609
- return (0, import_neverthrow49.ok)({
75543
+ return (0, import_neverthrow48.ok)({
75610
75544
  specs: parsed.data.specs,
75611
75545
  freestyle: parsed.data.freestyle,
75612
75546
  hooks
@@ -75665,7 +75599,7 @@ function buildFreestyleItems(entries) {
75665
75599
  // src/suite/shell/suite-findings-writer.ts
75666
75600
  var import_promises19 = __toESM(require("node:fs/promises"), 1);
75667
75601
  var import_node_path20 = __toESM(require("node:path"), 1);
75668
- var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75602
+ var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75669
75603
  var INDENT_SPACES = 2;
75670
75604
  function writeSuiteFindings(findings, options) {
75671
75605
  const directory = import_node_path20.default.join(
@@ -75677,7 +75611,7 @@ function writeSuiteFindings(findings, options) {
75677
75611
  );
75678
75612
  const finalPath = import_node_path20.default.join(directory, "findings.json");
75679
75613
  const temporaryPath = `${finalPath}.tmp`;
75680
- const safeWriteAtomically = import_neverthrow50.ResultAsync.fromThrowable(
75614
+ const safeWriteAtomically = import_neverthrow49.ResultAsync.fromThrowable(
75681
75615
  async () => {
75682
75616
  await import_promises19.default.mkdir(directory, { recursive: true });
75683
75617
  await import_promises19.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
@@ -75690,7 +75624,7 @@ function writeSuiteFindings(findings, options) {
75690
75624
  }
75691
75625
 
75692
75626
  // src/suite/shell/worker-pool.ts
75693
- var import_neverthrow53 = __toESM(require_index_cjs(), 1);
75627
+ var import_neverthrow52 = __toESM(require_index_cjs(), 1);
75694
75628
 
75695
75629
  // src/suite/core/priority-queue.ts
75696
75630
  var PriorityQueue = class {
@@ -75874,7 +75808,7 @@ function recordHookFailure(input) {
75874
75808
  }
75875
75809
 
75876
75810
  // src/suite/shell/hook-invoker.ts
75877
- var import_neverthrow52 = __toESM(require_index_cjs(), 1);
75811
+ var import_neverthrow51 = __toESM(require_index_cjs(), 1);
75878
75812
 
75879
75813
  // src/suite/core/hook-env-builder.ts
75880
75814
  function buildReservedKeys(input) {
@@ -75900,8 +75834,8 @@ function buildHookEnv(input) {
75900
75834
  }
75901
75835
 
75902
75836
  // src/suite/shell/hook-runner.ts
75903
- var import_node_child_process7 = require("node:child_process");
75904
- var import_neverthrow51 = __toESM(require_index_cjs(), 1);
75837
+ var import_node_child_process6 = require("node:child_process");
75838
+ var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75905
75839
  var noop2 = () => void 0;
75906
75840
  function cleanup2(context) {
75907
75841
  clearTimeout(context.timeoutHandle);
@@ -75930,22 +75864,22 @@ function collectStderr(child, stderrReference) {
75930
75864
  function buildOnAbort(context) {
75931
75865
  return () => {
75932
75866
  context.child.kill("SIGTERM");
75933
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_ABORTED" }));
75867
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_ABORTED" }));
75934
75868
  };
75935
75869
  }
75936
75870
  function attachChildListeners(context) {
75937
75871
  const { child, stderrReference } = context;
75938
75872
  child.on("error", (cause) => {
75939
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_SPAWN_FAILED", cause }));
75873
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_SPAWN_FAILED", cause }));
75940
75874
  });
75941
75875
  child.on("exit", (code) => {
75942
75876
  if (code === 0) {
75943
- settle(context, (0, import_neverthrow51.ok)());
75877
+ settle(context, (0, import_neverthrow50.ok)());
75944
75878
  return;
75945
75879
  }
75946
75880
  settle(
75947
75881
  context,
75948
- (0, import_neverthrow51.err)({
75882
+ (0, import_neverthrow50.err)({
75949
75883
  type: "HOOK_EXIT_NONZERO",
75950
75884
  code: code ?? -1,
75951
75885
  stderr: stderrReference.value
@@ -75957,14 +75891,14 @@ function attachTimeout(context, timeoutMs) {
75957
75891
  clearTimeout(context.timeoutHandle);
75958
75892
  context.timeoutHandle = setTimeout(() => {
75959
75893
  context.child.kill("SIGTERM");
75960
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
75894
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
75961
75895
  }, timeoutMs);
75962
75896
  }
75963
75897
  function buildContext2(options) {
75964
75898
  const { script, cwd, env: env2, baseEnv, nodeExecPath } = options;
75965
75899
  const deferred = makeDeferred();
75966
75900
  const stderrReference = { value: "" };
75967
- const child = (0, import_node_child_process7.spawn)(nodeExecPath, [script], {
75901
+ const child = (0, import_node_child_process6.spawn)(nodeExecPath, [script], {
75968
75902
  cwd,
75969
75903
  env: { ...baseEnv, ...env2 },
75970
75904
  stdio: ["ignore", "inherit", "pipe"]
@@ -75992,7 +75926,7 @@ async function spawnHook(options) {
75992
75926
  attachChildListeners(context);
75993
75927
  return context.deferred.promise;
75994
75928
  }
75995
- var safeSpawn = import_neverthrow51.ResultAsync.fromThrowable(
75929
+ var safeSpawn = import_neverthrow50.ResultAsync.fromThrowable(
75996
75930
  spawnHook,
75997
75931
  (cause) => ({ type: "HOOK_SPAWN_FAILED", cause })
75998
75932
  );
@@ -76020,7 +75954,7 @@ async function invokeHook(input) {
76020
75954
  async function maybeInvokeHook(input) {
76021
75955
  const { hook } = input;
76022
75956
  if (hook === void 0) {
76023
- return (0, import_neverthrow52.ok)();
75957
+ return (0, import_neverthrow51.ok)();
76024
75958
  }
76025
75959
  return invokeHook({ ...input, hook });
76026
75960
  }
@@ -76159,7 +76093,7 @@ function runWorkerPool(config3) {
76159
76093
  const queue = setupQueue(config3);
76160
76094
  const results = [];
76161
76095
  const suiteStartMs = Date.now();
76162
- const safeRun = import_neverthrow53.ResultAsync.fromThrowable(
76096
+ const safeRun = import_neverthrow52.ResultAsync.fromThrowable(
76163
76097
  async () => runAllWorkers({ config: config3, queue, results, suiteStartMs }),
76164
76098
  (cause) => ({ type: "WORKER_POOL_FAILED", cause })
76165
76099
  );
@@ -76188,29 +76122,31 @@ function resolveFreestyleTimeout(item, config3) {
76188
76122
  }
76189
76123
  return config3.QA_EXPLORE_TIMEOUT_SECONDS ?? DEFAULT_FREESTYLE_TIMEOUT_SECONDS2;
76190
76124
  }
76125
+ function buildFreestyleExplorerConfig(input) {
76126
+ const { item, context, simulatorUdid } = input;
76127
+ const { config: config3, date: date5, appContext } = context;
76128
+ return {
76129
+ mode: "freestyle",
76130
+ date: date5,
76131
+ mcpServers: createDefaultMcpServers(simulatorUdid),
76132
+ allowedTools: ALLOWED_TOOLS,
76133
+ timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
76134
+ appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext, item.prompt]),
76135
+ buildEnv: config3.QA_BUILD_ENV,
76136
+ cwd: ensureWorkerCwd(simulatorUdid),
76137
+ record: true
76138
+ };
76139
+ }
76191
76140
  function buildFreestylePipelineConfig(input) {
76192
76141
  const { item, context, signal, simulatorUdid, onEvent } = input;
76193
- const { config: config3, xqaDirectory, runId, date: date5, appContext } = context;
76142
+ const { xqaDirectory, runId } = context;
76194
76143
  return {
76195
76144
  outputDir: import_node_path21.default.join(xqaDirectory, "output", item.id),
76196
76145
  runId,
76197
76146
  simulatorUdid,
76198
76147
  signal,
76199
76148
  onEvent,
76200
- explorer: {
76201
- mode: "freestyle",
76202
- date: date5,
76203
- mcpServers: createDefaultMcpServers(simulatorUdid),
76204
- allowedTools: ALLOWED_TOOLS,
76205
- timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
76206
- appContext: composeAppContext([
76207
- buildDeviceInstruction(simulatorUdid),
76208
- appContext,
76209
- item.prompt
76210
- ]),
76211
- buildEnv: config3.QA_BUILD_ENV,
76212
- cwd: ensureWorkerCwd(simulatorUdid)
76213
- }
76149
+ explorer: buildFreestyleExplorerConfig(input)
76214
76150
  };
76215
76151
  }
76216
76152
  function executeFreestyleItem(input) {
@@ -76232,7 +76168,8 @@ function buildSpecPipelineConfig(input) {
76232
76168
  allowedTools: ALLOWED_TOOLS,
76233
76169
  appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext]),
76234
76170
  buildEnv: config3.QA_BUILD_ENV,
76235
- cwd: ensureWorkerCwd(simulatorUdid)
76171
+ cwd: ensureWorkerCwd(simulatorUdid),
76172
+ record: true
76236
76173
  }
76237
76174
  };
76238
76175
  }
@@ -76252,7 +76189,7 @@ function makeExecuteItem(context) {
76252
76189
  // src/suite/commands/suite-run-context.ts
76253
76190
  var import_promises20 = __toESM(require("node:fs/promises"), 1);
76254
76191
  var import_node_path22 = __toESM(require("node:path"), 1);
76255
- var import_neverthrow54 = __toESM(require_index_cjs(), 1);
76192
+ var import_neverthrow53 = __toESM(require_index_cjs(), 1);
76256
76193
 
76257
76194
  // src/suite/core/run-id.ts
76258
76195
  var RUN_ID_PAD_LENGTH2 = 4;
@@ -76287,7 +76224,7 @@ function deriveSuiteId(input) {
76287
76224
 
76288
76225
  // src/suite/commands/suite-run-context.ts
76289
76226
  var ISO_DATE_LENGTH3 = 10;
76290
- var safeReaddir2 = import_neverthrow54.ResultAsync.fromThrowable(
76227
+ var safeReaddir2 = import_neverthrow53.ResultAsync.fromThrowable(
76291
76228
  async (directoryPath) => {
76292
76229
  const entries = await import_promises20.default.readdir(directoryPath, { withFileTypes: true });
76293
76230
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -76335,7 +76272,7 @@ async function buildSuiteRunContext(input) {
76335
76272
  }
76336
76273
 
76337
76274
  // src/suite/commands/run-command.ts
76338
- var safeReadFile5 = import_neverthrow55.ResultAsync.fromThrowable(
76275
+ var safeReadFile5 = import_neverthrow54.ResultAsync.fromThrowable(
76339
76276
  async (filePath) => import_promises21.default.readFile(filePath, "utf8"),
76340
76277
  () => "READ_FAILED"
76341
76278
  );
@@ -76523,7 +76460,7 @@ function resolveXqaDirectory() {
76523
76460
  return result.value;
76524
76461
  }
76525
76462
  var program2 = new Command();
76526
- program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.14.2"}${false ? ` (dev build +${"5d87cb5"})` : ""}`);
76463
+ program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.15.1"}${false ? ` (dev build +${"8f8a210"})` : ""}`);
76527
76464
  program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
76528
76465
  runInitCommand();
76529
76466
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/xqa",
3
- "version": "1.14.2",
3
+ "version": "1.15.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -28,11 +28,11 @@
28
28
  "zod": "^3.0.0",
29
29
  "@qa-agents/display": "0.0.0",
30
30
  "@qa-agents/eslint-config": "0.0.0",
31
- "@qa-agents/explorer": "0.0.0",
32
- "@qa-agents/mobile-ios": "0.0.0",
33
31
  "@qa-agents/pipeline": "0.0.0",
34
32
  "@qa-agents/shared": "0.0.0",
35
- "@qa-agents/typescript-config": "0.0.0"
33
+ "@qa-agents/typescript-config": "0.0.0",
34
+ "@qa-agents/explorer": "0.0.0",
35
+ "@qa-agents/mobile-ios": "0.0.0"
36
36
  },
37
37
  "dependencies": {
38
38
  "ajv": "^8.18.0",