@exodus/xqa 1.14.2 → 1.15.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 (3) hide show
  1. package/README.md +0 -1
  2. package/dist/xqa.cjs +88 -171
  3. package/package.json +2 -2
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
  });
@@ -56123,24 +56121,13 @@ function makePollTick(onEvent) {
56123
56121
  });
56124
56122
  };
56125
56123
  }
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
56124
  function buildRunContext(artifacts, config3) {
56136
- const variant = config3?.videoVariant ?? "4x";
56137
56125
  return {
56138
56126
  onEvent: config3?.onEvent === void 0 ? void 0 : safeHandler(config3.onEvent),
56139
56127
  apiKey: config3?.apiKey ?? "",
56140
56128
  model: config3?.model ?? DEFAULT_MODEL,
56141
- variant,
56142
56129
  start: Date.now(),
56143
- videoPath: resolveVideoPath(artifacts, variant),
56130
+ videoPath: artifacts.videoPath,
56144
56131
  prompt: buildAnalyserPrompt(artifacts.snapshots)
56145
56132
  };
56146
56133
  }
@@ -56170,11 +56157,11 @@ function runUploadPipeline(context) {
56170
56157
  }
56171
56158
  function runAnalyser(artifacts, config3) {
56172
56159
  const context = buildRunContext(artifacts, config3);
56173
- const { onEvent, variant, start, videoPath } = context;
56160
+ const { onEvent, start, videoPath } = context;
56174
56161
  onEvent?.({ type: "STAGE_START", agent: "analyser" });
56175
56162
  if (!videoPath) {
56176
56163
  emitStageEnd(onEvent, start);
56177
- return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING", variant });
56164
+ return (0, import_neverthrow10.errAsync)({ type: "VIDEO_PATH_MISSING" });
56178
56165
  }
56179
56166
  return runUploadPipeline(context);
56180
56167
  }
@@ -62798,17 +62785,6 @@ function runQuery(prompt, config3) {
62798
62785
  function collectAgentOutput(prompt, config3) {
62799
62786
  return runQuery(prompt, config3).mapErr((cause) => ({ type: "QUERY_FAILED", cause }));
62800
62787
  }
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
62788
  function spawnRecorder(outputPath) {
62813
62789
  const safeMkdirSync = (0, import_neverthrow20.fromThrowable)(import_node_fs.mkdirSync, (cause) => cause);
62814
62790
  const safeSpawn2 = (0, import_neverthrow20.fromThrowable)(
@@ -62882,35 +62858,6 @@ function stopRecording(handle) {
62882
62858
  (cause) => ({ type: "STOP_FAILED", cause })
62883
62859
  );
62884
62860
  }
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
62861
  function runWithRecording(handle, collectOutput) {
62915
62862
  const toRecordingError = (cause) => ({
62916
62863
  type: "RECORDING_FAILED",
@@ -62939,7 +62886,7 @@ function startAndRun(params) {
62939
62886
  }).mapErr((error48) => {
62940
62887
  signal?.removeEventListener("abort", onAbort);
62941
62888
  return error48;
62942
- }).andThen((result) => applySpeedUpVariants({ result, params, toRecordingError }));
62889
+ });
62943
62890
  });
62944
62891
  }
62945
62892
  var TOOL_SELECTION_SECTION = `## Tool Selection
@@ -63413,15 +63360,13 @@ function toArtifacts(result, runPaths) {
63413
63360
  return {
63414
63361
  findings: result.findings,
63415
63362
  videoPath: runPaths.videoPath,
63416
- videoPath2x: runPaths.videoPath2x,
63417
- videoPath4x: runPaths.videoPath4x,
63418
63363
  snapshots: result.snapshots
63419
63364
  };
63420
63365
  }
63421
63366
  function emitStageEnd3(safeConfig, start) {
63422
63367
  safeConfig.onEvent?.({ type: "STAGE_END", agent: "explorer", durationMs: Date.now() - start });
63423
63368
  }
63424
- var EMPTY_RUN_PATHS = { videoPath: "", videoPath2x: "", videoPath4x: "" };
63369
+ var EMPTY_RUN_PATHS = { videoPath: "" };
63425
63370
  function collectWithoutRecording({
63426
63371
  safeConfig,
63427
63372
  prompt,
@@ -63446,8 +63391,6 @@ function collectAndFinalize({
63446
63391
  }
63447
63392
  return startAndRun({
63448
63393
  videoPath: runPaths.videoPath,
63449
- videoPath2x: runPaths.videoPath2x,
63450
- videoPath4x: runPaths.videoPath4x,
63451
63394
  signal: safeConfig.signal,
63452
63395
  collectOutput: () => collectAgentOutput(prompt, safeConfig)
63453
63396
  }).map((result) => {
@@ -67441,7 +67384,6 @@ var ALLOWED_TOOLS = [...MOBILE_IOS_TOOLS];
67441
67384
  var MS_PER_SECOND4 = 1e3;
67442
67385
  var DEFAULT_ABORT_EXIT_CODE = 130;
67443
67386
  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
67387
 
67446
67388
  // src/shell/debug-logger-core.ts
67447
67389
  var ELAPSED_PAD = 8;
@@ -67723,7 +67665,7 @@ var JSON_INDENT = 2;
67723
67665
  var ITEM_ID = "analyse";
67724
67666
  var ITEM_NAME = "analyse";
67725
67667
  function buildArtifacts(videoPath) {
67726
- return { videoPath, videoPath2x: "", videoPath4x: videoPath, findings: [], snapshots: [] };
67668
+ return { videoPath, findings: [], snapshots: [] };
67727
67669
  }
67728
67670
  async function checkVideoPathExists(videoPath) {
67729
67671
  const safeAccess = (0, import_neverthrow36.fromAsyncThrowable)(import_promises17.access, () => ({ type: "FILE_NOT_FOUND" }));
@@ -67770,7 +67712,6 @@ async function runAnalysisAndExit(input) {
67770
67712
  const state = { identity, startedAt };
67771
67713
  const result = await runAnalysis(input.artifacts, {
67772
67714
  apiKey: input.apiKey,
67773
- videoVariant: "1x",
67774
67715
  onEvent
67775
67716
  });
67776
67717
  result.match(
@@ -67791,7 +67732,7 @@ function ensureApiKey(config3) {
67791
67732
  }
67792
67733
  return apiKey;
67793
67734
  }
67794
- async function resolveVideoPath2(videoPath) {
67735
+ async function resolveVideoPath(videoPath) {
67795
67736
  if (videoPath === void 0) {
67796
67737
  process.stderr.write('A video path is required. Pass the path printed by "xqa explore".\n');
67797
67738
  process.exit(1);
@@ -67810,7 +67751,7 @@ async function runAnalyseCommand(input) {
67810
67751
  if (apiKey === void 0) {
67811
67752
  return;
67812
67753
  }
67813
- const videoPath = await resolveVideoPath2(input.videoPath);
67754
+ const videoPath = await resolveVideoPath(input.videoPath);
67814
67755
  if (videoPath === void 0) {
67815
67756
  return;
67816
67757
  }
@@ -68009,35 +67950,8 @@ function readExploreContext(xqaDirectory) {
68009
67950
  return readContextFile(xqaDirectory, "explore.md");
68010
67951
  }
68011
67952
 
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
67953
  // src/shell/preflight.ts
68035
67954
  async function runPreflightChecks() {
68036
- const ffmpegCheck = await checkFfmpegAvailable();
68037
- if (ffmpegCheck.isErr()) {
68038
- process.stderr.write(FFMPEG_INSTALL_MESSAGE);
68039
- return 1;
68040
- }
68041
67955
  const idbCheck = await checkIdbAvailable();
68042
67956
  if (idbCheck.isErr()) {
68043
67957
  process.stderr.write(IDB_INSTALL_MESSAGE);
@@ -68225,7 +68139,7 @@ var import_node_fs6 = require("node:fs");
68225
68139
  var import_node_path12 = __toESM(require("node:path"), 1);
68226
68140
 
68227
68141
  // src/commands/install-skills.ts
68228
- var import_node_child_process6 = require("node:child_process");
68142
+ var import_node_child_process5 = require("node:child_process");
68229
68143
  var import_node_fs5 = require("node:fs");
68230
68144
  var import_node_path11 = __toESM(require("node:path"), 1);
68231
68145
  var import_node_url = require("node:url");
@@ -68236,7 +68150,7 @@ function resolveSkillsRoot() {
68236
68150
  function installSkills() {
68237
68151
  const skillsRoot = resolveSkillsRoot();
68238
68152
  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"], {
68153
+ (0, import_node_child_process5.spawnSync)("npx", ["skills", "add", import_node_path11.default.join(skillsRoot, skill), "-y"], {
68240
68154
  stdio: "inherit"
68241
68155
  });
68242
68156
  }
@@ -68314,7 +68228,7 @@ function runInitCommand() {
68314
68228
  // src/commands/review-command.ts
68315
68229
  var import_node_fs7 = require("node:fs");
68316
68230
  var import_node_path14 = __toESM(require("node:path"), 1);
68317
- var import_neverthrow42 = __toESM(require_index_cjs(), 1);
68231
+ var import_neverthrow41 = __toESM(require_index_cjs(), 1);
68318
68232
 
68319
68233
  // ../../node_modules/.pnpm/@inquirer+core@10.3.2_@types+node@22.19.15/node_modules/@inquirer/core/dist/esm/lib/key.js
68320
68234
  var isUpKey = (key, keybindings = []) => (
@@ -70719,7 +70633,7 @@ var esm_default11 = createPrompt((config3, done) => {
70719
70633
  });
70720
70634
 
70721
70635
  // src/review-session.ts
70722
- var import_neverthrow41 = __toESM(require_index_cjs(), 1);
70636
+ var import_neverthrow40 = __toESM(require_index_cjs(), 1);
70723
70637
  var CONFIDENCE_PERCENT = 100;
70724
70638
  var FLOW_COL_WIDTH = 35;
70725
70639
  var TRIGGER_COL_WIDTH = 16;
@@ -70872,15 +70786,15 @@ async function runInteractiveLoop(findings, existing) {
70872
70786
  }
70873
70787
  return { staged: state.staged, undoneKeys: state.undoneKeys };
70874
70788
  }
70875
- var safeRunInteractiveLoop = (0, import_neverthrow41.fromAsyncThrowable)(
70789
+ var safeRunInteractiveLoop = (0, import_neverthrow40.fromAsyncThrowable)(
70876
70790
  runInteractiveLoop,
70877
70791
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "exit-prompt" : "unexpected"
70878
70792
  );
70879
70793
 
70880
70794
  // 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) => {
70795
+ var safeReadFile3 = (0, import_neverthrow41.fromThrowable)((filePath) => (0, import_node_fs7.readFileSync)(filePath, "utf8"));
70796
+ var safeParseJson3 = (0, import_neverthrow41.fromThrowable)(JSON.parse);
70797
+ var safeWrite = (0, import_neverthrow41.fromThrowable)((filePath, content) => {
70884
70798
  (0, import_node_fs7.writeFileSync)(filePath, content);
70885
70799
  });
70886
70800
  function readLastPath(xqaDirectory) {
@@ -70897,13 +70811,13 @@ function isPipelineOutput(data) {
70897
70811
  function readFindings(filePath) {
70898
70812
  const readResult = safeReadFile3(filePath);
70899
70813
  if (readResult.isErr()) {
70900
- return (0, import_neverthrow42.err)("not-found");
70814
+ return (0, import_neverthrow41.err)("not-found");
70901
70815
  }
70902
70816
  return safeParseJson3(readResult.value).mapErr(() => "invalid").andThen((data) => {
70903
70817
  if (!isPipelineOutput(data)) {
70904
- return (0, import_neverthrow42.err)("invalid");
70818
+ return (0, import_neverthrow41.err)("invalid");
70905
70819
  }
70906
- return (0, import_neverthrow42.ok)(data);
70820
+ return (0, import_neverthrow41.ok)(data);
70907
70821
  });
70908
70822
  }
70909
70823
  function loadExistingDismissals(filePath) {
@@ -70976,7 +70890,7 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70976
70890
  "No findings path provided and no last path found. Run: xqa review <findings-path>\n"
70977
70891
  );
70978
70892
  process.exit(1);
70979
- return (0, import_neverthrow42.err)();
70893
+ return (0, import_neverthrow41.err)();
70980
70894
  }
70981
70895
  const resolvedPath = resolvedPathResult.value;
70982
70896
  const findingsResult = readFindings(resolvedPath);
@@ -70989,9 +70903,9 @@ function resolveAndReadFindings(findingsPath, xqaDirectory) {
70989
70903
  `);
70990
70904
  }
70991
70905
  process.exit(1);
70992
- return (0, import_neverthrow42.err)();
70906
+ return (0, import_neverthrow41.err)();
70993
70907
  }
70994
- return (0, import_neverthrow42.ok)({ resolvedPath, output: findingsResult.value });
70908
+ return (0, import_neverthrow41.ok)({ resolvedPath, output: findingsResult.value });
70995
70909
  }
70996
70910
  async function runReviewLoop({
70997
70911
  findings,
@@ -71065,40 +70979,40 @@ function stripExtensions(filename) {
71065
70979
  // src/commands/spec-resolver.ts
71066
70980
  var import_node_fs8 = require("node:fs");
71067
70981
  var import_node_path16 = __toESM(require("node:path"), 1);
71068
- var import_neverthrow44 = __toESM(require_index_cjs(), 1);
70982
+ var import_neverthrow43 = __toESM(require_index_cjs(), 1);
71069
70983
 
71070
70984
  // src/spec-frontmatter.ts
71071
- var import_neverthrow43 = __toESM(require_index_cjs(), 1);
70985
+ var import_neverthrow42 = __toESM(require_index_cjs(), 1);
71072
70986
  var FRONTMATTER_OPEN_LEN = 4;
71073
70987
  var FRONTMATTER_MARKER_LEN = 3;
71074
70988
  function extractFrontmatterBlock(content) {
71075
70989
  const normalized = content.replaceAll("\r\n", "\n");
71076
70990
  if (!normalized.startsWith("---")) {
71077
- return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
70991
+ return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71078
70992
  }
71079
70993
  const end = normalized.indexOf("\n---", FRONTMATTER_MARKER_LEN);
71080
70994
  if (end === -1) {
71081
- return (0, import_neverthrow43.err)({ type: "MISSING_FRONTMATTER" });
70995
+ return (0, import_neverthrow42.err)({ type: "MISSING_FRONTMATTER" });
71082
70996
  }
71083
- return (0, import_neverthrow43.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
70997
+ return (0, import_neverthrow42.ok)(normalized.slice(FRONTMATTER_OPEN_LEN, end));
71084
70998
  }
71085
70999
  function parseTimeout(fields) {
71086
71000
  const raw = fields.get("timeout");
71087
71001
  if (raw === void 0) {
71088
- return (0, import_neverthrow43.ok)(raw);
71002
+ return (0, import_neverthrow42.ok)(raw);
71089
71003
  }
71090
71004
  const parsed = Number(raw);
71091
71005
  if (Number.isNaN(parsed) || parsed <= 0) {
71092
- return (0, import_neverthrow43.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
71006
+ return (0, import_neverthrow42.err)({ type: "PARSE_ERROR", cause: `invalid timeout: ${raw}` });
71093
71007
  }
71094
- return (0, import_neverthrow43.ok)(parsed);
71008
+ return (0, import_neverthrow42.ok)(parsed);
71095
71009
  }
71096
71010
  function parseSpecFrontmatter(content) {
71097
71011
  return extractFrontmatterBlock(content).andThen((block) => {
71098
71012
  const fields = parseYamlFields(block);
71099
71013
  const feature = fields.get("feature");
71100
71014
  if (feature === void 0) {
71101
- return (0, import_neverthrow43.err)({ type: "MISSING_FIELD", field: "feature" });
71015
+ return (0, import_neverthrow42.err)({ type: "MISSING_FIELD", field: "feature" });
71102
71016
  }
71103
71017
  return parseTimeout(fields).map((timeout) => ({ feature, timeout }));
71104
71018
  });
@@ -71120,12 +71034,12 @@ function parseYamlFields(block) {
71120
71034
  }
71121
71035
 
71122
71036
  // 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)(
71037
+ var safeReadFile4 = (0, import_neverthrow43.fromThrowable)((filePath) => (0, import_node_fs8.readFileSync)(filePath, "utf8"));
71038
+ var safeReaddir = (0, import_neverthrow43.fromThrowable)(
71125
71039
  (directory) => (0, import_node_fs8.readdirSync)(directory, { recursive: true, encoding: "utf8" })
71126
71040
  );
71127
71041
  var CANCEL = "xqa:cancel";
71128
- var safeSelect = import_neverthrow44.ResultAsync.fromThrowable(
71042
+ var safeSelect = import_neverthrow43.ResultAsync.fromThrowable(
71129
71043
  esm_default11,
71130
71044
  (error48) => error48 instanceof Error && error48.name === "ExitPromptError" ? "cancelled" : "failed"
71131
71045
  );
@@ -71315,7 +71229,7 @@ function runUpdateCommand() {
71315
71229
  var import_node_path18 = __toESM(require("node:path"), 1);
71316
71230
  var import_node_url2 = require("node:url");
71317
71231
  var import_dotenv = __toESM(require_main(), 1);
71318
- var import_neverthrow45 = __toESM(require_index_cjs(), 1);
71232
+ var import_neverthrow44 = __toESM(require_index_cjs(), 1);
71319
71233
 
71320
71234
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
71321
71235
  var external_exports2 = {};
@@ -75376,10 +75290,10 @@ function loadConfig() {
75376
75290
  const messages = result.error.issues.map(
75377
75291
  (issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`
75378
75292
  );
75379
- return (0, import_neverthrow45.err)({ type: "INVALID_CONFIG", message: `Configuration error:
75293
+ return (0, import_neverthrow44.err)({ type: "INVALID_CONFIG", message: `Configuration error:
75380
75294
  ${messages.join("\n")}` });
75381
75295
  }
75382
- return (0, import_neverthrow45.ok)(result.data);
75296
+ return (0, import_neverthrow44.ok)(result.data);
75383
75297
  }
75384
75298
 
75385
75299
  // src/core/parse-timeout-seconds.ts
@@ -75412,7 +75326,7 @@ function parseVerboseOption(value) {
75412
75326
 
75413
75327
  // src/pid-lock.ts
75414
75328
  var import_node_fs9 = require("node:fs");
75415
- var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75329
+ var import_neverthrow45 = __toESM(require_index_cjs(), 1);
75416
75330
  var PID_FILE = "/tmp/xqa.pid";
75417
75331
  var SIGINT_EXIT_CODE = 130;
75418
75332
  var SIGTERM_EXIT_CODE = 143;
@@ -75421,7 +75335,7 @@ var HARD_TIMEOUT_MS = 1e4;
75421
75335
  var cleanup = () => {
75422
75336
  (0, import_node_fs9.rmSync)(PID_FILE, { force: true });
75423
75337
  };
75424
- var checkProcessRunning = (0, import_neverthrow46.fromThrowable)(
75338
+ var checkProcessRunning = (0, import_neverthrow45.fromThrowable)(
75425
75339
  (pid) => {
75426
75340
  process.kill(pid, 0);
75427
75341
  return true;
@@ -75487,17 +75401,17 @@ function acquireLock() {
75487
75401
  // src/shell/xqa-directory.ts
75488
75402
  var import_node_fs10 = require("node:fs");
75489
75403
  var import_node_path19 = __toESM(require("node:path"), 1);
75490
- var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75404
+ var import_neverthrow46 = __toESM(require_index_cjs(), 1);
75491
75405
  function findXqaDirectory(startDirectory) {
75492
75406
  let current = startDirectory;
75493
75407
  for (; ; ) {
75494
75408
  const candidate = import_node_path19.default.join(current, ".xqa");
75495
75409
  if ((0, import_node_fs10.existsSync)(candidate)) {
75496
- return (0, import_neverthrow47.ok)(candidate);
75410
+ return (0, import_neverthrow46.ok)(candidate);
75497
75411
  }
75498
75412
  const parent = import_node_path19.default.dirname(current);
75499
75413
  if (parent === current) {
75500
- return (0, import_neverthrow47.err)({ type: "XQA_NOT_INITIALIZED" });
75414
+ return (0, import_neverthrow46.err)({ type: "XQA_NOT_INITIALIZED" });
75501
75415
  }
75502
75416
  current = parent;
75503
75417
  }
@@ -75507,11 +75421,11 @@ function findXqaDirectory(startDirectory) {
75507
75421
  var import_promises21 = __toESM(require("node:fs/promises"), 1);
75508
75422
  var import_node_path23 = __toESM(require("node:path"), 1);
75509
75423
  var import_fast_glob = __toESM(require_out4(), 1);
75510
- var import_neverthrow55 = __toESM(require_index_cjs(), 1);
75424
+ var import_neverthrow54 = __toESM(require_index_cjs(), 1);
75511
75425
 
75512
75426
  // src/suite/core/suite-config-parser.ts
75427
+ var import_neverthrow47 = __toESM(require_index_cjs(), 1);
75513
75428
  var import_neverthrow48 = __toESM(require_index_cjs(), 1);
75514
- var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75515
75429
  var RESERVED_HOOK_ENV_KEYS = [
75516
75430
  "XQA_SIM_UDID",
75517
75431
  "XQA_ITEM_ID",
@@ -75563,7 +75477,7 @@ var suiteConfigSchema = external_exports2.object({
75563
75477
  }).refine((data) => data.specs.length > 0 || data.freestyle.length > 0, {
75564
75478
  message: "Suite must declare at least one spec or freestyle entry"
75565
75479
  });
75566
- var safeJsonParse4 = (0, import_neverthrow48.fromThrowable)(
75480
+ var safeJsonParse4 = (0, import_neverthrow47.fromThrowable)(
75567
75481
  JSON.parse,
75568
75482
  (cause) => ({
75569
75483
  type: "INVALID_SUITE_CONFIG",
@@ -75597,16 +75511,16 @@ function parseSuiteConfig(raw) {
75597
75511
  return safeJsonParse4(raw).andThen((data) => {
75598
75512
  const parsed = suiteConfigSchema.safeParse(data);
75599
75513
  if (!parsed.success) {
75600
- return (0, import_neverthrow49.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75514
+ return (0, import_neverthrow48.err)({ type: "INVALID_SUITE_CONFIG", cause: parsed.error });
75601
75515
  }
75602
75516
  const hooks = normalizeHooks(parsed.data.hooks);
75603
75517
  if (hooks === void 0) {
75604
- return (0, import_neverthrow49.ok)({
75518
+ return (0, import_neverthrow48.ok)({
75605
75519
  specs: parsed.data.specs,
75606
75520
  freestyle: parsed.data.freestyle
75607
75521
  });
75608
75522
  }
75609
- return (0, import_neverthrow49.ok)({
75523
+ return (0, import_neverthrow48.ok)({
75610
75524
  specs: parsed.data.specs,
75611
75525
  freestyle: parsed.data.freestyle,
75612
75526
  hooks
@@ -75665,7 +75579,7 @@ function buildFreestyleItems(entries) {
75665
75579
  // src/suite/shell/suite-findings-writer.ts
75666
75580
  var import_promises19 = __toESM(require("node:fs/promises"), 1);
75667
75581
  var import_node_path20 = __toESM(require("node:path"), 1);
75668
- var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75582
+ var import_neverthrow49 = __toESM(require_index_cjs(), 1);
75669
75583
  var INDENT_SPACES = 2;
75670
75584
  function writeSuiteFindings(findings, options) {
75671
75585
  const directory = import_node_path20.default.join(
@@ -75677,7 +75591,7 @@ function writeSuiteFindings(findings, options) {
75677
75591
  );
75678
75592
  const finalPath = import_node_path20.default.join(directory, "findings.json");
75679
75593
  const temporaryPath = `${finalPath}.tmp`;
75680
- const safeWriteAtomically = import_neverthrow50.ResultAsync.fromThrowable(
75594
+ const safeWriteAtomically = import_neverthrow49.ResultAsync.fromThrowable(
75681
75595
  async () => {
75682
75596
  await import_promises19.default.mkdir(directory, { recursive: true });
75683
75597
  await import_promises19.default.writeFile(temporaryPath, JSON.stringify(findings, void 0, INDENT_SPACES));
@@ -75690,7 +75604,7 @@ function writeSuiteFindings(findings, options) {
75690
75604
  }
75691
75605
 
75692
75606
  // src/suite/shell/worker-pool.ts
75693
- var import_neverthrow53 = __toESM(require_index_cjs(), 1);
75607
+ var import_neverthrow52 = __toESM(require_index_cjs(), 1);
75694
75608
 
75695
75609
  // src/suite/core/priority-queue.ts
75696
75610
  var PriorityQueue = class {
@@ -75874,7 +75788,7 @@ function recordHookFailure(input) {
75874
75788
  }
75875
75789
 
75876
75790
  // src/suite/shell/hook-invoker.ts
75877
- var import_neverthrow52 = __toESM(require_index_cjs(), 1);
75791
+ var import_neverthrow51 = __toESM(require_index_cjs(), 1);
75878
75792
 
75879
75793
  // src/suite/core/hook-env-builder.ts
75880
75794
  function buildReservedKeys(input) {
@@ -75900,8 +75814,8 @@ function buildHookEnv(input) {
75900
75814
  }
75901
75815
 
75902
75816
  // 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);
75817
+ var import_node_child_process6 = require("node:child_process");
75818
+ var import_neverthrow50 = __toESM(require_index_cjs(), 1);
75905
75819
  var noop2 = () => void 0;
75906
75820
  function cleanup2(context) {
75907
75821
  clearTimeout(context.timeoutHandle);
@@ -75930,22 +75844,22 @@ function collectStderr(child, stderrReference) {
75930
75844
  function buildOnAbort(context) {
75931
75845
  return () => {
75932
75846
  context.child.kill("SIGTERM");
75933
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_ABORTED" }));
75847
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_ABORTED" }));
75934
75848
  };
75935
75849
  }
75936
75850
  function attachChildListeners(context) {
75937
75851
  const { child, stderrReference } = context;
75938
75852
  child.on("error", (cause) => {
75939
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_SPAWN_FAILED", cause }));
75853
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_SPAWN_FAILED", cause }));
75940
75854
  });
75941
75855
  child.on("exit", (code) => {
75942
75856
  if (code === 0) {
75943
- settle(context, (0, import_neverthrow51.ok)());
75857
+ settle(context, (0, import_neverthrow50.ok)());
75944
75858
  return;
75945
75859
  }
75946
75860
  settle(
75947
75861
  context,
75948
- (0, import_neverthrow51.err)({
75862
+ (0, import_neverthrow50.err)({
75949
75863
  type: "HOOK_EXIT_NONZERO",
75950
75864
  code: code ?? -1,
75951
75865
  stderr: stderrReference.value
@@ -75957,14 +75871,14 @@ function attachTimeout(context, timeoutMs) {
75957
75871
  clearTimeout(context.timeoutHandle);
75958
75872
  context.timeoutHandle = setTimeout(() => {
75959
75873
  context.child.kill("SIGTERM");
75960
- settle(context, (0, import_neverthrow51.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
75874
+ settle(context, (0, import_neverthrow50.err)({ type: "HOOK_TIMEOUT", timeoutMs }));
75961
75875
  }, timeoutMs);
75962
75876
  }
75963
75877
  function buildContext2(options) {
75964
75878
  const { script, cwd, env: env2, baseEnv, nodeExecPath } = options;
75965
75879
  const deferred = makeDeferred();
75966
75880
  const stderrReference = { value: "" };
75967
- const child = (0, import_node_child_process7.spawn)(nodeExecPath, [script], {
75881
+ const child = (0, import_node_child_process6.spawn)(nodeExecPath, [script], {
75968
75882
  cwd,
75969
75883
  env: { ...baseEnv, ...env2 },
75970
75884
  stdio: ["ignore", "inherit", "pipe"]
@@ -75992,7 +75906,7 @@ async function spawnHook(options) {
75992
75906
  attachChildListeners(context);
75993
75907
  return context.deferred.promise;
75994
75908
  }
75995
- var safeSpawn = import_neverthrow51.ResultAsync.fromThrowable(
75909
+ var safeSpawn = import_neverthrow50.ResultAsync.fromThrowable(
75996
75910
  spawnHook,
75997
75911
  (cause) => ({ type: "HOOK_SPAWN_FAILED", cause })
75998
75912
  );
@@ -76020,7 +75934,7 @@ async function invokeHook(input) {
76020
75934
  async function maybeInvokeHook(input) {
76021
75935
  const { hook } = input;
76022
75936
  if (hook === void 0) {
76023
- return (0, import_neverthrow52.ok)();
75937
+ return (0, import_neverthrow51.ok)();
76024
75938
  }
76025
75939
  return invokeHook({ ...input, hook });
76026
75940
  }
@@ -76159,7 +76073,7 @@ function runWorkerPool(config3) {
76159
76073
  const queue = setupQueue(config3);
76160
76074
  const results = [];
76161
76075
  const suiteStartMs = Date.now();
76162
- const safeRun = import_neverthrow53.ResultAsync.fromThrowable(
76076
+ const safeRun = import_neverthrow52.ResultAsync.fromThrowable(
76163
76077
  async () => runAllWorkers({ config: config3, queue, results, suiteStartMs }),
76164
76078
  (cause) => ({ type: "WORKER_POOL_FAILED", cause })
76165
76079
  );
@@ -76188,29 +76102,31 @@ function resolveFreestyleTimeout(item, config3) {
76188
76102
  }
76189
76103
  return config3.QA_EXPLORE_TIMEOUT_SECONDS ?? DEFAULT_FREESTYLE_TIMEOUT_SECONDS2;
76190
76104
  }
76105
+ function buildFreestyleExplorerConfig(input) {
76106
+ const { item, context, simulatorUdid } = input;
76107
+ const { config: config3, date: date5, appContext } = context;
76108
+ return {
76109
+ mode: "freestyle",
76110
+ date: date5,
76111
+ mcpServers: createDefaultMcpServers(simulatorUdid),
76112
+ allowedTools: ALLOWED_TOOLS,
76113
+ timeoutMs: resolveFreestyleTimeout(item, config3) * MS_PER_SECOND4,
76114
+ appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext, item.prompt]),
76115
+ buildEnv: config3.QA_BUILD_ENV,
76116
+ cwd: ensureWorkerCwd(simulatorUdid),
76117
+ record: true
76118
+ };
76119
+ }
76191
76120
  function buildFreestylePipelineConfig(input) {
76192
76121
  const { item, context, signal, simulatorUdid, onEvent } = input;
76193
- const { config: config3, xqaDirectory, runId, date: date5, appContext } = context;
76122
+ const { xqaDirectory, runId } = context;
76194
76123
  return {
76195
76124
  outputDir: import_node_path21.default.join(xqaDirectory, "output", item.id),
76196
76125
  runId,
76197
76126
  simulatorUdid,
76198
76127
  signal,
76199
76128
  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
- }
76129
+ explorer: buildFreestyleExplorerConfig(input)
76214
76130
  };
76215
76131
  }
76216
76132
  function executeFreestyleItem(input) {
@@ -76232,7 +76148,8 @@ function buildSpecPipelineConfig(input) {
76232
76148
  allowedTools: ALLOWED_TOOLS,
76233
76149
  appContext: composeAppContext([buildDeviceInstruction(simulatorUdid), appContext]),
76234
76150
  buildEnv: config3.QA_BUILD_ENV,
76235
- cwd: ensureWorkerCwd(simulatorUdid)
76151
+ cwd: ensureWorkerCwd(simulatorUdid),
76152
+ record: true
76236
76153
  }
76237
76154
  };
76238
76155
  }
@@ -76252,7 +76169,7 @@ function makeExecuteItem(context) {
76252
76169
  // src/suite/commands/suite-run-context.ts
76253
76170
  var import_promises20 = __toESM(require("node:fs/promises"), 1);
76254
76171
  var import_node_path22 = __toESM(require("node:path"), 1);
76255
- var import_neverthrow54 = __toESM(require_index_cjs(), 1);
76172
+ var import_neverthrow53 = __toESM(require_index_cjs(), 1);
76256
76173
 
76257
76174
  // src/suite/core/run-id.ts
76258
76175
  var RUN_ID_PAD_LENGTH2 = 4;
@@ -76287,7 +76204,7 @@ function deriveSuiteId(input) {
76287
76204
 
76288
76205
  // src/suite/commands/suite-run-context.ts
76289
76206
  var ISO_DATE_LENGTH3 = 10;
76290
- var safeReaddir2 = import_neverthrow54.ResultAsync.fromThrowable(
76207
+ var safeReaddir2 = import_neverthrow53.ResultAsync.fromThrowable(
76291
76208
  async (directoryPath) => {
76292
76209
  const entries = await import_promises20.default.readdir(directoryPath, { withFileTypes: true });
76293
76210
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -76335,7 +76252,7 @@ async function buildSuiteRunContext(input) {
76335
76252
  }
76336
76253
 
76337
76254
  // src/suite/commands/run-command.ts
76338
- var safeReadFile5 = import_neverthrow55.ResultAsync.fromThrowable(
76255
+ var safeReadFile5 = import_neverthrow54.ResultAsync.fromThrowable(
76339
76256
  async (filePath) => import_promises21.default.readFile(filePath, "utf8"),
76340
76257
  () => "READ_FAILED"
76341
76258
  );
@@ -76523,7 +76440,7 @@ function resolveXqaDirectory() {
76523
76440
  return result.value;
76524
76441
  }
76525
76442
  var program2 = new Command();
76526
- program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.14.2"}${false ? ` (dev build +${"5d87cb5"})` : ""}`);
76443
+ program2.name("xqa").description("AI-powered QA agent CLI").version(`${"1.15.0"}${false ? ` (dev build +${"befb4fb"})` : ""}`);
76527
76444
  program2.command("init").description("Initialize a new xqa project in the current directory").action(() => {
76528
76445
  runInitCommand();
76529
76446
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/xqa",
3
- "version": "1.14.2",
3
+ "version": "1.15.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -30,8 +30,8 @@
30
30
  "@qa-agents/eslint-config": "0.0.0",
31
31
  "@qa-agents/explorer": "0.0.0",
32
32
  "@qa-agents/mobile-ios": "0.0.0",
33
- "@qa-agents/pipeline": "0.0.0",
34
33
  "@qa-agents/shared": "0.0.0",
34
+ "@qa-agents/pipeline": "0.0.0",
35
35
  "@qa-agents/typescript-config": "0.0.0"
36
36
  },
37
37
  "dependencies": {