agent-qa 0.1.9 → 0.1.11

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.
package/dist/cli.cjs CHANGED
@@ -277,7 +277,7 @@ async function loadAuthPluginsForRawConfig(raw, configPath = "agent-qa.config.ya
277
277
  if (!Array.isArray(declarations) || declarations.length === 0) return;
278
278
  const { loadLLMAuthPlugins: loadLLMAuthPlugins2 } = await import("@vostride/agent-qa-core");
279
279
  await loadLLMAuthPlugins2(declarations, {
280
- baseDir: (0, import_node_path5.dirname)((0, import_node_path5.resolve)(configPath))
280
+ baseDir: (0, import_node_path6.dirname)((0, import_node_path6.resolve)(configPath))
281
281
  });
282
282
  }
283
283
  async function resolveNamedConfig(configName, configPath = "agent-qa.config.yaml") {
@@ -340,11 +340,11 @@ async function resolveCredentials(configName, llmConfig) {
340
340
  return {};
341
341
  }
342
342
  }
343
- var import_node_path5;
343
+ var import_node_path6;
344
344
  var init_llm_utils = __esm({
345
345
  "src/llm-utils.ts"() {
346
346
  "use strict";
347
- import_node_path5 = require("path");
347
+ import_node_path6 = require("path");
348
348
  init_model_defaults();
349
349
  }
350
350
  });
@@ -355,7 +355,7 @@ __export(cli_exports, {
355
355
  program: () => program
356
356
  });
357
357
  module.exports = __toCommonJS(cli_exports);
358
- var import_commander18 = require("commander");
358
+ var import_commander19 = require("commander");
359
359
 
360
360
  // src/index.ts
361
361
  var index_exports = {};
@@ -508,130 +508,17 @@ function createCacheCommand() {
508
508
  var VERSION = (0, import_agent_qa_core3.getAgentQaVersion)();
509
509
 
510
510
  // src/commands/init.ts
511
- var import_commander3 = require("commander");
511
+ var import_commander2 = require("commander");
512
512
  var import_node_fs = require("fs");
513
- var import_node_path4 = require("path");
514
- var import_node_child_process2 = require("child_process");
513
+ var import_node_path3 = require("path");
515
514
  var import_yaml3 = require("yaml");
516
- var import_picocolors4 = __toESM(require("picocolors"), 1);
515
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
517
516
  var import_select = __toESM(require("@inquirer/select"), 1);
518
517
  var import_input = __toESM(require("@inquirer/input"), 1);
519
518
  var import_checkbox = __toESM(require("@inquirer/checkbox"), 1);
520
-
521
- // src/commands/install-browsers.ts
522
- var import_node_child_process = require("child_process");
523
- var import_node_module = require("module");
524
- var import_node_path3 = require("path");
525
- var import_commander2 = require("commander");
526
- var import_picocolors3 = __toESM(require("picocolors"), 1);
527
- var import_meta = {};
528
- function selectedBrowserCount(selection) {
529
- return [selection.chromium, selection.firefox, selection.webkit].filter(Boolean).length;
530
- }
531
- function validateBrowserInstallSelection(selection) {
532
- if (!selection.all && selectedBrowserCount(selection) === 0) {
533
- return "Select at least one browser flag: --all, --chromium, --firefox, or --webkit.";
534
- }
535
- if (selection.all && selectedBrowserCount(selection) > 0) {
536
- return "Cannot combine --all with --chromium, --firefox, or --webkit.";
537
- }
538
- return null;
539
- }
540
- function buildBrowserInstallArgs(selection) {
541
- const args = [];
542
- if (selection.withDeps) args.push("--with-deps");
543
- if (selection.force) args.push("--force");
544
- if (!selection.all) {
545
- if (selection.chromium) args.push("chromium");
546
- if (selection.firefox) args.push("firefox");
547
- if (selection.webkit) args.push("webkit");
548
- }
549
- return args;
550
- }
551
- function formatInstallBrowsersRetryCommand(selection) {
552
- const args = ["agent-qa", "install-browsers"];
553
- if (selection.all) {
554
- args.push("--all");
555
- } else {
556
- if (selection.chromium) args.push("--chromium");
557
- if (selection.firefox) args.push("--firefox");
558
- if (selection.webkit) args.push("--webkit");
559
- }
560
- if (selection.withDeps) args.push("--with-deps");
561
- if (selection.force) args.push("--force");
562
- return args.join(" ");
563
- }
564
- function resolvePlaywrightCoreCli() {
565
- const require2 = (0, import_node_module.createRequire)(import_meta.url);
566
- const webEntry = require2.resolve("@vostride/agent-qa-web");
567
- const webRequire = (0, import_node_module.createRequire)(webEntry);
568
- const packagePath = webRequire.resolve("playwright-core/package.json");
569
- return (0, import_node_path3.join)((0, import_node_path3.dirname)(packagePath), "cli.js");
570
- }
571
- function normalizeSpawnStatus(result) {
572
- if (typeof result.status === "number") return result.status;
573
- return 1;
574
- }
575
- function runBrowserInstall(selection, deps = {}) {
576
- let cliPath;
577
- try {
578
- cliPath = (deps.resolveCli ?? resolvePlaywrightCoreCli)();
579
- } catch (error) {
580
- return { ok: false, status: 1, stage: "resolve", error };
581
- }
582
- const spawn = deps.spawn ?? import_node_child_process.spawnSync;
583
- const result = spawn(process.execPath, [cliPath, "install", ...buildBrowserInstallArgs(selection)], {
584
- stdio: "inherit"
585
- });
586
- if (result.error) {
587
- return { ok: false, status: normalizeSpawnStatus(result), stage: "spawn", error: result.error };
588
- }
589
- const status = normalizeSpawnStatus(result);
590
- return { ok: status === 0, status, stage: "installer" };
591
- }
592
- function printBrowserInstallFailure(selection, result) {
593
- if (result.stage === "resolve") {
594
- console.error(import_picocolors3.default.red("agent-qa could not find its bundled browser installer."));
595
- console.error(import_picocolors3.default.dim("Reinstall agent-qa dependencies, then retry:"));
596
- } else {
597
- console.error(import_picocolors3.default.red("Browser installation failed."));
598
- console.error(import_picocolors3.default.dim("Retry:"));
599
- }
600
- console.error(` ${import_picocolors3.default.cyan(formatInstallBrowsersRetryCommand(selection))}`);
601
- }
602
- function createInstallBrowsersCommand(deps = {}) {
603
- const cmd = new import_commander2.Command("install-browsers").description("Install browser support for agent-qa").option("--all", "install all Playwright-managed browsers for agent-qa").option("--chromium", "install Chromium browser support").option("--firefox", "install Firefox browser support").option("--webkit", "install WebKit browser support").option("--with-deps", "also install required system dependencies").option("--force", "force reinstall even if browser support already exists").action((opts) => {
604
- const selection = {
605
- all: !!opts.all,
606
- chromium: !!opts.chromium,
607
- firefox: !!opts.firefox,
608
- webkit: !!opts.webkit,
609
- withDeps: !!opts.withDeps,
610
- force: !!opts.force
611
- };
612
- const validationError = validateBrowserInstallSelection(selection);
613
- if (validationError) {
614
- console.error(import_picocolors3.default.red(`Error: ${validationError}`));
615
- console.error(cmd.helpInformation());
616
- process.exitCode = 1;
617
- return;
618
- }
619
- console.log(import_picocolors3.default.blue("Installing agent-qa browser support..."));
620
- const result = runBrowserInstall(selection, deps);
621
- if (result.ok) {
622
- console.log(import_picocolors3.default.green("\u2713 agent-qa browser support installed"));
623
- return;
624
- }
625
- printBrowserInstallFailure(selection, result);
626
- process.exitCode = result.status || 1;
627
- });
628
- return cmd;
629
- }
630
-
631
- // src/commands/init.ts
632
519
  var import_agent_qa_core4 = require("@vostride/agent-qa-core");
633
520
  init_model_defaults();
634
- var import_meta2 = {};
521
+ var import_meta = {};
635
522
  var INIT_CACHE_DIR = import_agent_qa_core4.DEFAULT_AGENT_QA_CACHE_DIR || ".agent-qa/cache";
636
523
  var INIT_RUNTIME_DIR = import_agent_qa_core4.DEFAULT_AGENT_QA_RUNTIME_DIR || ".agent-qa";
637
524
  var SUBSCRIPTION_AUTH_PACKAGE = "@vostride/agent-qa-subscription-auth";
@@ -695,9 +582,6 @@ function normalizePlatformSelection(platform) {
695
582
  return { capabilities: ["web", "mobile"], mobileRuntimes: ["ios"] };
696
583
  }
697
584
  }
698
- function hasMobile(platform) {
699
- return normalizePlatformSelection(platform).mobileRuntimes.length > 0;
700
- }
701
585
  function hasWeb(platform) {
702
586
  return normalizePlatformSelection(platform).capabilities.includes("web");
703
587
  }
@@ -851,7 +735,7 @@ function isSubscriptionProvider(provider) {
851
735
  }
852
736
  function readAgentQaPackageVersion() {
853
737
  try {
854
- const manifest = JSON.parse((0, import_node_fs.readFileSync)(new URL("../../package.json", import_meta2.url), "utf-8"));
738
+ const manifest = JSON.parse((0, import_node_fs.readFileSync)(new URL("../../package.json", import_meta.url), "utf-8"));
855
739
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : null;
856
740
  } catch {
857
741
  return null;
@@ -870,7 +754,7 @@ function configUsesSubscriptionAuth(config) {
870
754
  return authPlugins.some((plugin) => objectRecord(plugin)?.package === SUBSCRIPTION_AUTH_PACKAGE);
871
755
  }
872
756
  function upsertSubscriptionAuthPackageDependency(dir) {
873
- const packageJsonPath = (0, import_node_path4.join)(dir, PACKAGE_JSON_FILE);
757
+ const packageJsonPath = (0, import_node_path3.join)(dir, PACKAGE_JSON_FILE);
874
758
  const created = !(0, import_node_fs.existsSync)(packageJsonPath);
875
759
  const pkg = created ? { private: true } : JSON.parse((0, import_node_fs.readFileSync)(packageJsonPath, "utf-8"));
876
760
  if (!objectRecord(pkg)) {
@@ -1065,7 +949,7 @@ providers:
1065
949
  `;
1066
950
  }
1067
951
  function appendGitignore(dir) {
1068
- const gitignorePath = (0, import_node_path4.join)(dir, ".gitignore");
952
+ const gitignorePath = (0, import_node_path3.join)(dir, ".gitignore");
1069
953
  let existing = "";
1070
954
  if ((0, import_node_fs.existsSync)(gitignorePath)) {
1071
955
  existing = (0, import_node_fs.readFileSync)(gitignorePath, "utf-8");
@@ -1075,135 +959,38 @@ function appendGitignore(dir) {
1075
959
  const append = (existing.length > 0 && !existing.endsWith("\n") ? "\n" : "") + missing.join("\n") + "\n";
1076
960
  (0, import_node_fs.writeFileSync)(gitignorePath, existing + append);
1077
961
  }
1078
- function installPlaywrightBrowsers(platform) {
1079
- if (!hasWeb(platform)) return;
1080
- const selection = { chromium: true };
1081
- console.log(import_picocolors4.default.blue("Installing agent-qa browser support..."));
1082
- const result = runBrowserInstall(selection);
1083
- if (result.ok) {
1084
- console.log(import_picocolors4.default.green("\u2713 agent-qa browser support installed"));
1085
- } else {
1086
- console.log(import_picocolors4.default.yellow(`\u26A0 Browser installation failed. Run \`${formatInstallBrowsersRetryCommand(selection)}\` to retry.`));
1087
- }
1088
- }
1089
- function collectDriverNames(value, names) {
1090
- if (!value) return;
1091
- if (typeof value === "string") {
1092
- names.add(value);
1093
- return;
1094
- }
1095
- if (Array.isArray(value)) {
1096
- for (const item of value) collectDriverNames(item, names);
1097
- return;
1098
- }
1099
- if (typeof value !== "object") return;
1100
- const record = value;
1101
- if (typeof record.name === "string") names.add(record.name);
1102
- if (typeof record.driverName === "string") names.add(record.driverName);
1103
- if (typeof record.automationName === "string") names.add(record.automationName);
1104
- for (const [key, child] of Object.entries(record)) {
1105
- if (key !== "drivers" && key !== "installed" && key !== "name" && key !== "driverName" && typeof child === "object" && child !== null) {
1106
- names.add(key);
1107
- }
1108
- collectDriverNames(child, names);
1109
- }
1110
- }
1111
- function parseInstalledAppiumDrivers(output) {
1112
- const names = /* @__PURE__ */ new Set();
1113
- const trimmed = output.trim();
1114
- if (!trimmed) return names;
1115
- try {
1116
- collectDriverNames(JSON.parse(trimmed), names);
1117
- const normalizedNames = /* @__PURE__ */ new Set();
1118
- for (const name of names) {
1119
- const normalized = name.toLowerCase();
1120
- normalizedNames.add(normalized);
1121
- if (normalized.includes("uiautomator2")) normalizedNames.add("uiautomator2");
1122
- if (normalized.includes("xcuitest")) normalizedNames.add("xcuitest");
1123
- }
1124
- return normalizedNames;
1125
- } catch {
1126
- for (const line of trimmed.split("\n")) {
1127
- const normalized = line.toLowerCase();
1128
- if (normalized.includes("uiautomator2")) names.add("uiautomator2");
1129
- if (normalized.includes("xcuitest")) names.add("xcuitest");
1130
- }
1131
- return names;
1132
- }
1133
- }
1134
- function getInstalledAppiumDrivers() {
1135
- try {
1136
- const output = (0, import_node_child_process2.execSync)("appium driver list --installed --json", { stdio: "pipe", encoding: "utf-8" });
1137
- return parseInstalledAppiumDrivers(String(output));
1138
- } catch {
1139
- try {
1140
- const output = (0, import_node_child_process2.execSync)("appium driver list --installed", { stdio: "pipe", encoding: "utf-8" });
1141
- return parseInstalledAppiumDrivers(String(output));
1142
- } catch {
1143
- return /* @__PURE__ */ new Set();
1144
- }
1145
- }
1146
- }
1147
- function appiumDriverLabel(driver) {
1148
- return driver === "uiautomator2" ? "UiAutomator2" : "XCUITest";
1149
- }
1150
- function isAlreadyInstalledDriverError(err, driver) {
1151
- const details = [
1152
- err instanceof Error ? err.message : String(err),
1153
- err?.stdout,
1154
- err?.stderr
1155
- ].map(String).join("\n").toLowerCase();
1156
- return details.includes(`driver named "${driver}" is already installed`) || details.includes(`${driver}" is already installed`) || details.includes(`${driver} is already installed`);
1157
- }
1158
- function ensureAppiumDriver(driver, installedDrivers) {
1159
- const label = appiumDriverLabel(driver);
1160
- if (installedDrivers.has(driver)) {
1161
- console.log(import_picocolors4.default.green(`\u2713 ${label} driver already installed`));
1162
- return;
1163
- }
1164
- try {
1165
- (0, import_node_child_process2.execSync)(`appium driver install ${driver}`, { stdio: "inherit" });
1166
- console.log(import_picocolors4.default.green(`\u2713 ${label} driver installed`));
1167
- } catch (err) {
1168
- if (isAlreadyInstalledDriverError(err, driver)) {
1169
- console.log(import_picocolors4.default.green(`\u2713 ${label} driver already installed`));
1170
- return;
1171
- }
1172
- console.log(import_picocolors4.default.yellow(`\u26A0 ${label} driver installation failed. Run \`appium driver install ${driver}\` manually.`));
1173
- }
1174
- }
1175
- function installAppium(platform) {
1176
- if (!hasMobile(platform)) return;
1177
- const selection = normalizePlatformSelection(platform);
1178
- console.log(import_picocolors4.default.blue("Installing Appium..."));
1179
- let appiumInstalled = false;
1180
- try {
1181
- (0, import_node_child_process2.execSync)("appium --version", { stdio: "pipe" });
1182
- appiumInstalled = true;
1183
- console.log(import_picocolors4.default.green("\u2713 Appium already installed"));
1184
- } catch {
1185
- try {
1186
- (0, import_node_child_process2.execSync)("npm install -g appium", { stdio: "inherit" });
1187
- console.log(import_picocolors4.default.green("\u2713 Appium installed"));
1188
- appiumInstalled = true;
1189
- } catch {
1190
- console.log(import_picocolors4.default.yellow("\u26A0 Appium installation failed. Run `npm install -g appium` manually."));
1191
- }
1192
- }
1193
- if (!appiumInstalled) return;
1194
- const installedDrivers = getInstalledAppiumDrivers();
1195
- if (selection.mobileRuntimes.includes("android")) {
1196
- ensureAppiumDriver("uiautomator2", installedDrivers);
1197
- }
1198
- if (selection.mobileRuntimes.includes("ios")) {
1199
- ensureAppiumDriver("xcuitest", installedDrivers);
1200
- }
962
+ function browserSetupCommand(platform) {
963
+ return hasWeb(platform) ? "agent-qa install-browsers --chromium" : null;
964
+ }
965
+ function mobileDriverSetupCommand(platform) {
966
+ const runtimes = normalizePlatformSelection(platform).mobileRuntimes;
967
+ if (runtimes.length === 0) return null;
968
+ if (runtimes.includes("android") && runtimes.includes("ios")) return "agent-qa install-mobile-drivers --all";
969
+ if (runtimes.includes("android")) return "agent-qa install-mobile-drivers --android";
970
+ return "agent-qa install-mobile-drivers --ios";
971
+ }
972
+ function initNextSteps(platform) {
973
+ const steps = [];
974
+ const browserCommand = browserSetupCommand(platform);
975
+ const mobileCommand = mobileDriverSetupCommand(platform);
976
+ if (browserCommand) {
977
+ steps.push({ command: browserCommand, description: "Install browser support" });
978
+ }
979
+ if (mobileCommand) {
980
+ steps.push({ command: mobileCommand, description: "Install mobile driver support" });
981
+ }
982
+ steps.push(
983
+ { command: "agent-qa doctor", description: "Verify your environment" },
984
+ { command: "Edit tests/example-pass.yaml", description: "Write your first test" },
985
+ { command: "agent-qa run", description: "Run all tests" },
986
+ { command: "agent-qa dashboard", description: "View results in browser" }
987
+ );
988
+ return steps;
1201
989
  }
1202
990
  function createInitCommand() {
1203
- const cmd = new import_commander3.Command("init").description("Initialize a new agent-qa project").option("--dir <path>", "target directory", process.cwd()).option("--platform <type>", "platform to configure (web, android, ios, web+android, web+ios)").option("--skip-install", "skip auto-installation of browsers/Appium").option("--force", "overwrite existing files").action(async (opts) => {
1204
- const dir = (0, import_node_path4.resolve)(opts.dir);
991
+ const cmd = new import_commander2.Command("init").description("Initialize a new agent-qa project").option("--dir <path>", "target directory", process.cwd()).option("--platform <type>", "platform to configure (web, android, ios, web+android, web+ios)").option("--skip-install", "deprecated no-op; setup commands are shown after init").option("--force", "overwrite existing files").action(async (opts) => {
992
+ const dir = (0, import_node_path3.resolve)(opts.dir);
1205
993
  const force = opts.force ?? false;
1206
- const skipInstall = opts.skipInstall ?? false;
1207
994
  const isInteractive = !opts.platform;
1208
995
  let platform;
1209
996
  let provider = "anthropic-subscription";
@@ -1214,8 +1001,8 @@ function createInitCommand() {
1214
1001
  const capabilities = await (0, import_checkbox.default)({
1215
1002
  message: "What platform will you test?",
1216
1003
  choices: [
1217
- { value: "web", name: "Web", description: "Test web apps with Playwright" },
1218
- { value: "mobile", name: "Mobile", description: "Test Android or iOS apps with Appium" }
1004
+ { value: "web", name: "Web", description: "Test web apps with Playwright", checked: true },
1005
+ { value: "mobile", name: "Mobile", description: "Test Android or iOS apps with Appium", checked: false }
1219
1006
  ],
1220
1007
  required: true,
1221
1008
  validate: (value) => value.length > 0 || "Select at least one platform."
@@ -1231,7 +1018,7 @@ function createInitCommand() {
1231
1018
  }) : [];
1232
1019
  platform = { capabilities, mobileRuntimes };
1233
1020
  const llmSetup = await (0, import_checkbox.default)({
1234
- message: `Subscription auth ${import_picocolors4.default.dim("(optional)")}`,
1021
+ message: `Subscription auth ${import_picocolors3.default.dim("(optional)")}`,
1235
1022
  choices: LLM_SETUP_CHOICES,
1236
1023
  required: true,
1237
1024
  validate: (value) => {
@@ -1266,12 +1053,12 @@ function createInitCommand() {
1266
1053
  provider = "anthropic-subscription";
1267
1054
  model = DEFAULT_ANTHROPIC_MODEL;
1268
1055
  }
1269
- const configPath = (0, import_node_path4.join)(dir, "agent-qa.config.yaml");
1056
+ const configPath = (0, import_node_path3.join)(dir, "agent-qa.config.yaml");
1270
1057
  if ((0, import_node_fs.existsSync)(configPath) && !force) {
1271
- console.log(import_picocolors4.default.yellow(`Config file already exists at ${configPath}. Use --force to overwrite.`));
1058
+ console.log(import_picocolors3.default.yellow(`Config file already exists at ${configPath}. Use --force to overwrite.`));
1272
1059
  return;
1273
1060
  }
1274
- const testsDir = (0, import_node_path4.join)(dir, "tests");
1061
+ const testsDir = (0, import_node_path3.join)(dir, "tests");
1275
1062
  if (!(0, import_node_fs.existsSync)(testsDir)) {
1276
1063
  (0, import_node_fs.mkdirSync)(testsDir, { recursive: true });
1277
1064
  }
@@ -1280,119 +1067,111 @@ function createInitCommand() {
1280
1067
  const yamlStr = (0, import_yaml3.stringify)(config);
1281
1068
  const configContent = addYamlComments(yamlStr);
1282
1069
  (0, import_node_fs.writeFileSync)(configPath, configContent);
1283
- console.log(import_picocolors4.default.green(`\u2713 Created ${configPath}`));
1070
+ console.log(import_picocolors3.default.green(`\u2713 Created ${configPath}`));
1284
1071
  const subscriptionAuthDependency = usesSubscriptionAuth ? upsertSubscriptionAuthPackageDependency(dir) : null;
1285
1072
  if (subscriptionAuthDependency) {
1286
1073
  const action = subscriptionAuthDependency.created ? "Created" : "Updated";
1287
- console.log(import_picocolors4.default.green(`\u2713 ${action} ${subscriptionAuthDependency.path}`));
1074
+ console.log(import_picocolors3.default.green(`\u2713 ${action} ${subscriptionAuthDependency.path}`));
1288
1075
  }
1289
- const secretsPath = (0, import_node_path4.join)(dir, DEFAULT_SECRETS_FILE);
1076
+ const secretsPath = (0, import_node_path3.join)(dir, DEFAULT_SECRETS_FILE);
1290
1077
  if (force || !(0, import_node_fs.existsSync)(secretsPath)) {
1291
1078
  (0, import_node_fs.writeFileSync)(secretsPath, "");
1292
- console.log(import_picocolors4.default.green(`\u2713 Created ${secretsPath}`));
1079
+ console.log(import_picocolors3.default.green(`\u2713 Created ${secretsPath}`));
1293
1080
  }
1294
- const envPath = (0, import_node_path4.join)(dir, DEFAULT_ENV_FILE);
1081
+ const envPath = (0, import_node_path3.join)(dir, DEFAULT_ENV_FILE);
1295
1082
  if (force || !(0, import_node_fs.existsSync)(envPath)) {
1296
1083
  (0, import_node_fs.writeFileSync)(envPath, "");
1297
- console.log(import_picocolors4.default.green(`\u2713 Created ${envPath}`));
1084
+ console.log(import_picocolors3.default.green(`\u2713 Created ${envPath}`));
1298
1085
  }
1299
- const hooksPath = (0, import_node_path4.join)(dir, DEFAULT_HOOKS_FILE);
1086
+ const hooksPath = (0, import_node_path3.join)(dir, DEFAULT_HOOKS_FILE);
1300
1087
  if (force || !(0, import_node_fs.existsSync)(hooksPath)) {
1301
1088
  (0, import_node_fs.writeFileSync)(hooksPath, buildHooksFile(platform));
1302
- console.log(import_picocolors4.default.green(`\u2713 Created ${hooksPath}`));
1089
+ console.log(import_picocolors3.default.green(`\u2713 Created ${hooksPath}`));
1303
1090
  }
1304
- const agentRulesPath = (0, import_node_path4.join)(dir, DEFAULT_AGENT_RULES_FILE);
1091
+ const agentRulesPath = (0, import_node_path3.join)(dir, DEFAULT_AGENT_RULES_FILE);
1305
1092
  if (force || !(0, import_node_fs.existsSync)(agentRulesPath)) {
1306
1093
  (0, import_node_fs.writeFileSync)(agentRulesPath, "# agent-qa Rules\n");
1307
- console.log(import_picocolors4.default.green(`\u2713 Created ${agentRulesPath}`));
1094
+ console.log(import_picocolors3.default.green(`\u2713 Created ${agentRulesPath}`));
1308
1095
  }
1309
- const localConfigPath = (0, import_node_path4.join)(dir, DEFAULT_LOCAL_CONFIG_FILE);
1096
+ const localConfigPath = (0, import_node_path3.join)(dir, DEFAULT_LOCAL_CONFIG_FILE);
1310
1097
  if (force || !(0, import_node_fs.existsSync)(localConfigPath)) {
1311
1098
  (0, import_node_fs.writeFileSync)(localConfigPath, buildLocalConfigTemplate());
1312
- console.log(import_picocolors4.default.green(`\u2713 Created ${localConfigPath}`));
1099
+ console.log(import_picocolors3.default.green(`\u2713 Created ${localConfigPath}`));
1313
1100
  }
1314
- const examplePassPath = (0, import_node_path4.join)(testsDir, "example-pass.yaml");
1101
+ const examplePassPath = (0, import_node_path3.join)(testsDir, "example-pass.yaml");
1315
1102
  (0, import_node_fs.writeFileSync)(examplePassPath, buildExampleTest(platform, "pass"));
1316
- console.log(import_picocolors4.default.green(`\u2713 Created ${examplePassPath}`));
1317
- const exampleFailPath = (0, import_node_path4.join)(testsDir, "example-fail.yaml");
1103
+ console.log(import_picocolors3.default.green(`\u2713 Created ${examplePassPath}`));
1104
+ const exampleFailPath = (0, import_node_path3.join)(testsDir, "example-fail.yaml");
1318
1105
  (0, import_node_fs.writeFileSync)(exampleFailPath, buildExampleTest(platform, "fail"));
1319
- console.log(import_picocolors4.default.green(`\u2713 Created ${exampleFailPath}`));
1106
+ console.log(import_picocolors3.default.green(`\u2713 Created ${exampleFailPath}`));
1320
1107
  if (hasWeb(platform)) {
1321
- const suitesDir = (0, import_node_path4.join)(dir, "suites");
1108
+ const suitesDir = (0, import_node_path3.join)(dir, "suites");
1322
1109
  if (!(0, import_node_fs.existsSync)(suitesDir)) {
1323
1110
  (0, import_node_fs.mkdirSync)(suitesDir, { recursive: true });
1324
1111
  }
1325
- const scriptsDir = (0, import_node_path4.join)(dir, "scripts");
1112
+ const scriptsDir = (0, import_node_path3.join)(dir, "scripts");
1326
1113
  if (!(0, import_node_fs.existsSync)(scriptsDir)) {
1327
1114
  (0, import_node_fs.mkdirSync)(scriptsDir, { recursive: true });
1328
1115
  }
1329
- const automationTestsDir = (0, import_node_path4.join)(testsDir, "automation-exercise");
1116
+ const automationTestsDir = (0, import_node_path3.join)(testsDir, "automation-exercise");
1330
1117
  if (!(0, import_node_fs.existsSync)(automationTestsDir)) {
1331
1118
  (0, import_node_fs.mkdirSync)(automationTestsDir, { recursive: true });
1332
1119
  }
1333
1120
  for (const test of AUTOMATION_EXERCISE_TESTS) {
1334
- const testPath = (0, import_node_path4.join)(automationTestsDir, test.fileName);
1121
+ const testPath = (0, import_node_path3.join)(automationTestsDir, test.fileName);
1335
1122
  (0, import_node_fs.writeFileSync)(testPath, buildAutomationExerciseTest(test));
1336
- console.log(import_picocolors4.default.green(`\u2713 Created ${testPath}`));
1123
+ console.log(import_picocolors3.default.green(`\u2713 Created ${testPath}`));
1337
1124
  }
1338
- const automationSuitePath = (0, import_node_path4.join)(suitesDir, "automation-exercise.suite.yaml");
1125
+ const automationSuitePath = (0, import_node_path3.join)(suitesDir, "automation-exercise.suite.yaml");
1339
1126
  (0, import_node_fs.writeFileSync)(automationSuitePath, buildAutomationExerciseSuite());
1340
- console.log(import_picocolors4.default.green(`\u2713 Created ${automationSuitePath}`));
1341
- const hnScriptPath = (0, import_node_path4.join)(scriptsDir, "fetch-hn-top-story.mjs");
1127
+ console.log(import_picocolors3.default.green(`\u2713 Created ${automationSuitePath}`));
1128
+ const hnScriptPath = (0, import_node_path3.join)(scriptsDir, "fetch-hn-top-story.mjs");
1342
1129
  (0, import_node_fs.writeFileSync)(hnScriptPath, buildHackerNewsHookScript());
1343
- console.log(import_picocolors4.default.green(`\u2713 Created ${hnScriptPath}`));
1344
- const hnTestPath = (0, import_node_path4.join)(testsDir, "hacker-news-top-story.yaml");
1130
+ console.log(import_picocolors3.default.green(`\u2713 Created ${hnScriptPath}`));
1131
+ const hnTestPath = (0, import_node_path3.join)(testsDir, "hacker-news-top-story.yaml");
1345
1132
  (0, import_node_fs.writeFileSync)(hnTestPath, buildHackerNewsTopStoryTest());
1346
- console.log(import_picocolors4.default.green(`\u2713 Created ${hnTestPath}`));
1133
+ console.log(import_picocolors3.default.green(`\u2713 Created ${hnTestPath}`));
1347
1134
  }
1348
1135
  appendGitignore(dir);
1349
- console.log(import_picocolors4.default.green("\u2713 Updated .gitignore"));
1350
- if (!skipInstall) {
1351
- installPlaywrightBrowsers(platform);
1352
- installAppium(platform);
1353
- }
1136
+ console.log(import_picocolors3.default.green("\u2713 Updated .gitignore"));
1354
1137
  console.log("");
1355
- console.log(import_picocolors4.default.green(import_picocolors4.default.bold("\u2713 agent-qa project initialized!")));
1138
+ console.log(import_picocolors3.default.green(import_picocolors3.default.bold("\u2713 agent-qa project initialized!")));
1356
1139
  console.log("");
1357
1140
  console.log(" Created:");
1358
- console.log(` ${import_picocolors4.default.dim("\u2022")} agent-qa.config.yaml`);
1141
+ console.log(` ${import_picocolors3.default.dim("\u2022")} agent-qa.config.yaml`);
1359
1142
  if (subscriptionAuthDependency) {
1360
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${PACKAGE_JSON_FILE} dependency for ${SUBSCRIPTION_AUTH_PACKAGE}@${subscriptionAuthDependency.range}`);
1361
- }
1362
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${DEFAULT_HOOKS_FILE}`);
1363
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${DEFAULT_AGENT_RULES_FILE}`);
1364
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${DEFAULT_ENV_FILE}`);
1365
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${DEFAULT_SECRETS_FILE}`);
1366
- console.log(` ${import_picocolors4.default.dim("\u2022")} ${DEFAULT_LOCAL_CONFIG_FILE}`);
1367
- console.log(` ${import_picocolors4.default.dim("\u2022")} tests/example-pass.yaml`);
1368
- console.log(` ${import_picocolors4.default.dim("\u2022")} tests/example-fail.yaml`);
1143
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${PACKAGE_JSON_FILE} dependency for ${SUBSCRIPTION_AUTH_PACKAGE}@${subscriptionAuthDependency.range}`);
1144
+ }
1145
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${DEFAULT_HOOKS_FILE}`);
1146
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${DEFAULT_AGENT_RULES_FILE}`);
1147
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${DEFAULT_ENV_FILE}`);
1148
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${DEFAULT_SECRETS_FILE}`);
1149
+ console.log(` ${import_picocolors3.default.dim("\u2022")} ${DEFAULT_LOCAL_CONFIG_FILE}`);
1150
+ console.log(` ${import_picocolors3.default.dim("\u2022")} tests/example-pass.yaml`);
1151
+ console.log(` ${import_picocolors3.default.dim("\u2022")} tests/example-fail.yaml`);
1369
1152
  if (hasWeb(platform)) {
1370
- console.log(` ${import_picocolors4.default.dim("\u2022")} tests/automation-exercise/*.yaml`);
1371
- console.log(` ${import_picocolors4.default.dim("\u2022")} tests/hacker-news-top-story.yaml`);
1372
- console.log(` ${import_picocolors4.default.dim("\u2022")} suites/automation-exercise.suite.yaml`);
1373
- console.log(` ${import_picocolors4.default.dim("\u2022")} scripts/fetch-hn-top-story.mjs`);
1153
+ console.log(` ${import_picocolors3.default.dim("\u2022")} tests/automation-exercise/*.yaml`);
1154
+ console.log(` ${import_picocolors3.default.dim("\u2022")} tests/hacker-news-top-story.yaml`);
1155
+ console.log(` ${import_picocolors3.default.dim("\u2022")} suites/automation-exercise.suite.yaml`);
1156
+ console.log(` ${import_picocolors3.default.dim("\u2022")} scripts/fetch-hn-top-story.mjs`);
1374
1157
  }
1375
- console.log(` ${import_picocolors4.default.dim("\u2022")} .gitignore entries`);
1158
+ console.log(` ${import_picocolors3.default.dim("\u2022")} .gitignore entries`);
1376
1159
  console.log("");
1377
1160
  console.log(" Next steps:");
1378
- console.log(` 1. ${import_picocolors4.default.cyan("agent-qa doctor")} Verify your environment`);
1379
- console.log(` 2. Edit ${import_picocolors4.default.cyan("tests/example-pass.yaml")} with your first test`);
1380
- console.log(` 3. ${import_picocolors4.default.cyan("agent-qa run")} Run all tests`);
1381
- console.log(` 4. ${import_picocolors4.default.cyan("agent-qa dashboard")} View results in browser`);
1382
- if (skipInstall && hasWeb(platform)) {
1383
- console.log(` ${import_picocolors4.default.dim("\u2022")} Install browser support with ${import_picocolors4.default.cyan("agent-qa install-browsers --chromium")}`);
1161
+ for (const [index, step] of initNextSteps(platform).entries()) {
1162
+ console.log(` ${index + 1}. ${import_picocolors3.default.cyan(step.command)} ${step.description}`);
1384
1163
  }
1385
1164
  const generatedLLMs = config.registry?.llms ?? [];
1386
1165
  const subscriptionLLMs = generatedLLMs.filter((llm) => isSubscriptionProvider(String(llm.provider)));
1387
1166
  if (subscriptionLLMs.length === 0) {
1388
- console.log(` ${import_picocolors4.default.dim("\u2022")} Save credentials with ${import_picocolors4.default.cyan("agent-qa auth set --config default --type api-key")}`);
1167
+ console.log(` ${import_picocolors3.default.dim("\u2022")} Save credentials with ${import_picocolors3.default.cyan("agent-qa auth set --config default --type api-key")}`);
1389
1168
  if (provider === "anthropic-compatible") {
1390
- console.log(` ${import_picocolors4.default.dim("\u2022")} Bearer tokens use ${import_picocolors4.default.cyan("agent-qa auth set --config default --type bearer-token")}`);
1169
+ console.log(` ${import_picocolors3.default.dim("\u2022")} Bearer tokens use ${import_picocolors3.default.cyan("agent-qa auth set --config default --type bearer-token")}`);
1391
1170
  }
1392
1171
  } else {
1393
- console.log(` ${import_picocolors4.default.dim("\u2022")} Fetch ${SUBSCRIPTION_AUTH_PACKAGE} with your package manager install command`);
1172
+ console.log(` ${import_picocolors3.default.dim("\u2022")} Fetch ${SUBSCRIPTION_AUTH_PACKAGE} with your package manager install command`);
1394
1173
  for (const llm of subscriptionLLMs) {
1395
- console.log(` ${import_picocolors4.default.dim("\u2022")} Authenticate ${import_picocolors4.default.cyan(String(llm.name))} from ${import_picocolors4.default.cyan("agent-qa dashboard")}`);
1174
+ console.log(` ${import_picocolors3.default.dim("\u2022")} Authenticate ${import_picocolors3.default.cyan(String(llm.name))} from ${import_picocolors3.default.cyan("agent-qa dashboard")}`);
1396
1175
  }
1397
1176
  }
1398
1177
  console.log("");
@@ -1400,20 +1179,369 @@ function createInitCommand() {
1400
1179
  return cmd;
1401
1180
  }
1402
1181
 
1403
- // src/commands/doctor.ts
1182
+ // src/commands/install-browsers.ts
1183
+ var import_node_child_process = require("child_process");
1184
+ var import_node_module = require("module");
1185
+ var import_node_path4 = require("path");
1186
+ var import_commander3 = require("commander");
1187
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
1188
+ var import_meta2 = {};
1189
+ function selectedBrowserCount(selection) {
1190
+ return [selection.chromium, selection.firefox, selection.webkit].filter(Boolean).length;
1191
+ }
1192
+ function validateBrowserInstallSelection(selection) {
1193
+ if (!selection.all && selectedBrowserCount(selection) === 0) {
1194
+ return "Select at least one browser flag: --all, --chromium, --firefox, or --webkit.";
1195
+ }
1196
+ if (selection.all && selectedBrowserCount(selection) > 0) {
1197
+ return "Cannot combine --all with --chromium, --firefox, or --webkit.";
1198
+ }
1199
+ return null;
1200
+ }
1201
+ function buildBrowserInstallArgs(selection) {
1202
+ const args = [];
1203
+ if (selection.withDeps) args.push("--with-deps");
1204
+ if (selection.force) args.push("--force");
1205
+ if (!selection.all) {
1206
+ if (selection.chromium) args.push("chromium");
1207
+ if (selection.firefox) args.push("firefox");
1208
+ if (selection.webkit) args.push("webkit");
1209
+ }
1210
+ return args;
1211
+ }
1212
+ function formatInstallBrowsersRetryCommand(selection) {
1213
+ const args = ["agent-qa", "install-browsers"];
1214
+ if (selection.all) {
1215
+ args.push("--all");
1216
+ } else {
1217
+ if (selection.chromium) args.push("--chromium");
1218
+ if (selection.firefox) args.push("--firefox");
1219
+ if (selection.webkit) args.push("--webkit");
1220
+ }
1221
+ if (selection.withDeps) args.push("--with-deps");
1222
+ if (selection.force) args.push("--force");
1223
+ return args.join(" ");
1224
+ }
1225
+ function resolvePlaywrightCoreCli() {
1226
+ const require2 = (0, import_node_module.createRequire)(import_meta2.url);
1227
+ const webEntry = require2.resolve("@vostride/agent-qa-web");
1228
+ const webRequire = (0, import_node_module.createRequire)(webEntry);
1229
+ const packagePath = webRequire.resolve("playwright-core/package.json");
1230
+ return (0, import_node_path4.join)((0, import_node_path4.dirname)(packagePath), "cli.js");
1231
+ }
1232
+ function normalizeSpawnStatus(result) {
1233
+ if (typeof result.status === "number") return result.status;
1234
+ return 1;
1235
+ }
1236
+ function runBrowserInstall(selection, deps = {}) {
1237
+ let cliPath;
1238
+ try {
1239
+ cliPath = (deps.resolveCli ?? resolvePlaywrightCoreCli)();
1240
+ } catch (error) {
1241
+ return { ok: false, status: 1, stage: "resolve", error };
1242
+ }
1243
+ const spawn = deps.spawn ?? import_node_child_process.spawnSync;
1244
+ const result = spawn(process.execPath, [cliPath, "install", ...buildBrowserInstallArgs(selection)], {
1245
+ stdio: "inherit"
1246
+ });
1247
+ if (result.error) {
1248
+ return { ok: false, status: normalizeSpawnStatus(result), stage: "spawn", error: result.error };
1249
+ }
1250
+ const status = normalizeSpawnStatus(result);
1251
+ return { ok: status === 0, status, stage: "installer" };
1252
+ }
1253
+ function printBrowserInstallFailure(selection, result) {
1254
+ if (result.stage === "resolve") {
1255
+ console.error(import_picocolors4.default.red("agent-qa could not find its bundled browser installer."));
1256
+ console.error(import_picocolors4.default.dim("Reinstall agent-qa dependencies, then retry:"));
1257
+ } else {
1258
+ console.error(import_picocolors4.default.red("Browser installation failed."));
1259
+ console.error(import_picocolors4.default.dim("Retry:"));
1260
+ }
1261
+ console.error(` ${import_picocolors4.default.cyan(formatInstallBrowsersRetryCommand(selection))}`);
1262
+ }
1263
+ function createInstallBrowsersCommand(deps = {}) {
1264
+ const cmd = new import_commander3.Command("install-browsers").description("Install browser support for agent-qa").option("--all", "install all Playwright-managed browsers for agent-qa").option("--chromium", "install Chromium browser support").option("--firefox", "install Firefox browser support").option("--webkit", "install WebKit browser support").option("--with-deps", "also install required system dependencies").option("--force", "force reinstall even if browser support already exists").action((opts) => {
1265
+ const selection = {
1266
+ all: !!opts.all,
1267
+ chromium: !!opts.chromium,
1268
+ firefox: !!opts.firefox,
1269
+ webkit: !!opts.webkit,
1270
+ withDeps: !!opts.withDeps,
1271
+ force: !!opts.force
1272
+ };
1273
+ const validationError = validateBrowserInstallSelection(selection);
1274
+ if (validationError) {
1275
+ console.error(import_picocolors4.default.red(`Error: ${validationError}`));
1276
+ console.error(cmd.helpInformation());
1277
+ process.exitCode = 1;
1278
+ return;
1279
+ }
1280
+ console.log(import_picocolors4.default.blue("Installing agent-qa browser support..."));
1281
+ const result = runBrowserInstall(selection, deps);
1282
+ if (result.ok) {
1283
+ console.log(import_picocolors4.default.green("\u2713 agent-qa browser support installed"));
1284
+ return;
1285
+ }
1286
+ printBrowserInstallFailure(selection, result);
1287
+ process.exitCode = result.status || 1;
1288
+ });
1289
+ return cmd;
1290
+ }
1291
+
1292
+ // src/commands/install-mobile-drivers.ts
1293
+ var import_node_child_process2 = require("child_process");
1294
+ var import_node_path5 = require("path");
1404
1295
  var import_commander4 = require("commander");
1296
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
1297
+ var import_agent_qa_core5 = require("@vostride/agent-qa-core");
1298
+ var MOBILE_DRIVERS = [
1299
+ { target: "android", driver: "uiautomator2", label: "UiAutomator2" },
1300
+ { target: "ios", driver: "xcuitest", label: "XCUITest" }
1301
+ ];
1302
+ function selectedTargetCount(selection) {
1303
+ return [selection.android, selection.ios].filter(Boolean).length;
1304
+ }
1305
+ function selectedDrivers(selection) {
1306
+ if (selection.all) return MOBILE_DRIVERS;
1307
+ return MOBILE_DRIVERS.filter((driver) => selection[driver.target]);
1308
+ }
1309
+ function validateMobileDriverInstallSelection(selection) {
1310
+ if (!selection.all && selectedTargetCount(selection) === 0) {
1311
+ return "Select at least one mobile platform flag: --all, --android, or --ios.";
1312
+ }
1313
+ if (selection.all && selectedTargetCount(selection) > 0) {
1314
+ return "Cannot combine --all with --android or --ios.";
1315
+ }
1316
+ if (selection.unsafe && !selection.update) {
1317
+ return "Cannot use --unsafe without --update.";
1318
+ }
1319
+ return null;
1320
+ }
1321
+ function formatInstallMobileDriversRetryCommand(selection) {
1322
+ const args = ["agent-qa", "install-mobile-drivers"];
1323
+ if (selection.all) {
1324
+ args.push("--all");
1325
+ } else {
1326
+ if (selection.android) args.push("--android");
1327
+ if (selection.ios) args.push("--ios");
1328
+ }
1329
+ if (selection.update) args.push("--update");
1330
+ if (selection.unsafe) args.push("--unsafe");
1331
+ return args.join(" ");
1332
+ }
1333
+ function collectDriverNames(value, names) {
1334
+ if (!value) return;
1335
+ if (typeof value === "string") {
1336
+ names.add(value);
1337
+ return;
1338
+ }
1339
+ if (Array.isArray(value)) {
1340
+ for (const item of value) collectDriverNames(item, names);
1341
+ return;
1342
+ }
1343
+ if (typeof value !== "object") return;
1344
+ const record = value;
1345
+ if (typeof record.name === "string") names.add(record.name);
1346
+ if (typeof record.driverName === "string") names.add(record.driverName);
1347
+ if (typeof record.automationName === "string") names.add(record.automationName);
1348
+ for (const [key, child] of Object.entries(record)) {
1349
+ if (key !== "drivers" && key !== "installed" && key !== "name" && key !== "driverName" && typeof child === "object" && child !== null) {
1350
+ names.add(key);
1351
+ }
1352
+ collectDriverNames(child, names);
1353
+ }
1354
+ }
1355
+ function parseInstalledAppiumDrivers(output) {
1356
+ const names = /* @__PURE__ */ new Set();
1357
+ const trimmed = output.trim();
1358
+ if (!trimmed) return names;
1359
+ try {
1360
+ collectDriverNames(JSON.parse(trimmed), names);
1361
+ const normalizedNames = /* @__PURE__ */ new Set();
1362
+ for (const name of names) {
1363
+ const normalized = name.toLowerCase();
1364
+ normalizedNames.add(normalized);
1365
+ if (normalized.includes("uiautomator2")) normalizedNames.add("uiautomator2");
1366
+ if (normalized.includes("xcuitest")) normalizedNames.add("xcuitest");
1367
+ }
1368
+ return normalizedNames;
1369
+ } catch {
1370
+ for (const line of trimmed.split("\n")) {
1371
+ const normalized = line.toLowerCase();
1372
+ if (normalized.includes("uiautomator2")) names.add("uiautomator2");
1373
+ if (normalized.includes("xcuitest")) names.add("xcuitest");
1374
+ }
1375
+ return names;
1376
+ }
1377
+ }
1378
+ function errorStatus(error) {
1379
+ const status = error?.status;
1380
+ return typeof status === "number" ? status : 1;
1381
+ }
1382
+ function isMissingExecutableError(error) {
1383
+ return error?.code === "ENOENT";
1384
+ }
1385
+ function isAlreadyInstalledDriverError(error, driver) {
1386
+ const details = [
1387
+ error instanceof Error ? error.message : String(error),
1388
+ error?.stdout,
1389
+ error?.stderr
1390
+ ].map(String).join("\n").toLowerCase();
1391
+ return details.includes(`driver named "${driver}" is already installed`) || details.includes(`${driver}" is already installed`) || details.includes(`${driver} is already installed`);
1392
+ }
1393
+ function getInstalledAppiumDrivers(appiumCommand, execFile) {
1394
+ try {
1395
+ const output = execFile(appiumCommand, ["driver", "list", "--installed", "--json"], { stdio: "pipe", encoding: "utf-8" });
1396
+ return { ok: true, drivers: parseInstalledAppiumDrivers(String(output)) };
1397
+ } catch {
1398
+ try {
1399
+ const output = execFile(appiumCommand, ["driver", "list", "--installed"], { stdio: "pipe", encoding: "utf-8" });
1400
+ return { ok: true, drivers: parseInstalledAppiumDrivers(String(output)) };
1401
+ } catch (error) {
1402
+ return { ok: false, error, status: errorStatus(error) };
1403
+ }
1404
+ }
1405
+ }
1406
+ function driverOperationArgs(driver, selection, installed) {
1407
+ if (installed && !selection.update) {
1408
+ return { action: "reuse", args: null };
1409
+ }
1410
+ if (installed && selection.update) {
1411
+ const args = ["driver", "update", driver];
1412
+ if (selection.unsafe) args.push("--unsafe");
1413
+ return { action: "update", args };
1414
+ }
1415
+ return { action: "install", args: ["driver", "install", driver] };
1416
+ }
1417
+ function runMobileDriverInstall(selection, deps = {}) {
1418
+ const resolveAppium = deps.resolveAppium ?? import_agent_qa_core5.resolveAppiumExecutable;
1419
+ const execFile = deps.execFile ?? import_node_child_process2.execFileSync;
1420
+ const events = [];
1421
+ let appium;
1422
+ try {
1423
+ appium = resolveAppium({ cwd: selection.cwd ?? process.cwd() });
1424
+ } catch (error) {
1425
+ return { ok: false, status: 1, stage: "resolve", events, error };
1426
+ }
1427
+ try {
1428
+ execFile(appium.command, ["--version"], { stdio: "pipe" });
1429
+ } catch (error) {
1430
+ return { ok: false, status: errorStatus(error), stage: "check", appium, events, error };
1431
+ }
1432
+ const installedResult = getInstalledAppiumDrivers(appium.command, execFile);
1433
+ if (!installedResult.ok) {
1434
+ return { ok: false, status: installedResult.status, stage: "list", appium, events, error: installedResult.error };
1435
+ }
1436
+ let status = 0;
1437
+ for (const definition of selectedDrivers(selection)) {
1438
+ const installed = installedResult.drivers.has(definition.driver);
1439
+ const operation = driverOperationArgs(definition.driver, selection, installed);
1440
+ if (!operation.args) {
1441
+ events.push({ driver: definition.driver, label: definition.label, action: operation.action, ok: true });
1442
+ continue;
1443
+ }
1444
+ try {
1445
+ execFile(appium.command, operation.args, { stdio: "inherit" });
1446
+ events.push({ driver: definition.driver, label: definition.label, action: operation.action, ok: true });
1447
+ } catch (error) {
1448
+ if (operation.action === "install" && isAlreadyInstalledDriverError(error, definition.driver)) {
1449
+ events.push({ driver: definition.driver, label: definition.label, action: "reuse", ok: true });
1450
+ continue;
1451
+ }
1452
+ status = status || errorStatus(error);
1453
+ events.push({ driver: definition.driver, label: definition.label, action: operation.action, ok: false, error });
1454
+ }
1455
+ }
1456
+ return { ok: status === 0, status, stage: "driver", appium, events };
1457
+ }
1458
+ function resolveCommandCwd(command) {
1459
+ const configPath = command.parent?.opts().config ?? "agent-qa.config.yaml";
1460
+ return (0, import_node_path5.dirname)((0, import_node_path5.resolve)(configPath));
1461
+ }
1462
+ function printMobileDriverInstallResult(selection, result) {
1463
+ if (result.stage === "check" && isMissingExecutableError(result.error)) {
1464
+ console.error(import_picocolors5.default.red("Appium not found."));
1465
+ console.error(import_picocolors5.default.dim((0, import_agent_qa_core5.formatAppiumInstallGuidance)()));
1466
+ return;
1467
+ }
1468
+ if (result.stage === "resolve") {
1469
+ console.error(import_picocolors5.default.red("agent-qa could not resolve an Appium executable."));
1470
+ console.error(import_picocolors5.default.dim((0, import_agent_qa_core5.formatAppiumInstallGuidance)()));
1471
+ return;
1472
+ }
1473
+ if (result.stage === "check") {
1474
+ console.error(import_picocolors5.default.red("Could not run Appium."));
1475
+ console.error(import_picocolors5.default.dim((0, import_agent_qa_core5.formatAppiumInstallGuidance)()));
1476
+ return;
1477
+ }
1478
+ if (result.stage === "list") {
1479
+ console.error(import_picocolors5.default.red("Could not inspect installed Appium drivers."));
1480
+ console.error(import_picocolors5.default.dim("Retry:"));
1481
+ console.error(` ${import_picocolors5.default.cyan(formatInstallMobileDriversRetryCommand(selection))}`);
1482
+ return;
1483
+ }
1484
+ for (const event of result.events) {
1485
+ if (event.ok && event.action === "reuse") {
1486
+ console.log(import_picocolors5.default.green(`\u2713 ${event.label} driver already installed`));
1487
+ } else if (event.ok && event.action === "install") {
1488
+ console.log(import_picocolors5.default.green(`\u2713 ${event.label} driver installed`));
1489
+ } else if (event.ok && event.action === "update") {
1490
+ console.log(import_picocolors5.default.green(`\u2713 ${event.label} driver updated`));
1491
+ } else {
1492
+ const action = event.action === "update" ? "update" : "installation";
1493
+ console.error(import_picocolors5.default.red(`\u2717 ${event.label} driver ${action} failed`));
1494
+ }
1495
+ }
1496
+ if (!result.ok) {
1497
+ console.error(import_picocolors5.default.dim("Retry:"));
1498
+ console.error(` ${import_picocolors5.default.cyan(formatInstallMobileDriversRetryCommand(selection))}`);
1499
+ return;
1500
+ }
1501
+ console.log(import_picocolors5.default.green("\u2713 agent-qa mobile driver support ready"));
1502
+ }
1503
+ function createInstallMobileDriversCommand(deps = {}) {
1504
+ const cmd = new import_commander4.Command("install-mobile-drivers").description("Install Appium mobile drivers for agent-qa").option("--all", "install Android and iOS Appium drivers").option("--android", "install the UiAutomator2 Appium driver").option("--ios", "install the XCUITest Appium driver").option("--update", "update selected drivers when they are already installed").option("--unsafe", "allow Appium driver updates across major versions; requires --update").action((opts) => {
1505
+ const selection = {
1506
+ all: !!opts.all,
1507
+ android: !!opts.android,
1508
+ ios: !!opts.ios,
1509
+ update: !!opts.update,
1510
+ unsafe: !!opts.unsafe,
1511
+ cwd: resolveCommandCwd(cmd)
1512
+ };
1513
+ const validationError = validateMobileDriverInstallSelection(selection);
1514
+ if (validationError) {
1515
+ console.error(import_picocolors5.default.red(`Error: ${validationError}`));
1516
+ console.error(cmd.helpInformation());
1517
+ process.exitCode = 1;
1518
+ return;
1519
+ }
1520
+ console.log(import_picocolors5.default.blue("Installing agent-qa mobile driver support..."));
1521
+ const result = runMobileDriverInstall(selection, deps);
1522
+ printMobileDriverInstallResult(selection, result);
1523
+ if (!result.ok) {
1524
+ process.exitCode = result.status || 1;
1525
+ }
1526
+ });
1527
+ return cmd;
1528
+ }
1529
+
1530
+ // src/commands/doctor.ts
1531
+ var import_commander5 = require("commander");
1405
1532
  var import_node_child_process3 = require("child_process");
1406
1533
  var import_node_fs2 = require("fs");
1407
- var import_picocolors5 = __toESM(require("picocolors"), 1);
1534
+ var import_node_path7 = require("path");
1535
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
1408
1536
  init_config();
1409
1537
  init_llm_utils();
1410
1538
  init_model_defaults();
1411
- var import_agent_qa_core5 = require("@vostride/agent-qa-core");
1539
+ var import_agent_qa_core6 = require("@vostride/agent-qa-core");
1412
1540
  function resolveDoctorWorkspace(config, configPath) {
1413
1541
  try {
1414
1542
  return {
1415
1543
  ok: true,
1416
- workspace: (0, import_agent_qa_core5.resolveWorkspacePaths)({
1544
+ workspace: (0, import_agent_qa_core6.resolveWorkspacePaths)({
1417
1545
  config,
1418
1546
  configPath
1419
1547
  })
@@ -1467,20 +1595,20 @@ function applyResolvedAuth(llm, auth) {
1467
1595
  function formatStatus(status) {
1468
1596
  switch (status) {
1469
1597
  case "pass":
1470
- return import_picocolors5.default.green("PASS");
1598
+ return import_picocolors6.default.green("PASS");
1471
1599
  case "warn":
1472
- return import_picocolors5.default.yellow("WARN");
1600
+ return import_picocolors6.default.yellow("WARN");
1473
1601
  case "fail":
1474
- return import_picocolors5.default.red("FAIL");
1602
+ return import_picocolors6.default.red("FAIL");
1475
1603
  case "skip":
1476
- return import_picocolors5.default.dim("SKIP");
1604
+ return import_picocolors6.default.dim("SKIP");
1477
1605
  }
1478
1606
  }
1479
1607
  function padDots(name, message) {
1480
1608
  const totalWidth = 60;
1481
1609
  const used = name.length + message.length;
1482
1610
  const dots = Math.max(2, totalWidth - used);
1483
- return import_picocolors5.default.dim(".".repeat(dots));
1611
+ return import_picocolors6.default.dim(".".repeat(dots));
1484
1612
  }
1485
1613
  function checkNodeVersion() {
1486
1614
  return {
@@ -1710,7 +1838,7 @@ function checkLLMConnection(config) {
1710
1838
  function checkPlaywright(config) {
1711
1839
  return {
1712
1840
  name: "Playwright",
1713
- fixInstructions: "Run `agent-qa install-browsers --chromium` or `agent-qa init`",
1841
+ fixInstructions: "Run `agent-qa install-browsers --chromium`",
1714
1842
  check: async () => {
1715
1843
  const browsers = config?.browsers;
1716
1844
  const hasBrowsers = browsers && browsers.length > 0;
@@ -1726,16 +1854,28 @@ function checkPlaywright(config) {
1726
1854
  }
1727
1855
  };
1728
1856
  }
1729
- function checkAppium(config) {
1857
+ function appiumResolverCwd(configPath) {
1858
+ return (0, import_node_path7.dirname)((0, import_node_path7.resolve)(configPath));
1859
+ }
1860
+ function mobileDriverInstallCommand(config) {
1861
+ const hasAndroid = hasAndroidPlatform(config);
1862
+ const hasIOS = hasIOSPlatform(config);
1863
+ if (hasAndroid && hasIOS) return "agent-qa install-mobile-drivers --all";
1864
+ if (hasAndroid) return "agent-qa install-mobile-drivers --android";
1865
+ if (hasIOS) return "agent-qa install-mobile-drivers --ios";
1866
+ return "agent-qa install-mobile-drivers --all";
1867
+ }
1868
+ function checkAppium(config, configPath) {
1730
1869
  return {
1731
1870
  name: "Appium",
1732
- fixInstructions: "Run `npm install -g appium`",
1871
+ fixInstructions: `${(0, import_agent_qa_core6.formatAppiumInstallGuidance)()} Then run \`${mobileDriverInstallCommand(config)}\`.`,
1733
1872
  check: async () => {
1734
1873
  if (!isMobilePlatform(config)) {
1735
1874
  return { status: "skip", message: "not configured" };
1736
1875
  }
1737
1876
  try {
1738
- const version = (0, import_node_child_process3.execSync)("appium --version", { stdio: "pipe" }).toString().trim();
1877
+ const appium = (0, import_agent_qa_core6.resolveAppiumExecutable)({ cwd: appiumResolverCwd(configPath) });
1878
+ const version = (0, import_node_child_process3.execFileSync)(appium.command, ["--version"], { stdio: "pipe" }).toString().trim();
1739
1879
  return { status: "pass", message: `v${version}` };
1740
1880
  } catch {
1741
1881
  return { status: "fail", message: "not installed" };
@@ -1743,16 +1883,17 @@ function checkAppium(config) {
1743
1883
  }
1744
1884
  };
1745
1885
  }
1746
- function checkAppiumDrivers(config) {
1886
+ function checkAppiumDrivers(config, configPath) {
1747
1887
  return {
1748
1888
  name: "Appium drivers",
1749
- fixInstructions: "Run `appium driver install uiautomator2` or `appium driver install xcuitest`",
1889
+ fixInstructions: `Run \`${mobileDriverInstallCommand(config)}\``,
1750
1890
  check: async () => {
1751
1891
  if (!isMobilePlatform(config)) {
1752
1892
  return { status: "skip", message: "not configured" };
1753
1893
  }
1754
1894
  try {
1755
- const output = (0, import_node_child_process3.execSync)("appium driver list --installed", { stdio: "pipe" }).toString();
1895
+ const appium = (0, import_agent_qa_core6.resolveAppiumExecutable)({ cwd: appiumResolverCwd(configPath) });
1896
+ const output = (0, import_node_child_process3.execFileSync)(appium.command, ["driver", "list", "--installed"], { stdio: "pipe" }).toString();
1756
1897
  const hasDrivers = output.includes("uiautomator2") || output.includes("xcuitest");
1757
1898
  if (hasDrivers) {
1758
1899
  return { status: "pass", message: "drivers installed" };
@@ -1847,8 +1988,8 @@ function checkTestDiscovery(config, configPath) {
1847
1988
  }
1848
1989
  const parts = [];
1849
1990
  const [testFiles, suiteFiles] = await Promise.all([
1850
- (0, import_agent_qa_core5.discoverWorkspaceFiles)({ workspace: resolved.workspace, kind: "test" }),
1851
- (0, import_agent_qa_core5.discoverWorkspaceFiles)({ workspace: resolved.workspace, kind: "suite" })
1991
+ (0, import_agent_qa_core6.discoverWorkspaceFiles)({ workspace: resolved.workspace, kind: "test" }),
1992
+ (0, import_agent_qa_core6.discoverWorkspaceFiles)({ workspace: resolved.workspace, kind: "suite" })
1852
1993
  ]);
1853
1994
  parts.push(`${testFiles.length} test file(s)`);
1854
1995
  parts.push(`${suiteFiles.length} suite file(s)`);
@@ -1888,7 +2029,7 @@ function getGlobalConfigPath(command) {
1888
2029
  return command.parent?.opts().config ?? "agent-qa.config.yaml";
1889
2030
  }
1890
2031
  function createDoctorCommand() {
1891
- const cmd = new import_commander4.Command("doctor").description("Validate environment and dependencies").action(async function() {
2032
+ const cmd = new import_commander5.Command("doctor").description("Validate environment and dependencies").action(async function() {
1892
2033
  const configPath = getGlobalConfigPath(this);
1893
2034
  let config = null;
1894
2035
  try {
@@ -1910,15 +2051,15 @@ function createDoctorCommand() {
1910
2051
  checkSubscriptionAuth(config),
1911
2052
  checkLLMConnection(config),
1912
2053
  checkPlaywright(config),
1913
- checkAppium(config),
1914
- checkAppiumDrivers(config),
2054
+ checkAppium(config, configPath),
2055
+ checkAppiumDrivers(config, configPath),
1915
2056
  checkAndroidSDK(config),
1916
2057
  checkXcode(config),
1917
2058
  checkTestDiscovery(config, configPath),
1918
2059
  checkValidation(config, configPath)
1919
2060
  ];
1920
2061
  console.log("");
1921
- console.log(import_picocolors5.default.bold(" agent-qa doctor"));
2062
+ console.log(import_picocolors6.default.bold(" agent-qa doctor"));
1922
2063
  console.log("");
1923
2064
  let hasFailure = false;
1924
2065
  const results = [];
@@ -1929,16 +2070,16 @@ function createDoctorCommand() {
1929
2070
  const status = formatStatus(result.status);
1930
2071
  console.log(` ${check.name} ${dots} ${result.message} ${status}`);
1931
2072
  if (result.status === "fail") {
1932
- console.log(import_picocolors5.default.dim(` Fix: ${check.fixInstructions}`));
2073
+ console.log(import_picocolors6.default.dim(` Fix: ${check.fixInstructions}`));
1933
2074
  hasFailure = true;
1934
2075
  }
1935
2076
  }
1936
2077
  console.log("");
1937
2078
  if (hasFailure) {
1938
- console.log(import_picocolors5.default.red(" Some checks failed. See fix instructions above."));
2079
+ console.log(import_picocolors6.default.red(" Some checks failed. See fix instructions above."));
1939
2080
  process.exitCode = 1;
1940
2081
  } else {
1941
- console.log(import_picocolors5.default.green(" All checks passed! Your environment is ready."));
2082
+ console.log(import_picocolors6.default.green(" All checks passed! Your environment is ready."));
1942
2083
  }
1943
2084
  console.log("");
1944
2085
  });
@@ -1947,16 +2088,16 @@ function createDoctorCommand() {
1947
2088
 
1948
2089
  // src/commands/run.ts
1949
2090
  var import_node_crypto = require("crypto");
1950
- var import_node_path7 = require("path");
1951
- var import_commander5 = require("commander");
1952
- var import_picocolors6 = __toESM(require("picocolors"), 1);
2091
+ var import_node_path9 = require("path");
2092
+ var import_commander6 = require("commander");
2093
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
1953
2094
  init_config();
1954
- var import_agent_qa_core6 = require("@vostride/agent-qa-core");
1955
2095
  var import_agent_qa_core7 = require("@vostride/agent-qa-core");
2096
+ var import_agent_qa_core8 = require("@vostride/agent-qa-core");
1956
2097
 
1957
2098
  // src/devices.ts
1958
2099
  var import_node_fs3 = require("fs");
1959
- var import_node_path6 = require("path");
2100
+ var import_node_path8 = require("path");
1960
2101
  var import_yaml4 = require("yaml");
1961
2102
  var import_zod = require("zod");
1962
2103
  var LocalAppBindingSchema = import_zod.z.object({
@@ -1989,7 +2130,7 @@ var DISPLAY_NAMES = {
1989
2130
  browserstack: "BrowserStack"
1990
2131
  };
1991
2132
  function loadLocalBindings(dir) {
1992
- const filePath = (0, import_node_path6.join)(dir ?? process.cwd(), "agent-qa.local.yaml");
2133
+ const filePath = (0, import_node_path8.join)(dir ?? process.cwd(), "agent-qa.local.yaml");
1993
2134
  let content;
1994
2135
  try {
1995
2136
  content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
@@ -2045,10 +2186,10 @@ function resolveProviderCredentials(transport, localBindings) {
2045
2186
 
2046
2187
  // src/commands/run.ts
2047
2188
  init_llm_utils();
2048
- var import_agent_qa_core8 = require("@vostride/agent-qa-core");
2049
- var RUNTIME_ARTIFACTS_DIR = import_agent_qa_core6.DEFAULT_AGENT_QA_ARTIFACTS_DIR || ".agent-qa/artifacts";
2050
- var RUNTIME_CACHE_DIR = import_agent_qa_core6.DEFAULT_AGENT_QA_CACHE_DIR || ".agent-qa/cache";
2051
- var RUNTIME_VIDEOS_DIR = import_agent_qa_core6.DEFAULT_AGENT_QA_VIDEOS_DIR || ".agent-qa/artifacts/videos";
2189
+ var import_agent_qa_core9 = require("@vostride/agent-qa-core");
2190
+ var RUNTIME_ARTIFACTS_DIR = import_agent_qa_core7.DEFAULT_AGENT_QA_ARTIFACTS_DIR || ".agent-qa/artifacts";
2191
+ var RUNTIME_CACHE_DIR = import_agent_qa_core7.DEFAULT_AGENT_QA_CACHE_DIR || ".agent-qa/cache";
2192
+ var RUNTIME_VIDEOS_DIR = import_agent_qa_core7.DEFAULT_AGENT_QA_VIDEOS_DIR || ".agent-qa/artifacts/videos";
2052
2193
  var _appiumManager = null;
2053
2194
  async function acquireAppium(logLevel) {
2054
2195
  if (process.env.AGENT_QA_APPIUM_URL) return process.env.AGENT_QA_APPIUM_URL;
@@ -2173,13 +2314,13 @@ function resolveDashboardVideoDir(config, configDir, dashboardEnabled) {
2173
2314
  const recordingCfg = config.services?.recording;
2174
2315
  const configuredVideoDir = recordingCfg?.videoDir;
2175
2316
  if (configuredVideoDir) {
2176
- return (0, import_node_path7.resolve)(configDir, configuredVideoDir);
2317
+ return (0, import_node_path9.resolve)(configDir, configuredVideoDir);
2177
2318
  }
2178
2319
  if (dashboardEnabled) {
2179
2320
  const dashArtifactsDir = config.services?.dashboard?.artifactsDir;
2180
- return (0, import_node_path7.resolve)(configDir, dashArtifactsDir ?? RUNTIME_ARTIFACTS_DIR, "videos");
2321
+ return (0, import_node_path9.resolve)(configDir, dashArtifactsDir ?? RUNTIME_ARTIFACTS_DIR, "videos");
2181
2322
  }
2182
- return (0, import_node_path7.resolve)(configDir, RUNTIME_VIDEOS_DIR);
2323
+ return (0, import_node_path9.resolve)(configDir, RUNTIME_VIDEOS_DIR);
2183
2324
  }
2184
2325
  var SUPPORTED_REPORTERS = ["console", "junit", "stdout-live", "dashboard"];
2185
2326
  function parseVarFlags(flags) {
@@ -2199,8 +2340,8 @@ function resolveRunnerAttribute(transport) {
2199
2340
  return transport === "browserstack" ? "browserstack" : "local";
2200
2341
  }
2201
2342
  function buildCliRunAttributes(userAttributes, runner) {
2202
- return (0, import_agent_qa_core6.mergeRunAttributes)(
2203
- (0, import_agent_qa_core6.buildInternalRunAttributes)({ trigger: "cli", runner }),
2343
+ return (0, import_agent_qa_core7.mergeRunAttributes)(
2344
+ (0, import_agent_qa_core7.buildInternalRunAttributes)({ trigger: "cli", runner }),
2204
2345
  userAttributes
2205
2346
  );
2206
2347
  }
@@ -2213,17 +2354,17 @@ function readRunAttributesFromEnv() {
2213
2354
  } catch {
2214
2355
  throw new Error("inherited run attributes: AGENT_QA_RUN_ATTRIBUTES_JSON must contain valid run attributes JSON");
2215
2356
  }
2216
- const attributes = (0, import_agent_qa_core6.validateTrustedRunAttributes)(parsed, "inherited run attributes");
2357
+ const attributes = (0, import_agent_qa_core7.validateTrustedRunAttributes)(parsed, "inherited run attributes");
2217
2358
  return Object.keys(attributes).length > 0 ? attributes : void 0;
2218
2359
  }
2219
2360
  function resolveCliRunAttributes(userAttributes, runner, inheritedAttributes) {
2220
2361
  if (!inheritedAttributes) return buildCliRunAttributes(userAttributes, runner);
2221
- const trigger = inheritedAttributes[import_agent_qa_core6.ATTR_TRIGGER];
2362
+ const trigger = inheritedAttributes[import_agent_qa_core7.ATTR_TRIGGER];
2222
2363
  const inheritedUserAttributes = Object.fromEntries(
2223
- Object.entries(inheritedAttributes).filter(([key]) => key !== import_agent_qa_core6.ATTR_TRIGGER && key !== import_agent_qa_core6.ATTR_RUNNER)
2364
+ Object.entries(inheritedAttributes).filter(([key]) => key !== import_agent_qa_core7.ATTR_TRIGGER && key !== import_agent_qa_core7.ATTR_RUNNER)
2224
2365
  );
2225
- return (0, import_agent_qa_core6.mergeRunAttributes)(
2226
- (0, import_agent_qa_core6.buildInternalRunAttributes)({ trigger: trigger ?? "cli", runner }),
2366
+ return (0, import_agent_qa_core7.mergeRunAttributes)(
2367
+ (0, import_agent_qa_core7.buildInternalRunAttributes)({ trigger: trigger ?? "cli", runner }),
2227
2368
  inheritedUserAttributes
2228
2369
  );
2229
2370
  }
@@ -2232,7 +2373,7 @@ function writeRunAttributesToEnv(attributes) {
2232
2373
  }
2233
2374
  function printRunAttributes(attributes) {
2234
2375
  if (!attributes || Object.keys(attributes).length === 0) return;
2235
- const block = (0, import_agent_qa_core6.formatRunAttributesBlock)(attributes);
2376
+ const block = (0, import_agent_qa_core7.formatRunAttributesBlock)(attributes);
2236
2377
  if (block) console.log(block);
2237
2378
  }
2238
2379
  function resolveReporterSelection(opts, dashboardConfigured) {
@@ -2293,37 +2434,37 @@ async function captureSingleAnalyticsResult(reporter, result) {
2293
2434
  function formatParseErrors(errors) {
2294
2435
  for (const err of errors) {
2295
2436
  const location = `${err.file}:${err.line}:${err.column}`;
2296
- const severity = err.severity === "error" ? import_picocolors6.default.red("error") : import_picocolors6.default.yellow("warning");
2437
+ const severity = err.severity === "error" ? import_picocolors7.default.red("error") : import_picocolors7.default.yellow("warning");
2297
2438
  console.error(`${severity}: ${err.message}`);
2298
- console.error(` ${import_picocolors6.default.blue("-->")} ${location}`);
2439
+ console.error(` ${import_picocolors7.default.blue("-->")} ${location}`);
2299
2440
  if (err.source) {
2300
- console.error(` ${import_picocolors6.default.blue("|")} ${err.source}`);
2441
+ console.error(` ${import_picocolors7.default.blue("|")} ${err.source}`);
2301
2442
  }
2302
2443
  if (err.suggestion) {
2303
- console.error(` ${import_picocolors6.default.blue("=")} ${import_picocolors6.default.bold("help")}: ${err.suggestion}`);
2444
+ console.error(` ${import_picocolors7.default.blue("=")} ${import_picocolors7.default.bold("help")}: ${err.suggestion}`);
2304
2445
  }
2305
2446
  console.error();
2306
2447
  }
2307
2448
  }
2308
2449
  function createTestRunId() {
2309
- return process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core6.generateRunId)();
2450
+ return process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core7.generateRunId)();
2310
2451
  }
2311
2452
  function createSuiteRunId() {
2312
- return process.env.AGENT_QA_SUITE_QUEUE_ID ?? process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core6.generateRunId)();
2453
+ return process.env.AGENT_QA_SUITE_QUEUE_ID ?? process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core7.generateRunId)();
2313
2454
  }
2314
2455
  function printRunId(runId) {
2315
2456
  if (runId) console.log(`Run ID: ${runId}`);
2316
2457
  }
2317
2458
  function printMemoryStatus(log) {
2318
2459
  const parts = [];
2319
- if (log.added > 0) parts.push(import_picocolors6.default.green(`${log.added} added`));
2460
+ if (log.added > 0) parts.push(import_picocolors7.default.green(`${log.added} added`));
2320
2461
  if (log.confirmed > 0) parts.push(`${log.confirmed} confirmed`);
2321
- if (log.deprecated > 0) parts.push(import_picocolors6.default.yellow(`${log.deprecated} deprecated`));
2462
+ if (log.deprecated > 0) parts.push(import_picocolors7.default.yellow(`${log.deprecated} deprecated`));
2322
2463
  if (parts.length === 0) parts.push("no changes");
2323
2464
  const secs = log.curatorDuration >= 1e3 ? `${Math.round(log.curatorDuration / 1e3)}s` : `${log.curatorDuration}ms`;
2324
- console.log(` ${import_picocolors6.default.dim("Memory:")} ${parts.join(", ")} ${import_picocolors6.default.dim(`(${secs})`)}`);
2465
+ console.log(` ${import_picocolors7.default.dim("Memory:")} ${parts.join(", ")} ${import_picocolors7.default.dim(`(${secs})`)}`);
2325
2466
  for (const err of log.errors) {
2326
- console.log(` ${import_picocolors6.default.dim("Memory:")} ${import_picocolors6.default.yellow(`warning -- ${err}`)}`);
2467
+ console.log(` ${import_picocolors7.default.dim("Memory:")} ${import_picocolors7.default.yellow(`warning -- ${err}`)}`);
2327
2468
  }
2328
2469
  }
2329
2470
  function isGlobLikePattern(value) {
@@ -2338,15 +2479,15 @@ function matchesCliWorkspacePattern(workspace, kind, pattern, record) {
2338
2479
  testMatch: kind === "test" ? [pattern] : workspace.testMatch,
2339
2480
  suiteMatch: kind === "suite" ? [pattern] : workspace.suiteMatch
2340
2481
  };
2341
- return (0, import_agent_qa_core7.isWorkspacePathMatch)({
2482
+ return (0, import_agent_qa_core8.isWorkspacePathMatch)({
2342
2483
  workspace: patternWorkspace,
2343
2484
  kind,
2344
2485
  workspaceRelativePath: record.workspaceRelativePath
2345
2486
  });
2346
2487
  }
2347
2488
  async function discoverFilesForCliPatterns(workspace, patterns) {
2348
- const testRecords = await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace, kind: "test" });
2349
- const suiteRecords = await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace, kind: "suite" });
2489
+ const testRecords = await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace, kind: "test" });
2490
+ const suiteRecords = await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace, kind: "suite" });
2350
2491
  const discoveredByKind = {
2351
2492
  test: testRecords,
2352
2493
  suite: suiteRecords
@@ -2367,7 +2508,7 @@ async function discoverFilesForCliPatterns(workspace, patterns) {
2367
2508
  }
2368
2509
  continue;
2369
2510
  }
2370
- const record = await (0, import_agent_qa_core7.resolveWorkspaceFileTarget)({
2511
+ const record = await (0, import_agent_qa_core8.resolveWorkspaceFileTarget)({
2371
2512
  workspace,
2372
2513
  kind,
2373
2514
  filePath: pattern,
@@ -2395,21 +2536,21 @@ function buildHooksArtifact(hooks) {
2395
2536
  async function loadRuntimeSecrets(input3) {
2396
2537
  const configuredSecretsFile = input3.config.workspace?.secretsFile;
2397
2538
  if (typeof configuredSecretsFile !== "string" || configuredSecretsFile.trim().length === 0) {
2398
- console.error(import_picocolors6.default.red("Error: workspace.secretsFile is required. Run `agent-qa init` or set it in agent-qa.config.yaml."));
2539
+ console.error(import_picocolors7.default.red("Error: workspace.secretsFile is required. Run `agent-qa init` or set it in agent-qa.config.yaml."));
2399
2540
  process.exit(1);
2400
2541
  }
2401
- const secretsPath = input3.secretsFilePath ?? (0, import_node_path7.resolve)(input3.configDir, configuredSecretsFile);
2542
+ const secretsPath = input3.secretsFilePath ?? (0, import_node_path9.resolve)(input3.configDir, configuredSecretsFile);
2402
2543
  if (!input3.existsSync(secretsPath)) {
2403
- console.error(import_picocolors6.default.red(`Error: Secrets file not found: ${secretsPath}`));
2404
- console.error(import_picocolors6.default.dim("Create the file before running tests. It may be empty, but it must exist."));
2544
+ console.error(import_picocolors7.default.red(`Error: Secrets file not found: ${secretsPath}`));
2545
+ console.error(import_picocolors7.default.dim("Create the file before running tests. It may be empty, but it must exist."));
2405
2546
  process.exit(1);
2406
2547
  }
2407
2548
  let secretsContent;
2408
2549
  try {
2409
2550
  secretsContent = input3.readFileSync(secretsPath, "utf-8");
2410
2551
  } catch {
2411
- console.error(import_picocolors6.default.red(`Error: Secrets file could not be read: ${secretsPath}`));
2412
- console.error(import_picocolors6.default.dim("Check the path and file permissions before running tests."));
2552
+ console.error(import_picocolors7.default.red(`Error: Secrets file could not be read: ${secretsPath}`));
2553
+ console.error(import_picocolors7.default.dim("Check the path and file permissions before running tests."));
2413
2554
  process.exit(1);
2414
2555
  }
2415
2556
  try {
@@ -2426,7 +2567,7 @@ async function loadRuntimeSecrets(input3) {
2426
2567
  };
2427
2568
  } catch (err) {
2428
2569
  const message = err instanceof Error ? err.message : String(err);
2429
- console.error(import_picocolors6.default.red(`Error: Secrets file is invalid: ${message}`));
2570
+ console.error(import_picocolors7.default.red(`Error: Secrets file is invalid: ${message}`));
2430
2571
  process.exit(1);
2431
2572
  }
2432
2573
  }
@@ -2483,7 +2624,7 @@ async function resolveDeviceAndFarmSession(config, deviceName, testPlatform, tes
2483
2624
  }
2484
2625
  const browserstackApp = mobileResolved?.app.install?.browserstack;
2485
2626
  if (!browserstackApp && hasNativeAppIdentity(mobileResolved) && !isBrowserMode(resolved.match)) {
2486
- throw new import_agent_qa_core6.MobileSetupError({
2627
+ throw new import_agent_qa_core7.MobileSetupError({
2487
2628
  category: "app-install",
2488
2629
  message: `BrowserStack native app target "${mobileResolved?.targetName ?? "unknown"}" requires app.browserstack; app.path is local-only and is not used as a BrowserStack fallback.`,
2489
2630
  platform: resolved.platform,
@@ -2507,9 +2648,9 @@ async function resolveDeviceAndFarmSession(config, deviceName, testPlatform, tes
2507
2648
  appSourceTrace: mobileResolved?.sourceTrace
2508
2649
  });
2509
2650
  } catch (err) {
2510
- if (err instanceof import_agent_qa_core6.MobileSetupError) throw err;
2651
+ if (err instanceof import_agent_qa_core7.MobileSetupError) throw err;
2511
2652
  if (browserstackApp) {
2512
- throw new import_agent_qa_core6.MobileSetupError({
2653
+ throw new import_agent_qa_core7.MobileSetupError({
2513
2654
  category: "app-install",
2514
2655
  message: `Failed to resolve BrowserStack app reference: ${err instanceof Error ? err.message : String(err)}`,
2515
2656
  platform: resolved.platform,
@@ -2531,14 +2672,14 @@ function hasNativeAppIdentity(mobileResolved) {
2531
2672
  return Boolean(app?.bundleId || app?.appPackage || app?.appActivity || app?.deepLinkAppId);
2532
2673
  }
2533
2674
  function wrapMobileSetupError(err) {
2534
- if (err instanceof import_agent_qa_core6.MobileSetupError) {
2675
+ if (err instanceof import_agent_qa_core7.MobileSetupError) {
2535
2676
  throw new Error(`${err.category}: ${err.message}`);
2536
2677
  }
2537
2678
  throw err;
2538
2679
  }
2539
2680
  function resolveMobileRunOrThrow(input3) {
2540
2681
  try {
2541
- return (0, import_agent_qa_core6.resolveMobileRunConfig)(input3);
2682
+ return (0, import_agent_qa_core7.resolveMobileRunConfig)(input3);
2542
2683
  } catch (err) {
2543
2684
  wrapMobileSetupError(err);
2544
2685
  }
@@ -2589,7 +2730,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2589
2730
  const dashboardCfg = config.services?.dashboard;
2590
2731
  const reporterSelectionResult = resolveReporterSelection(opts, Boolean(dashboardCfg));
2591
2732
  if (reporterSelectionResult.error) {
2592
- console.error(import_picocolors6.default.red(`Error: ${reporterSelectionResult.error}`));
2733
+ console.error(import_picocolors7.default.red(`Error: ${reporterSelectionResult.error}`));
2593
2734
  process.exit(2);
2594
2735
  }
2595
2736
  const reporterSelection = reporterSelectionResult.selection;
@@ -2605,7 +2746,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2605
2746
  activeSuiteDbs.add(dashboardDb);
2606
2747
  return { dashboardDb, logStorage: dashboardDb };
2607
2748
  } catch (err) {
2608
- console.warn(import_picocolors6.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
2749
+ console.warn(import_picocolors7.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
2609
2750
  return {};
2610
2751
  }
2611
2752
  };
@@ -2646,8 +2787,8 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2646
2787
  const testEntries = [];
2647
2788
  const suiteMembers = [];
2648
2789
  for (const { test: relativePath, id: suiteEntryId } of suite.tests) {
2649
- if (!(0, import_agent_qa_core8.isPathInsideDir)(relativePath, configDir)) {
2650
- console.error(import_picocolors6.default.red(`Path traversal rejected: ${relativePath}`));
2790
+ if (!(0, import_agent_qa_core9.isPathInsideDir)(relativePath, configDir)) {
2791
+ console.error(import_picocolors7.default.red(`Path traversal rejected: ${relativePath}`));
2651
2792
  const runId = createSuiteRunId();
2652
2793
  if (dashboardDb) {
2653
2794
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -2685,7 +2826,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2685
2826
  const fileContent = await readFile5(absolutePath, "utf-8");
2686
2827
  const parseResult = parseTestFile2(fileContent, absolutePath);
2687
2828
  if (parseResult.errors.length > 0) {
2688
- console.error(import_picocolors6.default.red(`Parse errors in ${relativePath}:`));
2829
+ console.error(import_picocolors7.default.red(`Parse errors in ${relativePath}:`));
2689
2830
  formatParseErrors(parseResult.errors);
2690
2831
  const runId = createSuiteRunId();
2691
2832
  if (dashboardDb) {
@@ -2727,7 +2868,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2727
2868
  process.exit(2);
2728
2869
  }
2729
2870
  if (parseResult.tests.length === 0) {
2730
- console.error(import_picocolors6.default.red(`No test definition found in ${relativePath}`));
2871
+ console.error(import_picocolors7.default.red(`No test definition found in ${relativePath}`));
2731
2872
  const runId = createSuiteRunId();
2732
2873
  if (dashboardDb) {
2733
2874
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -2764,7 +2905,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2764
2905
  const testDef = parseResult.tests[0];
2765
2906
  if (testDef["test-id"] && testDef["test-id"] !== suiteEntryId) {
2766
2907
  const mismatchMsg = `ID mismatch in suite "${suite.name}": test file ${relativePath} has test-id "${testDef["test-id"]}" but suite entry specifies id "${suiteEntryId}"`;
2767
- console.error(import_picocolors6.default.red(mismatchMsg));
2908
+ console.error(import_picocolors7.default.red(mismatchMsg));
2768
2909
  const runId = createSuiteRunId();
2769
2910
  if (dashboardDb) {
2770
2911
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -2887,7 +3028,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2887
3028
  mobileResolved = { ...mobileResolved, appium: { url: appiumUrl, managed: !process.env.AGENT_QA_APPIUM_URL } };
2888
3029
  }
2889
3030
  } catch (err) {
2890
- throw new import_agent_qa_core6.MobileSetupError({
3031
+ throw new import_agent_qa_core7.MobileSetupError({
2891
3032
  category: "appium-startup",
2892
3033
  message: `Could not auto-start Appium: ${err instanceof Error ? err.message : String(err)}`,
2893
3034
  platform: suitePlatform,
@@ -2965,11 +3106,11 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2965
3106
  if (reporterSelection.dashboard && dashboardDb) {
2966
3107
  try {
2967
3108
  const { DashboardReporter } = await import("@vostride/agent-qa-dashboard");
2968
- const { resolve: resolve7 } = await import("path");
3109
+ const { resolve: resolve9 } = await import("path");
2969
3110
  const dashArtifactsDir = config.services?.dashboard?.artifactsDir ?? RUNTIME_ARTIFACTS_DIR;
2970
3111
  reporters.push(new DashboardReporter({
2971
3112
  db: dashboardDb,
2972
- artifactsDir: resolve7(configDir, dashArtifactsDir),
3113
+ artifactsDir: resolve9(configDir, dashArtifactsDir),
2973
3114
  redactor: variableContext?.secretRedactor,
2974
3115
  onRunCreated: (runId) => {
2975
3116
  lastSuiteRunId = runId;
@@ -2984,9 +3125,9 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2984
3125
  let actionCache;
2985
3126
  if (opts.cache !== false) {
2986
3127
  const { FileActionCache } = await import("@vostride/agent-qa-core");
2987
- const { resolve: resolve7 } = await import("path");
3128
+ const { resolve: resolve9 } = await import("path");
2988
3129
  actionCache = new FileActionCache({
2989
- dir: resolve7(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR),
3130
+ dir: resolve9(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR),
2990
3131
  ttl: config.services?.cache?.ttl ?? "7d",
2991
3132
  logger: logger.createScopedLogger("cache")
2992
3133
  });
@@ -2998,7 +3139,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
2998
3139
  let suiteMemoryProvider;
2999
3140
  let suiteCircuitBreaker;
3000
3141
  const suiteMemoryConfig = config.services?.memory;
3001
- const suiteMemoryRoot = (0, import_agent_qa_core6.resolveMemoryRoot)(config, configDir);
3142
+ const suiteMemoryRoot = (0, import_agent_qa_core7.resolveMemoryRoot)(config, configDir);
3002
3143
  if (opts.memory !== false && suiteMemoryConfig?.enabled !== false) {
3003
3144
  try {
3004
3145
  const { createMemoryProvider } = await import("@vostride/agent-qa-core");
@@ -3008,7 +3149,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
3008
3149
  curatorLockTimeout: suiteMemoryConfig?.curatorLockTimeout
3009
3150
  });
3010
3151
  } catch (err) {
3011
- console.warn(` ${import_picocolors6.default.dim("Memory:")} init error -- ${err.message}`);
3152
+ console.warn(` ${import_picocolors7.default.dim("Memory:")} init error -- ${err.message}`);
3012
3153
  }
3013
3154
  if (suiteMemoryConfig?.circuitBreakerEnabled !== false) {
3014
3155
  const { CircuitBreaker } = await import("@vostride/agent-qa-core");
@@ -3083,6 +3224,7 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
3083
3224
  resolvedHooks: hooksContext?.resolvedHooks,
3084
3225
  sandboxOptions: hooksContext?.sandboxOptions,
3085
3226
  logCapture: suiteLogCapture,
3227
+ accessibility: config.services?.accessibility,
3086
3228
  screenshotSize: plannerCfg.screenshotSize,
3087
3229
  effectiveResolution: plannerCfg.effectiveResolution,
3088
3230
  memoryProvider: suiteMemoryProvider,
@@ -3147,19 +3289,19 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
3147
3289
  const batchOutcome = await flushSuiteParents();
3148
3290
  if (batchOutcome.cancelled || shutdownRequested) break;
3149
3291
  if (opts.bail && batchOutcome.failed) {
3150
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3292
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3151
3293
  break;
3152
3294
  }
3153
3295
  const outcome = await runOneSuiteFile(suiteFile, suite);
3154
3296
  if (outcome.cancelled || shutdownRequested) break;
3155
3297
  if (opts.bail && outcome.failed) {
3156
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3298
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3157
3299
  break;
3158
3300
  }
3159
3301
  }
3160
3302
  const finalBatchOutcome = await flushSuiteParents();
3161
3303
  if (opts.bail && finalBatchOutcome.failed) {
3162
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3304
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failed suite"));
3163
3305
  }
3164
3306
  process.removeListener("SIGINT", handleShutdown);
3165
3307
  process.removeListener("SIGTERM", handleShutdown);
@@ -3167,12 +3309,12 @@ async function executeSuites(suiteFiles, opts, config, globalOpts, configContent
3167
3309
  return suiteResults.some((r) => r.status === "failed" || r.status === "cancelled") || shutdownRequested;
3168
3310
  }
3169
3311
  function createRunCommand() {
3170
- const cmd = new import_commander5.Command("run").description("Run test files").argument("[patterns...]", "glob patterns for test files").option("--browser <name>", "override browser (chromium, firefox, webkit)").option("--platform <name>", "filter by platform (web, android, ios)").option("--headless", "run in headless mode").option("--no-headless", "run in headed mode").option("--no-cache", "bypass action cache for this run").option("--no-memory", "disable runtime memory injection for this run").option("--bail", "stop on first test failure").option("--dry-run", "parse and list tests without executing").option("--list-tests", "list discovered test files without executing").option("--junit-output <path>", "path for JUnit XML output").option("--screenshot-dir <dir>", "directory for failure screenshots").option("--screenshot-mode <mode>", "when to capture: failure (default) or every-step").option("--reporter <names...>", "select reporters (default: console)").option("--record", "enable session video recording").option("--config-debug", "print resolved config with source attribution for each test").option("--test", "discover and run tests via testMatch patterns (default when no flags)").option("--suite", "discover and run suites via suiteMatch patterns").option("--all", "discover and run both tests (testMatch) and suites (suiteMatch)").option("--device <name>", "Override the mobile test/suite device for this run").option("--var <kv...>", "set variable KEY=VALUE (repeatable)").option("--run-attr <kv...>", "attach run attribute KEY=VALUE; repeatable").action(async (patterns, opts, command) => {
3312
+ const cmd = new import_commander6.Command("run").description("Run test files").argument("[patterns...]", "glob patterns for test files").option("--browser <name>", "override browser (chromium, firefox, webkit)").option("--platform <name>", "filter by platform (web, android, ios)").option("--headless", "run in headless mode").option("--no-headless", "run in headed mode").option("--no-cache", "bypass action cache for this run").option("--no-memory", "disable runtime memory injection for this run").option("--bail", "stop on first test failure").option("--dry-run", "parse and list tests without executing").option("--list-tests", "list discovered test files without executing").option("--junit-output <path>", "path for JUnit XML output").option("--screenshot-dir <dir>", "directory for failure screenshots").option("--screenshot-mode <mode>", "when to capture: failure (default) or every-step").option("--reporter <names...>", "select reporters (default: console)").option("--record", "enable session video recording").option("--config-debug", "print resolved config with source attribution for each test").option("--test", "discover and run tests via testMatch patterns (default when no flags)").option("--suite", "discover and run suites via suiteMatch patterns").option("--all", "discover and run both tests (testMatch) and suites (suiteMatch)").option("--device <name>", "Override the mobile test/suite device for this run").option("--var <kv...>", "set variable KEY=VALUE (repeatable)").option("--run-attr <kv...>", "attach run attribute KEY=VALUE; repeatable").action(async (patterns, opts, command) => {
3171
3313
  let _crashDb;
3172
3314
  try {
3173
3315
  const modeFlags = [opts.test, opts.suite, opts.all].filter(Boolean).length;
3174
3316
  if (modeFlags > 1) {
3175
- console.error(import_picocolors6.default.red("Error: --test, --suite, and --all are mutually exclusive."));
3317
+ console.error(import_picocolors7.default.red("Error: --test, --suite, and --all are mutually exclusive."));
3176
3318
  process.exit(1);
3177
3319
  }
3178
3320
  const program2 = command.parent;
@@ -3188,8 +3330,8 @@ function createRunCommand() {
3188
3330
  flags: flagOverrides,
3189
3331
  loadAuthPlugins: !(opts.dryRun || opts.listTests)
3190
3332
  });
3191
- const configFilePath = (0, import_node_path7.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
3192
- const workspacePaths = (0, import_agent_qa_core7.resolveWorkspacePaths)({
3333
+ const configFilePath = (0, import_node_path9.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
3334
+ const workspacePaths = (0, import_agent_qa_core8.resolveWorkspacePaths)({
3193
3335
  config,
3194
3336
  configPath: configFilePath
3195
3337
  });
@@ -3207,13 +3349,13 @@ function createRunCommand() {
3207
3349
  const inheritedRunAttributes = inheritedContextRunId ? readRunAttributesFromEnv() : void 0;
3208
3350
  let userRunAttributes;
3209
3351
  try {
3210
- const parsedRunAttrs = (0, import_agent_qa_core6.parseRunAttrFlags)(opts.runAttr);
3352
+ const parsedRunAttrs = (0, import_agent_qa_core7.parseRunAttrFlags)(opts.runAttr);
3211
3353
  userRunAttributes = parsedRunAttrs.attributes;
3212
3354
  for (const duplicateKey of parsedRunAttrs.duplicateKeys) {
3213
3355
  console.warn(`Warning: duplicate --run-attr key "${duplicateKey}"; using last value`);
3214
3356
  }
3215
3357
  } catch (err) {
3216
- console.error(import_picocolors6.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
3358
+ console.error(import_picocolors7.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
3217
3359
  process.exit(2);
3218
3360
  }
3219
3361
  const inlineVars = {};
@@ -3229,29 +3371,29 @@ function createRunCommand() {
3229
3371
  let resolvedEnvFilePath = null;
3230
3372
  resolvedEnvFilePath = workspacePaths.envFile.absolutePath;
3231
3373
  if (!existsSync8(workspacePaths.envFile.absolutePath)) {
3232
- console.error(import_picocolors6.default.red(`Error: Env file not found: ${workspacePaths.envFile.absolutePath}`));
3374
+ console.error(import_picocolors7.default.red(`Error: Env file not found: ${workspacePaths.envFile.absolutePath}`));
3233
3375
  process.exit(1);
3234
3376
  }
3235
3377
  try {
3236
3378
  rawEnvFileContent = readFileSync5(workspacePaths.envFile.absolutePath, "utf-8");
3237
3379
  envFileVars = parseEnvFile(rawEnvFileContent);
3238
3380
  } catch {
3239
- console.error(import_picocolors6.default.red(`Error: Env file could not be read: ${workspacePaths.envFile.absolutePath}`));
3381
+ console.error(import_picocolors7.default.red(`Error: Env file could not be read: ${workspacePaths.envFile.absolutePath}`));
3240
3382
  process.exit(1);
3241
3383
  }
3242
3384
  let resolvedHooks;
3243
3385
  let sandboxOptions;
3244
3386
  const hooksFilePath = workspacePaths.hooksFile.absolutePath;
3245
3387
  if (!existsSync8(hooksFilePath)) {
3246
- console.error(import_picocolors6.default.red(`Error: Hooks file not found: ${hooksFilePath}`));
3388
+ console.error(import_picocolors7.default.red(`Error: Hooks file not found: ${hooksFilePath}`));
3247
3389
  process.exit(1);
3248
3390
  }
3249
3391
  try {
3250
3392
  const { parseHooksFile } = await import("@vostride/agent-qa-core");
3251
3393
  const { hooks, errors } = await parseHooksFile(hooksFilePath);
3252
3394
  if (errors.length > 0) {
3253
- console.error(import_picocolors6.default.red("Hooks configuration errors:"));
3254
- for (const err of errors) console.error(import_picocolors6.default.red(` - ${err}`));
3395
+ console.error(import_picocolors7.default.red("Hooks configuration errors:"));
3396
+ for (const err of errors) console.error(import_picocolors7.default.red(` - ${err}`));
3255
3397
  process.exit(1);
3256
3398
  }
3257
3399
  resolvedHooks = new Map(hooks.map((h) => [h.id, h]));
@@ -3260,8 +3402,8 @@ function createRunCommand() {
3260
3402
  secretRedactor: runtimeSecrets.secretRedactor
3261
3403
  };
3262
3404
  } catch (err) {
3263
- console.error(import_picocolors6.default.red(`Error: Hooks file could not be read: ${hooksFilePath}`));
3264
- if (err instanceof Error && err.message) console.error(import_picocolors6.default.dim(err.message));
3405
+ console.error(import_picocolors7.default.red(`Error: Hooks file could not be read: ${hooksFilePath}`));
3406
+ if (err instanceof Error && err.message) console.error(import_picocolors7.default.dim(err.message));
3265
3407
  process.exit(1);
3266
3408
  }
3267
3409
  let agentRulesContent;
@@ -3269,7 +3411,7 @@ function createRunCommand() {
3269
3411
  try {
3270
3412
  agentRulesContent = await readFileAsync(workspacePaths.agentRules.absolutePath, "utf-8");
3271
3413
  } catch {
3272
- console.error(import_picocolors6.default.red(`Error: Agent rules file not found: ${workspacePaths.agentRules.absolutePath}`));
3414
+ console.error(import_picocolors7.default.red(`Error: Agent rules file not found: ${workspacePaths.agentRules.absolutePath}`));
3273
3415
  process.exit(1);
3274
3416
  }
3275
3417
  const effectiveLogLevel = globalOpts.logLevel ?? (globalOpts.verbose ? "debug" : void 0) ?? (globalOpts.quiet ? "silent" : void 0) ?? config.services?.logging?.level ?? "warn";
@@ -3281,41 +3423,41 @@ function createRunCommand() {
3281
3423
  discoveredTestFiles = selected.tests;
3282
3424
  discoveredSuiteFiles = selected.suites;
3283
3425
  } catch (err) {
3284
- console.error(import_picocolors6.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
3426
+ console.error(import_picocolors7.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
3285
3427
  process.exit(1);
3286
3428
  }
3287
3429
  if (discoveredTestFiles.length === 0 && discoveredSuiteFiles.length === 0) {
3288
3430
  const patternKind = patterns.every((pattern) => classifyRunPattern(pattern) === "suite") ? "suite" : "test";
3289
- console.error(import_picocolors6.default.red(`Error: No ${patternKind} files found matching patterns:`));
3290
- for (const p of patterns) console.error(import_picocolors6.default.red(` ${p}`));
3431
+ console.error(import_picocolors7.default.red(`Error: No ${patternKind} files found matching patterns:`));
3432
+ for (const p of patterns) console.error(import_picocolors7.default.red(` ${p}`));
3291
3433
  process.exit(2);
3292
3434
  }
3293
3435
  } else if (opts.suite) {
3294
- discoveredSuiteFiles = (await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
3436
+ discoveredSuiteFiles = (await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
3295
3437
  if (discoveredSuiteFiles.length === 0) {
3296
- console.error(import_picocolors6.default.red("Error: No suite files found matching workspace.suiteMatch"));
3438
+ console.error(import_picocolors7.default.red("Error: No suite files found matching workspace.suiteMatch"));
3297
3439
  for (const p of workspacePaths.suiteMatch) {
3298
- console.error(import_picocolors6.default.red(` ${p}`));
3440
+ console.error(import_picocolors7.default.red(` ${p}`));
3299
3441
  }
3300
3442
  process.exit(2);
3301
3443
  }
3302
3444
  } else if (opts.all) {
3303
- discoveredTestFiles = (await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
3304
- discoveredSuiteFiles = (await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
3445
+ discoveredTestFiles = (await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
3446
+ discoveredSuiteFiles = (await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "suite" })).map((record) => record.absolutePath);
3305
3447
  if (discoveredTestFiles.length === 0 && discoveredSuiteFiles.length === 0) {
3306
- console.error(import_picocolors6.default.red("Error: No test or suite files found. Check workspace.testMatch and workspace.suiteMatch in agent-qa.config.yaml"));
3448
+ console.error(import_picocolors7.default.red("Error: No test or suite files found. Check workspace.testMatch and workspace.suiteMatch in agent-qa.config.yaml"));
3307
3449
  process.exit(2);
3308
3450
  }
3309
3451
  } else {
3310
3452
  if (!workspacePaths.testMatch || workspacePaths.testMatch.length === 0) {
3311
- console.error(import_picocolors6.default.red("Error: No testMatch patterns in config. Add workspace.testMatch to agent-qa.config.yaml."));
3453
+ console.error(import_picocolors7.default.red("Error: No testMatch patterns in config. Add workspace.testMatch to agent-qa.config.yaml."));
3312
3454
  process.exit(2);
3313
3455
  }
3314
- discoveredTestFiles = (await (0, import_agent_qa_core7.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
3456
+ discoveredTestFiles = (await (0, import_agent_qa_core8.discoverWorkspaceFiles)({ workspace: workspacePaths, kind: "test" })).map((record) => record.absolutePath);
3315
3457
  if (discoveredTestFiles.length === 0) {
3316
- console.error(import_picocolors6.default.red("Error: No test files found matching workspace.testMatch"));
3458
+ console.error(import_picocolors7.default.red("Error: No test files found matching workspace.testMatch"));
3317
3459
  for (const p of workspacePaths.testMatch) {
3318
- console.error(import_picocolors6.default.red(` ${p}`));
3460
+ console.error(import_picocolors7.default.red(` ${p}`));
3319
3461
  }
3320
3462
  process.exit(2);
3321
3463
  }
@@ -3330,7 +3472,7 @@ function createRunCommand() {
3330
3472
  }
3331
3473
  const files = discoveredTestFiles;
3332
3474
  if (opts.listTests) {
3333
- console.log(import_picocolors6.default.bold(`
3475
+ console.log(import_picocolors7.default.bold(`
3334
3476
  Discovered ${files.length} test file(s):
3335
3477
  `));
3336
3478
  for (const file of files) {
@@ -3342,7 +3484,7 @@ Discovered ${files.length} test file(s):
3342
3484
  const dashboardCfg2 = config.services?.dashboard;
3343
3485
  const reporterSelectionResult = resolveReporterSelection(opts, Boolean(dashboardCfg2));
3344
3486
  if (reporterSelectionResult.error) {
3345
- console.error(import_picocolors6.default.red(`Error: ${reporterSelectionResult.error}`));
3487
+ console.error(import_picocolors7.default.red(`Error: ${reporterSelectionResult.error}`));
3346
3488
  process.exit(2);
3347
3489
  }
3348
3490
  const reporterSelection = reporterSelectionResult.selection;
@@ -3359,13 +3501,13 @@ Discovered ${files.length} test file(s):
3359
3501
  logStorage = dashboardDb;
3360
3502
  _crashDb = dashboardDb;
3361
3503
  } catch (err) {
3362
- console.warn(import_picocolors6.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
3504
+ console.warn(import_picocolors7.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
3363
3505
  }
3364
3506
  }
3365
3507
  const { parseAllTests, formatParseError } = await import("@vostride/agent-qa-core");
3366
3508
  const parseResult = await parseAllTests(files);
3367
3509
  if (parseResult.errors.length > 0) {
3368
- console.error(import_picocolors6.default.red(`Found ${parseResult.errors.length} parse error(s):
3510
+ console.error(import_picocolors7.default.red(`Found ${parseResult.errors.length} parse error(s):
3369
3511
  `));
3370
3512
  for (const err of parseResult.errors) {
3371
3513
  console.error(formatParseError(err));
@@ -3461,17 +3603,17 @@ Discovered ${files.length} test file(s):
3461
3603
  }
3462
3604
  const filtered = parseResult.tests;
3463
3605
  if (filtered.length === 0) {
3464
- console.log(import_picocolors6.default.yellow("No tests match filters"));
3606
+ console.log(import_picocolors7.default.yellow("No tests match filters"));
3465
3607
  process.exit(0);
3466
3608
  }
3467
3609
  if (opts.dryRun) {
3468
- console.log(import_picocolors6.default.bold(`
3610
+ console.log(import_picocolors7.default.bold(`
3469
3611
  Found ${filtered.length} test(s):
3470
3612
  `));
3471
3613
  for (const test of filtered) {
3472
- console.log(` ${import_picocolors6.default.green("\u25CF")} ${test.name}`);
3614
+ console.log(` ${import_picocolors7.default.green("\u25CF")} ${test.name}`);
3473
3615
  if (opts.configDebug) {
3474
- console.log(import_picocolors6.default.bold(`
3616
+ console.log(import_picocolors7.default.bold(`
3475
3617
  Config for: ${test.name}`));
3476
3618
  console.log(formatConfigDebug(config, test.use, flagOverrides));
3477
3619
  console.log();
@@ -3509,11 +3651,11 @@ Found ${filtered.length} test(s):
3509
3651
  if (reporterSelection.dashboard && dashboardDb) {
3510
3652
  try {
3511
3653
  const { DashboardReporter } = await import("@vostride/agent-qa-dashboard");
3512
- const { resolve: resolve7 } = await import("path");
3654
+ const { resolve: resolve9 } = await import("path");
3513
3655
  const dashArtifactsDir = config.services?.dashboard?.artifactsDir ?? RUNTIME_ARTIFACTS_DIR;
3514
3656
  reporters.push(new DashboardReporter({
3515
3657
  db: dashboardDb,
3516
- artifactsDir: resolve7(configDir, dashArtifactsDir),
3658
+ artifactsDir: resolve9(configDir, dashArtifactsDir),
3517
3659
  redactor: runtimeSecrets.secretRedactor,
3518
3660
  onRunCreated: (runId) => {
3519
3661
  currentRunId = runId;
@@ -3521,7 +3663,7 @@ Found ${filtered.length} test(s):
3521
3663
  }
3522
3664
  }));
3523
3665
  } catch (err) {
3524
- console.warn(import_picocolors6.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
3666
+ console.warn(import_picocolors7.default.yellow(`Warning: Could not load dashboard reporter: ${err instanceof Error ? err.message : String(err)}`));
3525
3667
  }
3526
3668
  }
3527
3669
  const analyticsReporter = createAnalyticsRunReporter({ config, surface: "cli" });
@@ -3535,9 +3677,9 @@ Found ${filtered.length} test(s):
3535
3677
  });
3536
3678
  if (opts.cache !== false) {
3537
3679
  const { FileActionCache } = await import("@vostride/agent-qa-core");
3538
- const { resolve: resolve7 } = await import("path");
3680
+ const { resolve: resolve9 } = await import("path");
3539
3681
  actionCache = new FileActionCache({
3540
- dir: resolve7(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR),
3682
+ dir: resolve9(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR),
3541
3683
  ttl: config.services?.cache?.ttl ?? "7d",
3542
3684
  logger: logger.createScopedLogger("cache")
3543
3685
  });
@@ -3550,7 +3692,7 @@ Found ${filtered.length} test(s):
3550
3692
  let circuitBreaker;
3551
3693
  let circuitBreakerTripped = false;
3552
3694
  const memoryGlobalConfig = config.services?.memory;
3553
- const runtimeMemoryRoot = (0, import_agent_qa_core6.resolveMemoryRoot)(config, configDir);
3695
+ const runtimeMemoryRoot = (0, import_agent_qa_core7.resolveMemoryRoot)(config, configDir);
3554
3696
  if (opts.memory !== false && memoryGlobalConfig?.circuitBreakerEnabled !== false && memoryGlobalConfig?.enabled !== false) {
3555
3697
  const { CircuitBreaker } = await import("@vostride/agent-qa-core");
3556
3698
  circuitBreaker = new CircuitBreaker({
@@ -3590,7 +3732,7 @@ Found ${filtered.length} test(s):
3590
3732
  (f) => parseResult.tests.some((t) => t.name === test.name)
3591
3733
  ) ?? "unknown";
3592
3734
  analyticsFilePath = filePath;
3593
- const runId = process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core6.generateRunId)();
3735
+ const runId = process.env.AGENT_QA_RUN_ID ?? (0, import_agent_qa_core7.generateRunId)();
3594
3736
  analyticsRunId = runId;
3595
3737
  analyticsStartedAt = Date.now();
3596
3738
  currentRunId = runId;
@@ -3615,7 +3757,7 @@ Found ${filtered.length} test(s):
3615
3757
  const testMergedConfig = { ...config, use: mergedUse };
3616
3758
  const effectiveCacheEnabled = opts.cache !== false && mergedUse.cache !== false;
3617
3759
  if (opts.configDebug) {
3618
- console.log(import_picocolors6.default.bold(`
3760
+ console.log(import_picocolors7.default.bold(`
3619
3761
  Config for: ${test.name}`));
3620
3762
  console.log(formatConfigDebug(config, test.use, testFlagOverrides));
3621
3763
  console.log();
@@ -3642,7 +3784,7 @@ Config for: ${test.name}`));
3642
3784
  useDeviceName: testMergedUse.device,
3643
3785
  appState: testMergedUse.mobile?.appState,
3644
3786
  localBindings: testLocalBindings,
3645
- configFilePath: (0, import_node_path7.resolve)(configFilePath),
3787
+ configFilePath: (0, import_node_path9.resolve)(configFilePath),
3646
3788
  localConfigFilePath: testLocalBindings?.filePath,
3647
3789
  appiumUrl: process.env.AGENT_QA_APPIUM_URL
3648
3790
  });
@@ -3688,7 +3830,7 @@ Config for: ${test.name}`));
3688
3830
  testMobileResolved = { ...testMobileResolved, appium: { url: appiumUrl, managed: !process.env.AGENT_QA_APPIUM_URL } };
3689
3831
  }
3690
3832
  } catch (err) {
3691
- throw new import_agent_qa_core6.MobileSetupError({
3833
+ throw new import_agent_qa_core7.MobileSetupError({
3692
3834
  category: "appium-startup",
3693
3835
  message: `Could not auto-start Appium: ${err instanceof Error ? err.message : String(err)}`,
3694
3836
  platform: testPlatform,
@@ -3725,7 +3867,7 @@ Config for: ${test.name}`));
3725
3867
  });
3726
3868
  }
3727
3869
  if (effectiveLogLevel === "debug") {
3728
- console.log(import_picocolors6.default.dim(` Platform: ${testPlatform}${useFarm ? ` (farm: ${testResolvedDevice?.transport})` : ""}${testPlatform !== "web" ? " (adapter re-created)" : ""}`));
3870
+ console.log(import_picocolors7.default.dim(` Platform: ${testPlatform}${useFarm ? ` (farm: ${testResolvedDevice?.transport})` : ""}${testPlatform !== "web" ? " (adapter re-created)" : ""}`));
3729
3871
  }
3730
3872
  }
3731
3873
  const mergedForLLM = { registry: config.registry, use: testMergedConfig.use ?? config.use };
@@ -3736,7 +3878,7 @@ Config for: ${test.name}`));
3736
3878
  process.env.AGENT_QA_LLM_MODEL = plannerCfg.model;
3737
3879
  process.env.AGENT_QA_LLM_PROVIDER = plannerCfg.provider;
3738
3880
  if (effectiveLogLevel === "debug") {
3739
- console.log(import_picocolors6.default.dim(` Model: ${plannerCfg.model} (${plannerCfg.provider})`));
3881
+ console.log(import_picocolors7.default.dim(` Model: ${plannerCfg.model} (${plannerCfg.provider})`));
3740
3882
  }
3741
3883
  const plannerModel = await createModel(plannerModelConfig);
3742
3884
  const verifierModel = await createModel(verifierModelConfig);
@@ -3901,7 +4043,7 @@ Config for: ${test.name}`));
3901
4043
  const testId = testDef["test-id"] ?? testDef.name;
3902
4044
  memoryInitParams = { product, testId, memoryRoot };
3903
4045
  } catch (err) {
3904
- console.warn(` ${import_picocolors6.default.dim("Memory:")} init error -- ${err.message}`);
4046
+ console.warn(` ${import_picocolors7.default.dim("Memory:")} init error -- ${err.message}`);
3905
4047
  }
3906
4048
  }
3907
4049
  const result = await runTestWithRetry(testDef, {
@@ -3931,6 +4073,7 @@ Config for: ${test.name}`));
3931
4073
  inlineHookDefs: resolvedHooks,
3932
4074
  inlineHookSandboxOptions: sandboxOptions,
3933
4075
  logCapture: testLogCapture,
4076
+ accessibility: config.services?.accessibility,
3934
4077
  screenshotSize: plannerCfg.screenshotSize,
3935
4078
  effectiveResolution: plannerCfg.effectiveResolution,
3936
4079
  contextWindow: plannerCfg.contextWindow,
@@ -3995,7 +4138,7 @@ Config for: ${test.name}`));
3995
4138
  try {
3996
4139
  const { shouldAblate, collectAllInjectedIds, deprecateOnFailure } = await import("@vostride/agent-qa-core");
3997
4140
  if (shouldAblate(result, memoryProvider)) {
3998
- console.log(` ${import_picocolors6.default.dim("Memory:")} ablation retry -- re-running without memory...`);
4141
+ console.log(` ${import_picocolors7.default.dim("Memory:")} ablation retry -- re-running without memory...`);
3999
4142
  const ablationAdapter = await createPlatformAdapter(testPlatform);
4000
4143
  const mergedBrowser = mergedUse.browser;
4001
4144
  const ablationPlatformConfig = buildPlatformConfig(testPlatform, testResolvedDevice, testTimeoutsForSetup, mergedBrowser ?? config.use?.browser, testMergedConfig.use?.logCapture ?? config.use?.logCapture, testFarmSession, testMobileResolved);
@@ -4032,6 +4175,7 @@ Config for: ${test.name}`));
4032
4175
  inlineHookDefs: resolvedHooks,
4033
4176
  inlineHookSandboxOptions: sandboxOptions,
4034
4177
  logCapture: testLogCapture,
4178
+ accessibility: config.services?.accessibility,
4035
4179
  screenshotSize: plannerCfg.screenshotSize,
4036
4180
  effectiveResolution: plannerCfg.effectiveResolution,
4037
4181
  contextWindow: plannerCfg.contextWindow
@@ -4040,7 +4184,7 @@ Config for: ${test.name}`));
4040
4184
  circuitBreaker.record({ withMemory: false, passed: ablationResult.status === "passed" });
4041
4185
  }
4042
4186
  if (ablationResult.status === "passed") {
4043
- console.log(` ${import_picocolors6.default.dim("Memory:")} ablation confirmed memory caused failure -- penalizing observations`);
4187
+ console.log(` ${import_picocolors7.default.dim("Memory:")} ablation confirmed memory caused failure -- penalizing observations`);
4044
4188
  const injectedMap = collectAllInjectedIds(result, memoryProvider);
4045
4189
  await deprecateOnFailure({
4046
4190
  testResult: result,
@@ -4051,14 +4195,14 @@ Config for: ${test.name}`));
4051
4195
  });
4052
4196
  ablationHandledDeprecation = true;
4053
4197
  } else {
4054
- console.log(` ${import_picocolors6.default.dim("Memory:")} ablation retry also failed -- memory not the cause`);
4198
+ console.log(` ${import_picocolors7.default.dim("Memory:")} ablation retry also failed -- memory not the cause`);
4055
4199
  }
4056
4200
  } finally {
4057
4201
  await ablationAdapter.cleanup();
4058
4202
  }
4059
4203
  }
4060
4204
  } catch (err) {
4061
- console.warn(` ${import_picocolors6.default.dim("Memory:")} ablation error -- ${err.message}`);
4205
+ console.warn(` ${import_picocolors7.default.dim("Memory:")} ablation error -- ${err.message}`);
4062
4206
  }
4063
4207
  }
4064
4208
  if (memoryProvider && memoryConfig?.curatorEnabled !== false && !ablationHandledDeprecation) {
@@ -4092,7 +4236,7 @@ Config for: ${test.name}`));
4092
4236
  }
4093
4237
  }
4094
4238
  } catch (err) {
4095
- console.warn(` ${import_picocolors6.default.dim("Memory:")} curator error -- ${err.message}`);
4239
+ console.warn(` ${import_picocolors7.default.dim("Memory:")} curator error -- ${err.message}`);
4096
4240
  }
4097
4241
  }
4098
4242
  results.push(result);
@@ -4103,13 +4247,13 @@ Config for: ${test.name}`));
4103
4247
  }
4104
4248
  if (circuitBreaker?.isTripped() && !circuitBreakerTripped) {
4105
4249
  circuitBreakerTripped = true;
4106
- console.log(` ${import_picocolors6.default.yellow("Memory: circuit breaker tripped -- disabling memory injection for remaining tests")}`);
4250
+ console.log(` ${import_picocolors7.default.yellow("Memory: circuit breaker tripped -- disabling memory injection for remaining tests")}`);
4107
4251
  }
4108
4252
  if (result.status === "failed" && actionCache && effectiveCacheEnabled && result.steps.length > 0) {
4109
4253
  const { hashStepInstruction: hashStepInstruction2 } = await import("@vostride/agent-qa-core");
4110
4254
  const { rm: rm3 } = await import("fs/promises");
4111
- const { resolve: resolve7, join: join8 } = await import("path");
4112
- const cacheDir = resolve7(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR);
4255
+ const { resolve: resolve9, join: join8 } = await import("path");
4256
+ const cacheDir = resolve9(configDir, config.services?.cache?.dir ?? RUNTIME_CACHE_DIR);
4113
4257
  const cacheLog = logger.createScopedLogger("cache");
4114
4258
  let purged = 0;
4115
4259
  for (let i = 0; i < result.steps.length; i++) {
@@ -4188,13 +4332,13 @@ Config for: ${test.name}`));
4188
4332
  }
4189
4333
  const parallelBatchFailed = await flushParallelRuns();
4190
4334
  if (opts.bail && parallelBatchFailed) {
4191
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failure"));
4335
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failure"));
4192
4336
  bailTriggered = true;
4193
4337
  break;
4194
4338
  }
4195
4339
  const outcome = await runOneDirectTest(test);
4196
4340
  if (opts.bail && outcome.failed) {
4197
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failure"));
4341
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failure"));
4198
4342
  bailTriggered = true;
4199
4343
  break;
4200
4344
  }
@@ -4202,7 +4346,7 @@ Config for: ${test.name}`));
4202
4346
  if (!bailTriggered) {
4203
4347
  const parallelBatchFailed = await flushParallelRuns();
4204
4348
  if (opts.bail && parallelBatchFailed) {
4205
- console.log(import_picocolors6.default.yellow("\nBailing out \u2014 stopping after first failure"));
4349
+ console.log(import_picocolors7.default.yellow("\nBailing out \u2014 stopping after first failure"));
4206
4350
  }
4207
4351
  }
4208
4352
  releaseAppium();
@@ -4221,7 +4365,7 @@ Config for: ${test.name}`));
4221
4365
  const errMsg = err instanceof Error ? err.message : String(err);
4222
4366
  const errStack = err instanceof Error ? err.stack ?? "" : "";
4223
4367
  const formattedError = formatFrameworkError(errMsg);
4224
- console.error(import_picocolors6.default.red(formattedError.displayMessage));
4368
+ console.error(import_picocolors7.default.red(formattedError.displayMessage));
4225
4369
  if (_crashDb) {
4226
4370
  const fullError = `${errMsg}
4227
4371
  ${errStack}`;
@@ -4272,11 +4416,11 @@ ${errStack}`;
4272
4416
  }
4273
4417
 
4274
4418
  // src/commands/dashboard.ts
4275
- var import_commander6 = require("commander");
4276
- var import_node_path8 = require("path");
4419
+ var import_commander7 = require("commander");
4420
+ var import_node_path10 = require("path");
4277
4421
  var import_node_module2 = require("module");
4278
- var import_picocolors7 = __toESM(require("picocolors"), 1);
4279
- var import_agent_qa_core9 = require("@vostride/agent-qa-core");
4422
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
4423
+ var import_agent_qa_core10 = require("@vostride/agent-qa-core");
4280
4424
  init_config();
4281
4425
  init_llm_utils();
4282
4426
  init_model_defaults();
@@ -4288,9 +4432,9 @@ async function startDashboardBackedServices(opts, command, labels) {
4288
4432
  const config = await resolveConfig({
4289
4433
  configPath: globalOpts.config
4290
4434
  });
4291
- const configFilePath = (0, import_node_path8.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
4292
- const configDir = (0, import_node_path8.dirname)(configFilePath);
4293
- const workspacePaths = (0, import_agent_qa_core9.resolveWorkspacePaths)({
4435
+ const configFilePath = (0, import_node_path10.resolve)(globalOpts.config ?? "agent-qa.config.yaml");
4436
+ const configDir = (0, import_node_path10.dirname)(configFilePath);
4437
+ const workspacePaths = (0, import_agent_qa_core10.resolveWorkspacePaths)({
4294
4438
  config,
4295
4439
  configPath: configFilePath,
4296
4440
  requireExistingFiles: true
@@ -4329,17 +4473,17 @@ async function startDashboardBackedServices(opts, command, labels) {
4329
4473
  try {
4330
4474
  const require2 = (0, import_node_module2.createRequire)(import_meta3.url);
4331
4475
  const pkgPath = require2.resolve("@vostride/agent-qa-dashboard-ui/package.json");
4332
- uiDir = (0, import_node_path8.resolve)((0, import_node_path8.dirname)(pkgPath), "dist");
4476
+ uiDir = (0, import_node_path10.resolve)((0, import_node_path10.dirname)(pkgPath), "dist");
4333
4477
  } catch {
4334
4478
  if (globalOpts.verbose) {
4335
- console.log(import_picocolors7.default.yellow("Warning: @vostride/agent-qa-dashboard-ui not found \u2014 API-only mode"));
4479
+ console.log(import_picocolors8.default.yellow("Warning: @vostride/agent-qa-dashboard-ui not found \u2014 API-only mode"));
4336
4480
  }
4337
4481
  }
4338
4482
  const { close } = await startServer({
4339
4483
  db,
4340
4484
  port,
4341
4485
  uiDir,
4342
- artifactsDir: (0, import_node_path8.resolve)(configDir, artifactsDir),
4486
+ artifactsDir: (0, import_node_path10.resolve)(configDir, artifactsDir),
4343
4487
  workspacePaths,
4344
4488
  configPath: configFilePath,
4345
4489
  mcp: mcpConfig,
@@ -4355,22 +4499,22 @@ async function startDashboardBackedServices(opts, command, labels) {
4355
4499
  const shutdown = async () => {
4356
4500
  if (shuttingDown) return;
4357
4501
  shuttingDown = true;
4358
- console.log(import_picocolors7.default.dim(`
4502
+ console.log(import_picocolors8.default.dim(`
4359
4503
  Shutting down ${labels.shutdown}...`));
4360
4504
  close();
4361
- await (0, import_agent_qa_core9.flushAnalytics)({ config }).catch(() => {
4505
+ await (0, import_agent_qa_core10.flushAnalytics)({ config }).catch(() => {
4362
4506
  });
4363
4507
  process.exit(0);
4364
4508
  };
4365
4509
  process.on("SIGINT", shutdown);
4366
4510
  process.on("SIGTERM", shutdown);
4367
4511
  } catch (err) {
4368
- console.error(import_picocolors7.default.red(`${labels.error} error: ${err instanceof Error ? err.message : String(err)}`));
4512
+ console.error(import_picocolors8.default.red(`${labels.error} error: ${err instanceof Error ? err.message : String(err)}`));
4369
4513
  process.exit(2);
4370
4514
  }
4371
4515
  }
4372
4516
  function createDashboardCommand() {
4373
- const cmd = new import_commander6.Command("dashboard").description("Start the dashboard web server").option("--port <number>", "server port (default: 3470)").option("--db <path>", "database file path").option("--open", "open browser after start").action(async (opts, command) => {
4517
+ const cmd = new import_commander7.Command("dashboard").description("Start the dashboard web server").option("--port <number>", "server port (default: 3470)").option("--db <path>", "database file path").option("--open", "open browser after start").action(async (opts, command) => {
4374
4518
  await startDashboardBackedServices(opts, command, {
4375
4519
  error: "Dashboard",
4376
4520
  shutdown: "dashboard"
@@ -4379,7 +4523,7 @@ function createDashboardCommand() {
4379
4523
  return cmd;
4380
4524
  }
4381
4525
  function createServeCommand() {
4382
- const cmd = new import_commander6.Command("serve").description("Start configured local agent-qa services").action(async (_opts, command) => {
4526
+ const cmd = new import_commander7.Command("serve").description("Start configured local agent-qa services").action(async (_opts, command) => {
4383
4527
  await startDashboardBackedServices({}, command, {
4384
4528
  error: "Serve",
4385
4529
  shutdown: "local services"
@@ -4389,12 +4533,12 @@ function createServeCommand() {
4389
4533
  }
4390
4534
 
4391
4535
  // src/commands/mcp.ts
4392
- var import_commander7 = require("commander");
4393
- var import_picocolors8 = __toESM(require("picocolors"), 1);
4394
- var import_agent_qa_core10 = require("@vostride/agent-qa-core");
4536
+ var import_commander8 = require("commander");
4537
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
4538
+ var import_agent_qa_core11 = require("@vostride/agent-qa-core");
4395
4539
  init_config();
4396
4540
  function createMcpCommand() {
4397
- const cmd = new import_commander7.Command("mcp").description("Start MCP (Model Context Protocol) server over stdio").action(async (command) => {
4541
+ const cmd = new import_commander8.Command("mcp").description("Start MCP (Model Context Protocol) server over stdio").action(async (command) => {
4398
4542
  try {
4399
4543
  const program2 = command.parent;
4400
4544
  const globalOpts = program2?.opts() ?? {};
@@ -4408,11 +4552,11 @@ function createMcpCommand() {
4408
4552
  try {
4409
4553
  await startMcpServer({ analyticsConfig: config });
4410
4554
  } finally {
4411
- await (0, import_agent_qa_core10.flushAnalytics)(config ? { config } : void 0).catch(() => {
4555
+ await (0, import_agent_qa_core11.flushAnalytics)(config ? { config } : void 0).catch(() => {
4412
4556
  });
4413
4557
  }
4414
4558
  } catch (err) {
4415
- console.error(import_picocolors8.default.red("Failed to start MCP server:"), err instanceof Error ? err.message : String(err));
4559
+ console.error(import_picocolors9.default.red("Failed to start MCP server:"), err instanceof Error ? err.message : String(err));
4416
4560
  process.exitCode = 1;
4417
4561
  }
4418
4562
  });
@@ -4420,8 +4564,8 @@ function createMcpCommand() {
4420
4564
  }
4421
4565
 
4422
4566
  // src/commands/config.ts
4423
- var import_commander8 = require("commander");
4424
- var import_picocolors9 = __toESM(require("picocolors"), 1);
4567
+ var import_commander9 = require("commander");
4568
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
4425
4569
  var import_yaml5 = require("yaml");
4426
4570
  var import_promises3 = require("fs/promises");
4427
4571
  var import_node_readline2 = require("readline");
@@ -4451,10 +4595,10 @@ async function readStdin() {
4451
4595
  }
4452
4596
  function plainPrompt(question) {
4453
4597
  const rl = (0, import_node_readline2.createInterface)({ input: process.stdin, output: process.stdout });
4454
- return new Promise((resolve7) => {
4598
+ return new Promise((resolve9) => {
4455
4599
  rl.question(question, (answer) => {
4456
4600
  rl.close();
4457
- resolve7(answer.trim());
4601
+ resolve9(answer.trim());
4458
4602
  });
4459
4603
  });
4460
4604
  }
@@ -4467,14 +4611,14 @@ async function getValue(key, providedValue) {
4467
4611
  }
4468
4612
  async function setAction(key, value) {
4469
4613
  if (isSensitiveKey(key)) {
4470
- console.error(import_picocolors9.default.red("Credential values are not written by config set."));
4614
+ console.error(import_picocolors10.default.red("Credential values are not written by config set."));
4471
4615
  console.error("Use agent-qa auth set --config <name> --type api-key|bearer-token for compatible or Gemini configs.");
4472
4616
  console.error("Use plugins.auth plus the dashboard auth flow for subscription configs.");
4473
4617
  return;
4474
4618
  }
4475
4619
  const resolved = await getValue(key, value);
4476
4620
  if (!resolved) {
4477
- console.error(import_picocolors9.default.red("No value provided."));
4621
+ console.error(import_picocolors10.default.red("No value provided."));
4478
4622
  return;
4479
4623
  }
4480
4624
  const configPath = "agent-qa.config.yaml";
@@ -4491,7 +4635,7 @@ async function setAction(key, value) {
4491
4635
  }
4492
4636
  doc.setIn(key.split("."), resolved);
4493
4637
  await (0, import_promises3.writeFile)(configPath, doc.toString());
4494
- console.log(import_picocolors9.default.green(`\u2713 ${key} = ${resolved} saved to agent-qa.config.yaml`));
4638
+ console.log(import_picocolors10.default.green(`\u2713 ${key} = ${resolved} saved to agent-qa.config.yaml`));
4495
4639
  }
4496
4640
  async function showAction() {
4497
4641
  const fileConfig = await loadConfigFile("agent-qa.config.yaml");
@@ -4499,20 +4643,20 @@ async function showAction() {
4499
4643
  const { mergeConfigs: mergeConfigs2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4500
4644
  const globalConfig = mergeConfigs2(fileConfig, envOverrides, {});
4501
4645
  const table = formatConfigDebug(globalConfig, {}, {});
4502
- console.log(import_picocolors9.default.bold("\nResolved Configuration:\n"));
4646
+ console.log(import_picocolors10.default.bold("\nResolved Configuration:\n"));
4503
4647
  console.log(table);
4504
4648
  console.log();
4505
4649
  }
4506
4650
  function createConfigCommand() {
4507
- const cmd = new import_commander8.Command("config").description("Manage configuration");
4651
+ const cmd = new import_commander9.Command("config").description("Manage configuration");
4508
4652
  cmd.command("set <key> [value]").description("Set a config value").action(setAction);
4509
4653
  cmd.command("show").description("Show resolved config with source attribution").action(showAction);
4510
4654
  return cmd;
4511
4655
  }
4512
4656
 
4513
4657
  // src/commands/queue.ts
4514
- var import_commander9 = require("commander");
4515
- var import_picocolors10 = __toESM(require("picocolors"), 1);
4658
+ var import_commander10 = require("commander");
4659
+ var import_picocolors11 = __toESM(require("picocolors"), 1);
4516
4660
  init_config();
4517
4661
  function timeAgo(dateStr) {
4518
4662
  const diff = Date.now() - new Date(dateStr).getTime();
@@ -4536,7 +4680,7 @@ async function resolveServerUrl(opts, globalOpts) {
4536
4680
  }
4537
4681
  }
4538
4682
  function createQueueCommand() {
4539
- const cmd = new import_commander9.Command("queue").description("Manage the test execution queue");
4683
+ const cmd = new import_commander10.Command("queue").description("Manage the test execution queue");
4540
4684
  cmd.command("list").description("Show pending and running jobs in the queue").option("--json", "output raw JSON").option("--all", "include recently completed jobs").option("--server <url>", "dashboard server URL").action(async (opts, command) => {
4541
4685
  try {
4542
4686
  const program2 = command.parent.parent;
@@ -4547,13 +4691,13 @@ function createQueueCommand() {
4547
4691
  try {
4548
4692
  const response = await fetch(endpoint);
4549
4693
  if (!response.ok) {
4550
- console.error(import_picocolors10.default.red(`Server returned ${response.status}: ${response.statusText}`));
4694
+ console.error(import_picocolors11.default.red(`Server returned ${response.status}: ${response.statusText}`));
4551
4695
  process.exit(1);
4552
4696
  }
4553
4697
  data = await response.json();
4554
4698
  } catch {
4555
- console.error(import_picocolors10.default.red(`Could not connect to dashboard server at ${serverUrl}`));
4556
- console.error(import_picocolors10.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
4699
+ console.error(import_picocolors11.default.red(`Could not connect to dashboard server at ${serverUrl}`));
4700
+ console.error(import_picocolors11.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
4557
4701
  process.exit(1);
4558
4702
  }
4559
4703
  if (opts.json) {
@@ -4561,19 +4705,19 @@ function createQueueCommand() {
4561
4705
  return;
4562
4706
  }
4563
4707
  console.log();
4564
- console.log(import_picocolors10.default.bold("Queue Status"));
4565
- console.log(` Concurrency: ${import_picocolors10.default.blue(String(data.concurrency))} slots (${import_picocolors10.default.yellow(String(data.activeSlots))} active)`);
4708
+ console.log(import_picocolors11.default.bold("Queue Status"));
4709
+ console.log(` Concurrency: ${import_picocolors11.default.blue(String(data.concurrency))} slots (${import_picocolors11.default.yellow(String(data.activeSlots))} active)`);
4566
4710
  console.log();
4567
4711
  const pending = data.pending?.jobs ?? [];
4568
4712
  const running = data.running?.jobs ?? [];
4569
4713
  if (pending.length === 0 && running.length === 0) {
4570
- console.log(import_picocolors10.default.dim(" Queue is empty \u2014 no pending or running jobs"));
4714
+ console.log(import_picocolors11.default.dim(" Queue is empty \u2014 no pending or running jobs"));
4571
4715
  console.log();
4572
4716
  return;
4573
4717
  }
4574
4718
  if (pending.length > 0) {
4575
- console.log(import_picocolors10.default.bold(`Pending (${pending.length} jobs):`));
4576
- console.log(import_picocolors10.default.dim(" # ID Name Priority Source Queued"));
4719
+ console.log(import_picocolors11.default.bold(`Pending (${pending.length} jobs):`));
4720
+ console.log(import_picocolors11.default.dim(" # ID Name Priority Source Queued"));
4577
4721
  for (let i = 0; i < pending.length; i++) {
4578
4722
  const job = pending[i];
4579
4723
  const num = String(i + 1).padEnd(3);
@@ -4582,37 +4726,37 @@ function createQueueCommand() {
4582
4726
  const priority = String(job.priority ?? 0).padEnd(8);
4583
4727
  const source = (job.source ?? "").padEnd(10);
4584
4728
  const queued = job.createdAt ? timeAgo(job.createdAt) : "";
4585
- console.log(` ${num} ${import_picocolors10.default.dim(id)} ${name} ${priority} ${source} ${import_picocolors10.default.dim(queued)}`);
4729
+ console.log(` ${num} ${import_picocolors11.default.dim(id)} ${name} ${priority} ${source} ${import_picocolors11.default.dim(queued)}`);
4586
4730
  }
4587
4731
  console.log();
4588
4732
  }
4589
4733
  if (running.length > 0) {
4590
- console.log(import_picocolors10.default.bold(`Running (${running.length} jobs):`));
4591
- console.log(import_picocolors10.default.dim(" ID Name Source Started"));
4734
+ console.log(import_picocolors11.default.bold(`Running (${running.length} jobs):`));
4735
+ console.log(import_picocolors11.default.dim(" ID Name Source Started"));
4592
4736
  for (const job of running) {
4593
4737
  const id = (job.id ?? "").slice(0, 12).padEnd(12);
4594
4738
  const name = (job.name ?? "").slice(0, 18).padEnd(18);
4595
4739
  const source = (job.source ?? "").padEnd(10);
4596
4740
  const started = job.startedAt ? timeAgo(job.startedAt) : "";
4597
- console.log(` ${import_picocolors10.default.green(id)} ${name} ${source} ${import_picocolors10.default.dim(started)}`);
4741
+ console.log(` ${import_picocolors11.default.green(id)} ${name} ${source} ${import_picocolors11.default.dim(started)}`);
4598
4742
  }
4599
4743
  console.log();
4600
4744
  }
4601
4745
  if (opts.all && data.recent?.length > 0) {
4602
- console.log(import_picocolors10.default.bold(`Recent (${data.recent.length} jobs):`));
4603
- console.log(import_picocolors10.default.dim(" ID Name Status Duration"));
4746
+ console.log(import_picocolors11.default.bold(`Recent (${data.recent.length} jobs):`));
4747
+ console.log(import_picocolors11.default.dim(" ID Name Status Duration"));
4604
4748
  for (const job of data.recent) {
4605
4749
  const id = (job.id ?? "").slice(0, 12).padEnd(12);
4606
4750
  const name = (job.name ?? "").slice(0, 18).padEnd(18);
4607
4751
  const status = (job.status ?? "").padEnd(8);
4608
4752
  const duration = job.duration ? `${Math.round(job.duration / 1e3)}s` : "";
4609
- const statusColor = job.status === "passed" ? import_picocolors10.default.green : job.status === "failed" ? import_picocolors10.default.red : import_picocolors10.default.dim;
4610
- console.log(` ${import_picocolors10.default.dim(id)} ${name} ${statusColor(status)} ${import_picocolors10.default.dim(duration)}`);
4753
+ const statusColor = job.status === "passed" ? import_picocolors11.default.green : job.status === "failed" ? import_picocolors11.default.red : import_picocolors11.default.dim;
4754
+ console.log(` ${import_picocolors11.default.dim(id)} ${name} ${statusColor(status)} ${import_picocolors11.default.dim(duration)}`);
4611
4755
  }
4612
4756
  console.log();
4613
4757
  }
4614
4758
  } catch (err) {
4615
- console.error(import_picocolors10.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
4759
+ console.error(import_picocolors11.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
4616
4760
  process.exit(1);
4617
4761
  }
4618
4762
  });
@@ -4625,25 +4769,25 @@ function createQueueCommand() {
4625
4769
  try {
4626
4770
  response = await fetch(`${serverUrl}/api/runs/${runId}/cancel`, { method: "POST" });
4627
4771
  } catch {
4628
- console.error(import_picocolors10.default.red(`Could not connect to dashboard server at ${serverUrl}`));
4629
- console.error(import_picocolors10.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
4772
+ console.error(import_picocolors11.default.red(`Could not connect to dashboard server at ${serverUrl}`));
4773
+ console.error(import_picocolors11.default.dim("Make sure the dashboard is running: agent-qa dashboard"));
4630
4774
  process.exit(1);
4631
4775
  }
4632
4776
  if (response.ok) {
4633
4777
  const data = await response.json();
4634
4778
  if (data.cancelled) {
4635
- console.log(import_picocolors10.default.green(`\u2713 Cancelled job ${runId}`));
4779
+ console.log(import_picocolors11.default.green(`\u2713 Cancelled job ${runId}`));
4636
4780
  } else {
4637
- console.log(import_picocolors10.default.red(`Failed to cancel: ${data.error ?? "Unknown error"}`));
4781
+ console.log(import_picocolors11.default.red(`Failed to cancel: ${data.error ?? "Unknown error"}`));
4638
4782
  }
4639
4783
  } else if (response.status === 404) {
4640
- console.log(import_picocolors10.default.red(`Job not found: ${runId}`));
4784
+ console.log(import_picocolors11.default.red(`Job not found: ${runId}`));
4641
4785
  } else {
4642
4786
  const data = await response.json().catch(() => ({}));
4643
- console.log(import_picocolors10.default.red(`Failed to cancel: ${data.error ?? response.statusText}`));
4787
+ console.log(import_picocolors11.default.red(`Failed to cancel: ${data.error ?? response.statusText}`));
4644
4788
  }
4645
4789
  } catch (err) {
4646
- console.error(import_picocolors10.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
4790
+ console.error(import_picocolors11.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
4647
4791
  process.exit(1);
4648
4792
  }
4649
4793
  });
@@ -4651,24 +4795,24 @@ function createQueueCommand() {
4651
4795
  }
4652
4796
 
4653
4797
  // src/commands/validate.ts
4654
- var import_commander10 = require("commander");
4655
- var import_agent_qa_core11 = require("@vostride/agent-qa-core");
4798
+ var import_commander11 = require("commander");
4799
+ var import_agent_qa_core12 = require("@vostride/agent-qa-core");
4656
4800
  init_config();
4657
4801
  function createValidateCommand() {
4658
- const cmd = new import_commander10.Command("validate").description("Validate config, test files, and suite references").argument("[files...]", "specific files to validate (default: auto-discover all)").action(async (files, command) => {
4802
+ const cmd = new import_commander11.Command("validate").description("Validate config, test files, and suite references").argument("[files...]", "specific files to validate (default: auto-discover all)").action(async (files, command) => {
4659
4803
  const configPath = command.parent?.opts().config ?? "agent-qa.config.yaml";
4660
4804
  let workspace;
4661
4805
  try {
4662
4806
  const config = await resolveConfig({ configPath });
4663
- workspace = (0, import_agent_qa_core11.resolveWorkspacePaths)({ config, configPath });
4807
+ workspace = (0, import_agent_qa_core12.resolveWorkspacePaths)({ config, configPath });
4664
4808
  } catch {
4665
4809
  }
4666
- const result = await (0, import_agent_qa_core11.validateProject)({
4810
+ const result = await (0, import_agent_qa_core12.validateProject)({
4667
4811
  configPath,
4668
4812
  files: files.length > 0 ? files : void 0,
4669
4813
  workspace
4670
4814
  });
4671
- const output = (0, import_agent_qa_core11.formatDiagnostics)(result);
4815
+ const output = (0, import_agent_qa_core12.formatDiagnostics)(result);
4672
4816
  process.stdout.write(output);
4673
4817
  if (result.errorCount > 0) {
4674
4818
  process.exitCode = 1;
@@ -4678,9 +4822,9 @@ function createValidateCommand() {
4678
4822
  }
4679
4823
 
4680
4824
  // src/commands/auth.ts
4681
- var import_commander11 = require("commander");
4825
+ var import_commander12 = require("commander");
4682
4826
  var import_node_child_process4 = require("child_process");
4683
- var import_picocolors11 = __toESM(require("picocolors"), 1);
4827
+ var import_picocolors12 = __toESM(require("picocolors"), 1);
4684
4828
  var import_input2 = __toESM(require("@inquirer/input"), 1);
4685
4829
  var import_password = __toESM(require("@inquirer/password"), 1);
4686
4830
  var PROVIDER_LABELS = {
@@ -4715,10 +4859,10 @@ function getGlobalConfigPath2(command) {
4715
4859
  return command.parent?.parent?.opts().config ?? command.parent?.opts().config;
4716
4860
  }
4717
4861
  function printCredentialGuidance(configName, provider) {
4718
- console.log(import_picocolors11.default.red(`Config ${configName} uses ${providerLabel(provider)} credentials.`));
4719
- console.log(import_picocolors11.default.dim("Use agent-qa auth set --config <name> --type api-key"));
4862
+ console.log(import_picocolors12.default.red(`Config ${configName} uses ${providerLabel(provider)} credentials.`));
4863
+ console.log(import_picocolors12.default.dim("Use agent-qa auth set --config <name> --type api-key"));
4720
4864
  if (provider === "anthropic-compatible") {
4721
- console.log(import_picocolors11.default.dim("or --type bearer-token"));
4865
+ console.log(import_picocolors12.default.dim("or --type bearer-token"));
4722
4866
  }
4723
4867
  }
4724
4868
  function applyAuthToModelConfig(llmConfig, auth) {
@@ -4738,15 +4882,15 @@ function applyAuthToModelConfig(llmConfig, auth) {
4738
4882
  function statusForResolvedAuth(auth) {
4739
4883
  switch (auth.kind) {
4740
4884
  case "api-key":
4741
- return import_picocolors11.default.green("Saved API key");
4885
+ return import_picocolors12.default.green("Saved API key");
4742
4886
  case "bearer-token":
4743
- return import_picocolors11.default.green("Saved bearer token");
4887
+ return import_picocolors12.default.green("Saved bearer token");
4744
4888
  case "auth-fetch":
4745
- return Number(auth.expires ?? Date.now() + 1) <= Date.now() ? import_picocolors11.default.red("Expired") : import_picocolors11.default.green("OAuth connected");
4889
+ return Number(auth.expires ?? Date.now() + 1) <= Date.now() ? import_picocolors12.default.red("Expired") : import_picocolors12.default.green("OAuth connected");
4746
4890
  case "unauthenticated":
4747
- return import_picocolors11.default.dim("No credential");
4891
+ return import_picocolors12.default.dim("No credential");
4748
4892
  case "missing":
4749
- return import_picocolors11.default.red("Missing credential");
4893
+ return import_picocolors12.default.red("Missing credential");
4750
4894
  }
4751
4895
  }
4752
4896
  function extractProviderError(err) {
@@ -4777,16 +4921,16 @@ function openAuthorizationUrl(url) {
4777
4921
  });
4778
4922
  }
4779
4923
  function printMissingSubscriptionPluginGuidance(configName, provider) {
4780
- console.log(import_picocolors11.default.red(`Provider "${provider}" is configured for "${configName}", but no auth plugin is registered.`));
4781
- console.log(import_picocolors11.default.dim(`Config: ${configName}`));
4782
- console.log(import_picocolors11.default.dim(`Provider: ${provider}`));
4783
- console.log(import_picocolors11.default.dim("Add the subscription auth plugin to package.json and install with your package manager:"));
4784
- console.log(import_picocolors11.default.dim(' "devDependencies": { "@vostride/agent-qa-subscription-auth": "<agent-qa version>" }'));
4785
- console.log(import_picocolors11.default.dim("Declare it in agent-qa.config.yaml under plugins.auth:"));
4786
- console.log(import_picocolors11.default.dim(" plugins:"));
4787
- console.log(import_picocolors11.default.dim(" auth:"));
4788
- console.log(import_picocolors11.default.dim(" - package: @vostride/agent-qa-subscription-auth"));
4789
- console.log(import_picocolors11.default.dim("Then authenticate from agent-qa dashboard or rerun this command."));
4924
+ console.log(import_picocolors12.default.red(`Provider "${provider}" is configured for "${configName}", but no auth plugin is registered.`));
4925
+ console.log(import_picocolors12.default.dim(`Config: ${configName}`));
4926
+ console.log(import_picocolors12.default.dim(`Provider: ${provider}`));
4927
+ console.log(import_picocolors12.default.dim("Add the subscription auth plugin to package.json and install with your package manager:"));
4928
+ console.log(import_picocolors12.default.dim(' "devDependencies": { "@vostride/agent-qa-subscription-auth": "<agent-qa version>" }'));
4929
+ console.log(import_picocolors12.default.dim("Declare it in agent-qa.config.yaml under plugins.auth:"));
4930
+ console.log(import_picocolors12.default.dim(" plugins:"));
4931
+ console.log(import_picocolors12.default.dim(" auth:"));
4932
+ console.log(import_picocolors12.default.dim(" - package: @vostride/agent-qa-subscription-auth"));
4933
+ console.log(import_picocolors12.default.dim("Then authenticate from agent-qa dashboard or rerun this command."));
4790
4934
  }
4791
4935
  async function readTokensFromPlugin(provider, plugin, started) {
4792
4936
  if (started.waitForTokens) {
@@ -4794,19 +4938,19 @@ async function readTokensFromPlugin(provider, plugin, started) {
4794
4938
  }
4795
4939
  if (plugin.dashboardAuth.mode === "manual-code") {
4796
4940
  if (!plugin.exchangeCode) {
4797
- console.log(import_picocolors11.default.red(`Provider "${provider}" does not support CLI code exchange. Use agent-qa dashboard.`));
4941
+ console.log(import_picocolors12.default.red(`Provider "${provider}" does not support CLI code exchange. Use agent-qa dashboard.`));
4798
4942
  process.exitCode = 1;
4799
4943
  return null;
4800
4944
  }
4801
4945
  const code = (await (0, import_input2.default)({ message: "Authorization code:" })).trim();
4802
4946
  if (!code) {
4803
- console.log(import_picocolors11.default.red("Authorization code is required"));
4947
+ console.log(import_picocolors12.default.red("Authorization code is required"));
4804
4948
  process.exitCode = 1;
4805
4949
  return null;
4806
4950
  }
4807
4951
  return await plugin.exchangeCode({ code, sessionState: started.sessionState });
4808
4952
  }
4809
- console.log(import_picocolors11.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
4953
+ console.log(import_picocolors12.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
4810
4954
  process.exitCode = 1;
4811
4955
  return null;
4812
4956
  }
@@ -4819,14 +4963,14 @@ async function runSubscriptionPluginLogin(configName, provider) {
4819
4963
  return;
4820
4964
  }
4821
4965
  if (!plugin.startAuth) {
4822
- console.log(import_picocolors11.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
4966
+ console.log(import_picocolors12.default.red(`Provider "${provider}" does not support CLI auth login. Use agent-qa dashboard.`));
4823
4967
  process.exitCode = 1;
4824
4968
  return;
4825
4969
  }
4826
4970
  let started;
4827
4971
  try {
4828
4972
  started = await plugin.startAuth({ configName });
4829
- console.log(`Open this URL to authenticate: ${import_picocolors11.default.cyan(started.authorizeUrl)}`);
4973
+ console.log(`Open this URL to authenticate: ${import_picocolors12.default.cyan(started.authorizeUrl)}`);
4830
4974
  openAuthorizationUrl(started.authorizeUrl);
4831
4975
  const tokens = await readTokensFromPlugin(provider, plugin, started);
4832
4976
  if (!tokens) return;
@@ -4835,18 +4979,18 @@ async function runSubscriptionPluginLogin(configName, provider) {
4835
4979
  provider: plugin.credentialProviderId,
4836
4980
  tokens
4837
4981
  });
4838
- console.log(import_picocolors11.default.green(`Authenticated ${configName} with ${plugin.label}`));
4982
+ console.log(import_picocolors12.default.green(`Authenticated ${configName} with ${plugin.label}`));
4839
4983
  } catch (err) {
4840
- console.log(import_picocolors11.default.red(err instanceof Error ? err.message : String(err)));
4984
+ console.log(import_picocolors12.default.red(err instanceof Error ? err.message : String(err)));
4841
4985
  process.exitCode = 1;
4842
4986
  } finally {
4843
4987
  started?.cleanup?.();
4844
4988
  }
4845
4989
  }
4846
4990
  function createAuthCommand() {
4847
- const cmd = new import_commander11.Command("auth").description("Manage LLM config credentials");
4991
+ const cmd = new import_commander12.Command("auth").description("Manage LLM config credentials");
4848
4992
  cmd.addCommand(
4849
- new import_commander11.Command("login").description("Authenticate with a subscription provider").requiredOption("--config <name>", "named subscription LLM config to authenticate").action(async (opts, command) => {
4993
+ new import_commander12.Command("login").description("Authenticate with a subscription provider").requiredOption("--config <name>", "named subscription LLM config to authenticate").action(async (opts, command) => {
4850
4994
  const { resolveNamedConfig: resolveNamedConfig2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
4851
4995
  const configPath = getGlobalConfigPath2(command);
4852
4996
  let provider;
@@ -4854,13 +4998,13 @@ function createAuthCommand() {
4854
4998
  try {
4855
4999
  const requestedName = normalizeConfigName(opts.config);
4856
5000
  if (!requestedName) {
4857
- console.log(import_picocolors11.default.red("configName is required"));
5001
+ console.log(import_picocolors12.default.red("configName is required"));
4858
5002
  return;
4859
5003
  }
4860
5004
  const resolved = await resolveNamedConfig2(requestedName, configPath);
4861
5005
  const resolvedName = normalizeConfigName(resolved.config.name);
4862
5006
  if (!resolvedName) {
4863
- console.log(import_picocolors11.default.red("Config name is invalid"));
5007
+ console.log(import_picocolors12.default.red("Config name is invalid"));
4864
5008
  return;
4865
5009
  }
4866
5010
  configName = resolvedName;
@@ -4871,17 +5015,17 @@ function createAuthCommand() {
4871
5015
  }
4872
5016
  provider = configProvider;
4873
5017
  } catch (err) {
4874
- console.log(import_picocolors11.default.red(err instanceof Error ? err.message : String(err)));
5018
+ console.log(import_picocolors12.default.red(err instanceof Error ? err.message : String(err)));
4875
5019
  return;
4876
5020
  }
4877
5021
  await runSubscriptionPluginLogin(configName, provider);
4878
5022
  })
4879
5023
  );
4880
5024
  cmd.addCommand(
4881
- new import_commander11.Command("set").description("Save a credential for a named LLM config").requiredOption("--config <name>", "named LLM config to save a credential for").requiredOption("--type <type>", "credential type: api-key or bearer-token").argument("[secret]", "credential secret; prompted when omitted").action(async (secretArg, opts, command) => {
5025
+ new import_commander12.Command("set").description("Save a credential for a named LLM config").requiredOption("--config <name>", "named LLM config to save a credential for").requiredOption("--type <type>", "credential type: api-key or bearer-token").argument("[secret]", "credential secret; prompted when omitted").action(async (secretArg, opts, command) => {
4882
5026
  const credentialType = opts.type.trim();
4883
5027
  if (credentialType !== "api-key" && credentialType !== "bearer-token") {
4884
- console.log(import_picocolors11.default.red("type must be api-key or bearer-token"));
5028
+ console.log(import_picocolors12.default.red("type must be api-key or bearer-token"));
4885
5029
  return;
4886
5030
  }
4887
5031
  const { resolveNamedConfig: resolveNamedConfig2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
@@ -4890,44 +5034,44 @@ function createAuthCommand() {
4890
5034
  try {
4891
5035
  resolved = await resolveNamedConfig2(opts.config, configPath);
4892
5036
  } catch (err) {
4893
- console.log(import_picocolors11.default.red(err instanceof Error ? err.message : String(err)));
5037
+ console.log(import_picocolors12.default.red(err instanceof Error ? err.message : String(err)));
4894
5038
  return;
4895
5039
  }
4896
5040
  const configName = resolved.config.name ?? opts.config;
4897
5041
  const provider = resolved.config.provider;
4898
5042
  if (SUBSCRIPTION_PROVIDERS2.has(provider)) {
4899
- console.log(import_picocolors11.default.red("Subscription providers use OAuth login"));
5043
+ console.log(import_picocolors12.default.red("Subscription providers use OAuth login"));
4900
5044
  return;
4901
5045
  }
4902
5046
  if (credentialType === "bearer-token" && provider !== "anthropic-compatible") {
4903
- console.log(import_picocolors11.default.red("bearer-token credentials are only supported for anthropic-compatible configs"));
5047
+ console.log(import_picocolors12.default.red("bearer-token credentials are only supported for anthropic-compatible configs"));
4904
5048
  return;
4905
5049
  }
4906
5050
  if (credentialType === "api-key" && !API_KEY_CREDENTIAL_PROVIDERS.has(provider)) {
4907
- console.log(import_picocolors11.default.red("api-key credentials are not supported for this provider"));
5051
+ console.log(import_picocolors12.default.red("api-key credentials are not supported for this provider"));
4908
5052
  return;
4909
5053
  }
4910
5054
  const secret = (secretArg ?? await (0, import_password.default)({ message: "Secret:" })).trim();
4911
5055
  if (!secret) {
4912
- console.log(import_picocolors11.default.red("Secret is required"));
5056
+ console.log(import_picocolors12.default.red("Secret is required"));
4913
5057
  return;
4914
5058
  }
4915
5059
  const { writeAuth } = await import("@vostride/agent-qa-core");
4916
5060
  if (credentialType === "bearer-token") {
4917
5061
  await writeAuth(configName, { type: "bearer", provider: "anthropic-compatible", token: secret });
4918
- console.log(import_picocolors11.default.green(`Saved bearer token for ${configName}`));
5062
+ console.log(import_picocolors12.default.green(`Saved bearer token for ${configName}`));
4919
5063
  } else {
4920
5064
  await writeAuth(configName, { type: "api", provider, key: secret });
4921
- console.log(import_picocolors11.default.green(`Saved API key for ${configName}`));
5065
+ console.log(import_picocolors12.default.green(`Saved API key for ${configName}`));
4922
5066
  }
4923
5067
  })
4924
5068
  );
4925
5069
  cmd.addCommand(
4926
- new import_commander11.Command("status").description("Show credential status for configured LLMs").action(async function() {
5070
+ new import_commander12.Command("status").description("Show credential status for configured LLMs").action(async function() {
4927
5071
  const { loadAuthPluginsForRawConfig: loadAuthPluginsForRawConfig2, resolveModelAuth: resolveModelAuth2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
4928
5072
  const { loadConfigFile: loadConfigFile2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4929
5073
  const configPath = getGlobalConfigPath2(this) ?? "agent-qa.config.yaml";
4930
- console.log(import_picocolors11.default.bold("Auth status:\n"));
5074
+ console.log(import_picocolors12.default.bold("Auth status:\n"));
4931
5075
  let llms;
4932
5076
  let defaultLLM;
4933
5077
  try {
@@ -4938,25 +5082,25 @@ function createAuthCommand() {
4938
5082
  } catch {
4939
5083
  }
4940
5084
  if (!llms?.length) {
4941
- console.log(import_picocolors11.default.dim(" No LLM configs found"));
5085
+ console.log(import_picocolors12.default.dim(" No LLM configs found"));
4942
5086
  return;
4943
5087
  }
4944
5088
  const maxNameLen = Math.max(...llms.map((c) => String(c.name ?? "").length), 4) + 12;
4945
5089
  for (const llm of llms) {
4946
5090
  const name = String(llm.name ?? "");
4947
5091
  const isDefault = name === defaultLLM;
4948
- const label = isDefault ? `${name} ${import_picocolors11.default.dim("(default)")}` : name;
5092
+ const label = isDefault ? `${name} ${import_picocolors12.default.dim("(default)")}` : name;
4949
5093
  const rawLabel = isDefault ? `${name} (default)` : name;
4950
5094
  const padLen = Math.max(0, maxNameLen - rawLabel.length);
4951
5095
  const dots = ".".repeat(padLen + 2);
4952
5096
  const provider = llm.provider;
4953
5097
  const status = statusForResolvedAuth(await resolveModelAuth2(name, llm));
4954
- console.log(` ${label} ${import_picocolors11.default.dim(`[${providerLabel(provider)}]`)} ${import_picocolors11.default.dim(dots)} ${status}`);
5098
+ console.log(` ${label} ${import_picocolors12.default.dim(`[${providerLabel(provider)}]`)} ${import_picocolors12.default.dim(dots)} ${status}`);
4955
5099
  }
4956
5100
  })
4957
5101
  );
4958
5102
  cmd.addCommand(
4959
- new import_commander11.Command("logout").description("Remove stored credentials for a named config").option("--config <name>", "named LLM config to log out from").action(async (opts, command) => {
5103
+ new import_commander12.Command("logout").description("Remove stored credentials for a named config").option("--config <name>", "named LLM config to log out from").action(async (opts, command) => {
4960
5104
  const { removeAuth } = await import("@vostride/agent-qa-core");
4961
5105
  const { resolveNamedConfig: resolveNamedConfig2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
4962
5106
  const configPath = getGlobalConfigPath2(command);
@@ -4968,16 +5112,16 @@ function createAuthCommand() {
4968
5112
  const resolved = await resolveNamedConfig2(void 0, configPath);
4969
5113
  storeKey = resolved.config.name;
4970
5114
  } catch {
4971
- console.log(import_picocolors11.default.red("Specify --config <name> or run with a config file"));
5115
+ console.log(import_picocolors12.default.red("Specify --config <name> or run with a config file"));
4972
5116
  return;
4973
5117
  }
4974
5118
  }
4975
5119
  await removeAuth(storeKey);
4976
- console.log(import_picocolors11.default.green(`Logged out from ${storeKey}`));
5120
+ console.log(import_picocolors12.default.green(`Logged out from ${storeKey}`));
4977
5121
  })
4978
5122
  );
4979
5123
  cmd.addCommand(
4980
- new import_commander11.Command("test").description("Test LLM connection using named config credentials").option("--config <name>", "named LLM config to test").option("--provider <name>", "override config provider").option("--model <name>", "override config model").action(async (opts, command) => {
5124
+ new import_commander12.Command("test").description("Test LLM connection using named config credentials").option("--config <name>", "named LLM config to test").option("--provider <name>", "override config provider").option("--model <name>", "override config model").action(async (opts, command) => {
4981
5125
  const { resolveModelAuth: resolveModelAuth2, resolveNamedConfig: resolveNamedConfig2 } = await Promise.resolve().then(() => (init_llm_utils(), llm_utils_exports));
4982
5126
  const configPath = getGlobalConfigPath2(command);
4983
5127
  let planner;
@@ -4988,19 +5132,19 @@ function createAuthCommand() {
4988
5132
  planner = modelFields;
4989
5133
  configName = name;
4990
5134
  } catch (err) {
4991
- console.log(import_picocolors11.default.red(err instanceof Error ? err.message : String(err)));
5135
+ console.log(import_picocolors12.default.red(err instanceof Error ? err.message : String(err)));
4992
5136
  return;
4993
5137
  }
4994
5138
  if (opts.provider) planner = { ...planner, provider: opts.provider };
4995
5139
  if (opts.model) planner = { ...planner, model: opts.model };
4996
- console.log(import_picocolors11.default.dim(`Testing config: ${configName}...`));
5140
+ console.log(import_picocolors12.default.dim(`Testing config: ${configName}...`));
4997
5141
  const auth = await resolveModelAuth2(configName, planner);
4998
5142
  if (auth.kind === "missing") {
4999
- console.log(import_picocolors11.default.red(auth.message));
5143
+ console.log(import_picocolors12.default.red(auth.message));
5000
5144
  return;
5001
5145
  }
5002
5146
  if (auth.kind === "unauthenticated") {
5003
- console.log(import_picocolors11.default.dim(auth.message));
5147
+ console.log(import_picocolors12.default.dim(auth.message));
5004
5148
  }
5005
5149
  const { createModel, getProviderOptions } = await import("@vostride/agent-qa-core");
5006
5150
  const { generateText } = await import("ai");
@@ -5018,24 +5162,24 @@ function createAuthCommand() {
5018
5162
  ...providerOpts ? { providerOptions: providerOpts } : {}
5019
5163
  });
5020
5164
  const elapsed = Date.now() - start;
5021
- console.log(import_picocolors11.default.green(`Connected to ${planner.provider}/${planner.model} (${elapsed}ms)`));
5165
+ console.log(import_picocolors12.default.green(`Connected to ${planner.provider}/${planner.model} (${elapsed}ms)`));
5022
5166
  } catch (err) {
5023
5167
  const { message, statusCode } = extractProviderError(err);
5024
5168
  const fullContext = `${message} ${statusCode ?? ""}`;
5025
5169
  if (/auth|unauthorized|401|invalid.*key|permission/i.test(fullContext)) {
5026
- console.log(import_picocolors11.default.red("Authentication failed. Check the saved credential for this config."));
5170
+ console.log(import_picocolors12.default.red("Authentication failed. Check the saved credential for this config."));
5027
5171
  } else if (/model.*not.*support|not found|404|does not exist/i.test(fullContext)) {
5028
- console.log(import_picocolors11.default.red("Model not found. Check the model name."));
5172
+ console.log(import_picocolors12.default.red("Model not found. Check the model name."));
5029
5173
  } else if (/rate|429|quota|limit/i.test(fullContext)) {
5030
- console.log(import_picocolors11.default.red("Rate limited. Try again later."));
5174
+ console.log(import_picocolors12.default.red("Rate limited. Try again later."));
5031
5175
  } else if (/ECONNREFUSED|ENOTFOUND|timeout|abort|fetch failed|network/i.test(fullContext)) {
5032
- console.log(import_picocolors11.default.red("Network error. Check the exact base URL and try again."));
5176
+ console.log(import_picocolors12.default.red("Network error. Check the exact base URL and try again."));
5033
5177
  } else if (/invalid_request|400|bad request/i.test(fullContext)) {
5034
- console.log(import_picocolors11.default.red("Invalid request. Check model and provider settings."));
5178
+ console.log(import_picocolors12.default.red("Invalid request. Check model and provider settings."));
5035
5179
  } else {
5036
- console.log(import_picocolors11.default.red(`Connection failed. ${message}`));
5180
+ console.log(import_picocolors12.default.red(`Connection failed. ${message}`));
5037
5181
  }
5038
- console.log(import_picocolors11.default.dim(message));
5182
+ console.log(import_picocolors12.default.dim(message));
5039
5183
  } finally {
5040
5184
  clearTimeout(timeout);
5041
5185
  }
@@ -5045,36 +5189,36 @@ function createAuthCommand() {
5045
5189
  }
5046
5190
 
5047
5191
  // src/commands/devices.ts
5048
- var import_commander12 = require("commander");
5192
+ var import_commander13 = require("commander");
5049
5193
  var import_node_child_process5 = require("child_process");
5050
5194
  var import_node_fs4 = require("fs");
5051
- var import_node_path9 = require("path");
5195
+ var import_node_path11 = require("path");
5052
5196
  var import_yaml6 = require("yaml");
5053
- var import_picocolors12 = __toESM(require("picocolors"), 1);
5197
+ var import_picocolors13 = __toESM(require("picocolors"), 1);
5054
5198
  init_config();
5055
5199
  function formatStatus2(transport, localBinding) {
5056
5200
  if (transport !== "local") {
5057
- return import_picocolors12.default.green("Ready") + import_picocolors12.default.dim(" (no local binding required)");
5201
+ return import_picocolors13.default.green("Ready") + import_picocolors13.default.dim(" (no local binding required)");
5058
5202
  }
5059
5203
  if (!localBinding) {
5060
- return import_picocolors12.default.red("UNBOUND") + import_picocolors12.default.yellow(" - run `agent-qa devices init`");
5204
+ return import_picocolors13.default.red("UNBOUND") + import_picocolors13.default.yellow(" - run `agent-qa devices init`");
5061
5205
  }
5062
5206
  const keys = Object.entries(localBinding).slice(0, 2);
5063
5207
  const details = keys.map(([k, v]) => `${k}: ${v}`).join(", ");
5064
- return import_picocolors12.default.green("Bound") + import_picocolors12.default.dim(` (${details})`);
5208
+ return import_picocolors13.default.green("Bound") + import_picocolors13.default.dim(` (${details})`);
5065
5209
  }
5066
5210
  async function listDevices() {
5067
5211
  let config;
5068
5212
  try {
5069
5213
  config = await resolveConfig({});
5070
5214
  } catch (err) {
5071
- console.error(import_picocolors12.default.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
5215
+ console.error(import_picocolors13.default.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
5072
5216
  process.exitCode = 1;
5073
5217
  return;
5074
5218
  }
5075
5219
  const devices = config.registry?.devices;
5076
5220
  if (!devices || Object.keys(devices).length === 0) {
5077
- console.log(import_picocolors12.default.yellow("No devices configured in registry.devices"));
5221
+ console.log(import_picocolors13.default.yellow("No devices configured in registry.devices"));
5078
5222
  return;
5079
5223
  }
5080
5224
  const localBindings = loadLocalBindings();
@@ -5082,13 +5226,13 @@ async function listDevices() {
5082
5226
  const platWidth = 10;
5083
5227
  const transWidth = 14;
5084
5228
  console.log("");
5085
- console.log(import_picocolors12.default.bold(" Configured Devices"));
5229
+ console.log(import_picocolors13.default.bold(" Configured Devices"));
5086
5230
  console.log("");
5087
5231
  console.log(
5088
- ` ${import_picocolors12.default.dim("Name".padEnd(nameWidth))} ${import_picocolors12.default.dim("Platform".padEnd(platWidth))} ${import_picocolors12.default.dim("Transport".padEnd(transWidth))} ${import_picocolors12.default.dim("Status")}`
5232
+ ` ${import_picocolors13.default.dim("Name".padEnd(nameWidth))} ${import_picocolors13.default.dim("Platform".padEnd(platWidth))} ${import_picocolors13.default.dim("Transport".padEnd(transWidth))} ${import_picocolors13.default.dim("Status")}`
5089
5233
  );
5090
5234
  console.log(
5091
- ` ${import_picocolors12.default.dim("\u2500".repeat(nameWidth))} ${import_picocolors12.default.dim("\u2500".repeat(platWidth))} ${import_picocolors12.default.dim("\u2500".repeat(transWidth))} ${import_picocolors12.default.dim("\u2500".repeat(30))}`
5235
+ ` ${import_picocolors13.default.dim("\u2500".repeat(nameWidth))} ${import_picocolors13.default.dim("\u2500".repeat(platWidth))} ${import_picocolors13.default.dim("\u2500".repeat(transWidth))} ${import_picocolors13.default.dim("\u2500".repeat(30))}`
5092
5236
  );
5093
5237
  for (const [name, profile] of Object.entries(devices)) {
5094
5238
  const localBinding = localBindings?.devices?.[name] ?? null;
@@ -5139,7 +5283,7 @@ function scanSimctlDevices() {
5139
5283
  }
5140
5284
  }
5141
5285
  function appendGitignoreEntry(dir, entry) {
5142
- const gitignorePath = (0, import_node_path9.join)(dir, ".gitignore");
5286
+ const gitignorePath = (0, import_node_path11.join)(dir, ".gitignore");
5143
5287
  let existing = "";
5144
5288
  if ((0, import_node_fs4.existsSync)(gitignorePath)) {
5145
5289
  existing = (0, import_node_fs4.readFileSync)(gitignorePath, "utf-8");
@@ -5150,30 +5294,30 @@ function appendGitignoreEntry(dir, entry) {
5150
5294
  }
5151
5295
  async function initDevices() {
5152
5296
  const dir = process.cwd();
5153
- const localFilePath = (0, import_node_path9.join)(dir, "agent-qa.local.yaml");
5297
+ const localFilePath = (0, import_node_path11.join)(dir, "agent-qa.local.yaml");
5154
5298
  if ((0, import_node_fs4.existsSync)(localFilePath)) {
5155
- console.log(import_picocolors12.default.yellow("agent-qa.local.yaml already exists. Skipping generation."));
5299
+ console.log(import_picocolors13.default.yellow("agent-qa.local.yaml already exists. Skipping generation."));
5156
5300
  return;
5157
5301
  }
5158
- console.log(import_picocolors12.default.bold("\n Scanning for connected devices...\n"));
5302
+ console.log(import_picocolors13.default.bold("\n Scanning for connected devices...\n"));
5159
5303
  const adbDevices = scanAdbDevices();
5160
5304
  const simctlDevices = scanSimctlDevices();
5161
5305
  if (adbDevices.length > 0) {
5162
- console.log(` ${import_picocolors12.default.green("Android:")} found ${adbDevices.length} device(s)`);
5306
+ console.log(` ${import_picocolors13.default.green("Android:")} found ${adbDevices.length} device(s)`);
5163
5307
  for (const d of adbDevices) {
5164
- console.log(` ${import_picocolors12.default.dim("\u2022")} ${d.model} (${d.serial})`);
5308
+ console.log(` ${import_picocolors13.default.dim("\u2022")} ${d.model} (${d.serial})`);
5165
5309
  }
5166
5310
  } else {
5167
- console.log(` ${import_picocolors12.default.dim("Android:")} no devices found (adb not available or no devices connected)`);
5311
+ console.log(` ${import_picocolors13.default.dim("Android:")} no devices found (adb not available or no devices connected)`);
5168
5312
  }
5169
5313
  if (simctlDevices.length > 0) {
5170
5314
  const booted = simctlDevices.filter((d) => d.state === "Booted");
5171
- console.log(` ${import_picocolors12.default.green("iOS:")} found ${simctlDevices.length} simulator(s) (${booted.length} booted)`);
5315
+ console.log(` ${import_picocolors13.default.green("iOS:")} found ${simctlDevices.length} simulator(s) (${booted.length} booted)`);
5172
5316
  for (const d of booted) {
5173
- console.log(` ${import_picocolors12.default.dim("\u2022")} ${d.name} (${d.udid})`);
5317
+ console.log(` ${import_picocolors13.default.dim("\u2022")} ${d.name} (${d.udid})`);
5174
5318
  }
5175
5319
  } else {
5176
- console.log(` ${import_picocolors12.default.dim("iOS:")} no simulators found (simctl not available or none installed)`);
5320
+ console.log(` ${import_picocolors13.default.dim("iOS:")} no simulators found (simctl not available or none installed)`);
5177
5321
  }
5178
5322
  let config;
5179
5323
  try {
@@ -5207,20 +5351,20 @@ async function initDevices() {
5207
5351
  (0, import_node_fs4.writeFileSync)(localFilePath, yamlContent);
5208
5352
  appendGitignoreEntry(dir, "agent-qa.local.yaml");
5209
5353
  console.log("");
5210
- console.log(import_picocolors12.default.green(` Generated agent-qa.local.yaml with ${bindCount} device binding(s)`));
5211
- console.log(import_picocolors12.default.dim(" Added agent-qa.local.yaml to .gitignore"));
5354
+ console.log(import_picocolors13.default.green(` Generated agent-qa.local.yaml with ${bindCount} device binding(s)`));
5355
+ console.log(import_picocolors13.default.dim(" Added agent-qa.local.yaml to .gitignore"));
5212
5356
  console.log("");
5213
5357
  }
5214
5358
  function createDevicesCommand() {
5215
- const cmd = new import_commander12.Command("devices").description("Manage device profiles and local bindings");
5359
+ const cmd = new import_commander13.Command("devices").description("Manage device profiles and local bindings");
5216
5360
  cmd.command("list").description("Show configured devices with merged shared + local view").action(listDevices);
5217
5361
  cmd.command("init").description("Scan connected devices and generate agent-qa.local.yaml").action(initDevices);
5218
5362
  return cmd;
5219
5363
  }
5220
5364
 
5221
5365
  // src/commands/ids.ts
5222
- var import_commander13 = require("commander");
5223
- var import_picocolors13 = __toESM(require("picocolors"), 1);
5366
+ var import_commander14 = require("commander");
5367
+ var import_picocolors14 = __toESM(require("picocolors"), 1);
5224
5368
  var import_agent_qa_ids = require("@vostride/agent-qa-ids");
5225
5369
  function parseEntityType(value) {
5226
5370
  if (import_agent_qa_ids.ENTITY_ID_TYPES.includes(value)) {
@@ -5229,7 +5373,7 @@ function parseEntityType(value) {
5229
5373
  throw new Error(`Unknown ID type "${value}". Expected one of: ${import_agent_qa_ids.ENTITY_ID_TYPES.join(", ")}`);
5230
5374
  }
5231
5375
  function createIdsCommand() {
5232
- const cmd = new import_commander13.Command("ids").description("Generate and validate canonical agent-qa entity IDs");
5376
+ const cmd = new import_commander14.Command("ids").description("Generate and validate canonical agent-qa entity IDs");
5233
5377
  cmd.command("generate").description("Generate a canonical agent-qa ID using id-agent").argument("<type>", `entity type: ${import_agent_qa_ids.ENTITY_ID_TYPES.join("|")}`).option("--json", "print structured JSON").action((typeValue, opts) => {
5234
5378
  try {
5235
5379
  const type = parseEntityType(typeValue);
@@ -5241,7 +5385,7 @@ function createIdsCommand() {
5241
5385
  console.log(id);
5242
5386
  }
5243
5387
  } catch (err) {
5244
- console.error(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
5388
+ console.error(import_picocolors14.default.red(err instanceof Error ? err.message : String(err)));
5245
5389
  process.exitCode = 1;
5246
5390
  }
5247
5391
  });
@@ -5260,15 +5404,15 @@ function createIdsCommand() {
5260
5404
  if (opts.json) {
5261
5405
  console.log(JSON.stringify(result, null, 2));
5262
5406
  } else if (valid) {
5263
- console.log(import_picocolors13.default.green(result.message));
5407
+ console.log(import_picocolors14.default.green(result.message));
5264
5408
  } else {
5265
- console.error(import_picocolors13.default.red(result.message));
5409
+ console.error(import_picocolors14.default.red(result.message));
5266
5410
  }
5267
5411
  if (!valid) {
5268
5412
  process.exitCode = 1;
5269
5413
  }
5270
5414
  } catch (err) {
5271
- console.error(import_picocolors13.default.red(err instanceof Error ? err.message : String(err)));
5415
+ console.error(import_picocolors14.default.red(err instanceof Error ? err.message : String(err)));
5272
5416
  process.exitCode = 1;
5273
5417
  }
5274
5418
  });
@@ -5276,18 +5420,18 @@ function createIdsCommand() {
5276
5420
  }
5277
5421
 
5278
5422
  // src/commands/create-test.ts
5279
- var import_commander14 = require("commander");
5423
+ var import_commander15 = require("commander");
5280
5424
  var import_agent_qa_ids2 = require("@vostride/agent-qa-ids");
5281
5425
  var import_yaml7 = require("yaml");
5282
5426
  var import_promises4 = require("fs/promises");
5283
5427
  var import_node_fs5 = require("fs");
5284
- var import_node_path10 = require("path");
5285
- var import_picocolors14 = __toESM(require("picocolors"), 1);
5428
+ var import_node_path12 = require("path");
5429
+ var import_picocolors15 = __toESM(require("picocolors"), 1);
5286
5430
  function createCreateTestCommand() {
5287
- const cmd = new import_commander14.Command("create-test").description("Scaffold a new test YAML file with auto-generated ID").argument("<path>", "output file path for the test YAML").action(async (path) => {
5288
- const resolved = (0, import_node_path10.resolve)(path);
5431
+ const cmd = new import_commander15.Command("create-test").description("Scaffold a new test YAML file with auto-generated ID").argument("<path>", "output file path for the test YAML").action(async (path) => {
5432
+ const resolved = (0, import_node_path12.resolve)(path);
5289
5433
  if ((0, import_node_fs5.existsSync)(resolved)) {
5290
- console.error(import_picocolors14.default.red("Error: File already exists: " + resolved));
5434
+ console.error(import_picocolors15.default.red("Error: File already exists: " + resolved));
5291
5435
  process.exitCode = 1;
5292
5436
  return;
5293
5437
  }
@@ -5299,27 +5443,27 @@ function createCreateTestCommand() {
5299
5443
  steps: ["Describe your first test step here"]
5300
5444
  };
5301
5445
  const yamlContent = (0, import_yaml7.stringify)(template);
5302
- await (0, import_promises4.mkdir)((0, import_node_path10.dirname)(resolved), { recursive: true });
5446
+ await (0, import_promises4.mkdir)((0, import_node_path12.dirname)(resolved), { recursive: true });
5303
5447
  await (0, import_promises4.writeFile)(resolved, yamlContent, "utf-8");
5304
- console.log(import_picocolors14.default.green("Created " + resolved));
5305
- console.log(import_picocolors14.default.dim(" test-id: " + testId));
5448
+ console.log(import_picocolors15.default.green("Created " + resolved));
5449
+ console.log(import_picocolors15.default.dim(" test-id: " + testId));
5306
5450
  });
5307
5451
  return cmd;
5308
5452
  }
5309
5453
 
5310
5454
  // src/commands/create-suite.ts
5311
- var import_commander15 = require("commander");
5455
+ var import_commander16 = require("commander");
5312
5456
  var import_agent_qa_ids3 = require("@vostride/agent-qa-ids");
5313
5457
  var import_yaml8 = require("yaml");
5314
5458
  var import_promises5 = require("fs/promises");
5315
5459
  var import_node_fs6 = require("fs");
5316
- var import_node_path11 = require("path");
5317
- var import_picocolors15 = __toESM(require("picocolors"), 1);
5460
+ var import_node_path13 = require("path");
5461
+ var import_picocolors16 = __toESM(require("picocolors"), 1);
5318
5462
  function createCreateSuiteCommand() {
5319
- const cmd = new import_commander15.Command("create-suite").description("Scaffold a new suite YAML file with auto-generated ID").argument("<path>", "output file path for the suite YAML").action(async (path) => {
5320
- const resolved = (0, import_node_path11.resolve)(path);
5463
+ const cmd = new import_commander16.Command("create-suite").description("Scaffold a new suite YAML file with auto-generated ID").argument("<path>", "output file path for the suite YAML").action(async (path) => {
5464
+ const resolved = (0, import_node_path13.resolve)(path);
5321
5465
  if ((0, import_node_fs6.existsSync)(resolved)) {
5322
- console.error(import_picocolors15.default.red("Error: File already exists: " + resolved));
5466
+ console.error(import_picocolors16.default.red("Error: File already exists: " + resolved));
5323
5467
  process.exitCode = 1;
5324
5468
  return;
5325
5469
  }
@@ -5333,22 +5477,22 @@ function createCreateSuiteCommand() {
5333
5477
  "tests: []",
5334
5478
  "tests: []\n # - test: ./path/to/test.yaml\n # id: my-test-id"
5335
5479
  );
5336
- await (0, import_promises5.mkdir)((0, import_node_path11.dirname)(resolved), { recursive: true });
5480
+ await (0, import_promises5.mkdir)((0, import_node_path13.dirname)(resolved), { recursive: true });
5337
5481
  await (0, import_promises5.writeFile)(resolved, yamlContent, "utf-8");
5338
- console.log(import_picocolors15.default.green("Created " + resolved));
5339
- console.log(import_picocolors15.default.dim(" suite-id: " + suiteId));
5482
+ console.log(import_picocolors16.default.green("Created " + resolved));
5483
+ console.log(import_picocolors16.default.dim(" suite-id: " + suiteId));
5340
5484
  });
5341
5485
  return cmd;
5342
5486
  }
5343
5487
 
5344
5488
  // src/commands/clean-memory.ts
5345
- var import_commander16 = require("commander");
5346
- var import_picocolors16 = __toESM(require("picocolors"), 1);
5489
+ var import_commander17 = require("commander");
5490
+ var import_picocolors17 = __toESM(require("picocolors"), 1);
5347
5491
  var import_node_readline3 = require("readline");
5348
5492
  var import_promises6 = require("fs/promises");
5349
5493
  var import_node_fs7 = require("fs");
5350
- var import_node_path12 = require("path");
5351
- var import_agent_qa_core12 = require("@vostride/agent-qa-core");
5494
+ var import_node_path14 = require("path");
5495
+ var import_agent_qa_core13 = require("@vostride/agent-qa-core");
5352
5496
  init_config();
5353
5497
  function formatSize(bytes) {
5354
5498
  if (bytes === 0) return "0 B";
@@ -5356,13 +5500,13 @@ function formatSize(bytes) {
5356
5500
  return `${(bytes / 1048576).toFixed(1)} MB`;
5357
5501
  }
5358
5502
  async function getOrphanInfo(memoryRoot, tier, scope) {
5359
- const scopePath = (0, import_node_path12.join)(memoryRoot, tier, scope);
5360
- const obsFiles = await (0, import_agent_qa_core12.listObservations)(scopePath);
5503
+ const scopePath = (0, import_node_path14.join)(memoryRoot, tier, scope);
5504
+ const obsFiles = await (0, import_agent_qa_core13.listObservations)(scopePath);
5361
5505
  let newest = null;
5362
5506
  for (const filename of obsFiles) {
5363
5507
  try {
5364
- const content = await (0, import_promises6.readFile)((0, import_node_path12.join)(scopePath, filename), "utf-8");
5365
- const { data } = (0, import_agent_qa_core12.parseObservation)(content, filename);
5508
+ const content = await (0, import_promises6.readFile)((0, import_node_path14.join)(scopePath, filename), "utf-8");
5509
+ const { data } = (0, import_agent_qa_core13.parseObservation)(content, filename);
5366
5510
  if (data?.last_confirmed) {
5367
5511
  if (!newest || data.last_confirmed > newest) {
5368
5512
  newest = data.last_confirmed;
@@ -5376,7 +5520,7 @@ async function getOrphanInfo(memoryRoot, tier, scope) {
5376
5520
  const entries = await (0, import_promises6.readdir)(scopePath);
5377
5521
  for (const entry of entries) {
5378
5522
  try {
5379
- const s = await (0, import_promises6.stat)((0, import_node_path12.join)(scopePath, entry));
5523
+ const s = await (0, import_promises6.stat)((0, import_node_path14.join)(scopePath, entry));
5380
5524
  if (s.isFile()) totalSize += s.size;
5381
5525
  } catch {
5382
5526
  }
@@ -5384,7 +5528,7 @@ async function getOrphanInfo(memoryRoot, tier, scope) {
5384
5528
  } catch {
5385
5529
  }
5386
5530
  return {
5387
- path: (0, import_node_path12.join)(tier, scope),
5531
+ path: (0, import_node_path14.join)(tier, scope),
5388
5532
  tier,
5389
5533
  scope,
5390
5534
  fileCount: obsFiles.length,
@@ -5393,21 +5537,21 @@ async function getOrphanInfo(memoryRoot, tier, scope) {
5393
5537
  };
5394
5538
  }
5395
5539
  function createCleanMemoryCommand() {
5396
- const cmd = new import_commander16.Command("clean-memory").description("Remove orphaned memory observation directories").option("-y, --yes", "skip confirmation prompt").action(async (_opts, command) => {
5540
+ const cmd = new import_commander17.Command("clean-memory").description("Remove orphaned memory observation directories").option("-y, --yes", "skip confirmation prompt").action(async (_opts, command) => {
5397
5541
  const opts = command.opts();
5398
5542
  const configPath = command.parent?.opts()?.config ?? "agent-qa.config.yaml";
5399
5543
  const config = await resolveConfig({ configPath });
5400
- const workspace = (0, import_agent_qa_core12.resolveWorkspacePaths)({ config, configPath });
5544
+ const workspace = (0, import_agent_qa_core13.resolveWorkspacePaths)({ config, configPath });
5401
5545
  const expectedProducts = /* @__PURE__ */ new Set();
5402
5546
  const targets = config.registry?.targets ?? {};
5403
5547
  for (const [name, entry] of Object.entries(targets)) {
5404
5548
  expectedProducts.add(entry?.product ?? name);
5405
5549
  }
5406
5550
  const expectedTests = /* @__PURE__ */ new Set();
5407
- for (const file of await (0, import_agent_qa_core12.discoverWorkspaceFiles)({ workspace, kind: "test" })) {
5551
+ for (const file of await (0, import_agent_qa_core13.discoverWorkspaceFiles)({ workspace, kind: "test" })) {
5408
5552
  try {
5409
5553
  const content = await (0, import_promises6.readFile)(file.absolutePath, "utf-8");
5410
- const result = (0, import_agent_qa_core12.parseTestFile)(content, file.workspaceRelativePath);
5554
+ const result = (0, import_agent_qa_core13.parseTestFile)(content, file.workspaceRelativePath);
5411
5555
  for (const t of result.tests) {
5412
5556
  if (t["test-id"]) expectedTests.add(t["test-id"]);
5413
5557
  expectedTests.add(t.name);
@@ -5416,17 +5560,17 @@ function createCleanMemoryCommand() {
5416
5560
  }
5417
5561
  }
5418
5562
  const expectedSuites = /* @__PURE__ */ new Set();
5419
- for (const file of await (0, import_agent_qa_core12.discoverWorkspaceFiles)({ workspace, kind: "suite" })) {
5563
+ for (const file of await (0, import_agent_qa_core13.discoverWorkspaceFiles)({ workspace, kind: "suite" })) {
5420
5564
  try {
5421
- const suite = await (0, import_agent_qa_core12.parseSuiteFile)(file.absolutePath);
5565
+ const suite = await (0, import_agent_qa_core13.parseSuiteFile)(file.absolutePath);
5422
5566
  if (suite["suite-id"]) expectedSuites.add(suite["suite-id"]);
5423
5567
  expectedSuites.add(suite.name);
5424
5568
  } catch {
5425
5569
  }
5426
5570
  }
5427
- const memoryRoot = (0, import_agent_qa_core12.resolveMemoryRoot)(config, workspace.configDir);
5571
+ const memoryRoot = (0, import_agent_qa_core13.resolveMemoryRoot)(config, workspace.configDir);
5428
5572
  if (!(0, import_node_fs7.existsSync)(memoryRoot)) {
5429
- console.log(import_picocolors16.default.dim("No memory directory found."));
5573
+ console.log(import_picocolors17.default.dim("No memory directory found."));
5430
5574
  return;
5431
5575
  }
5432
5576
  const TIERS = [
@@ -5436,7 +5580,7 @@ function createCleanMemoryCommand() {
5436
5580
  ];
5437
5581
  const orphans = [];
5438
5582
  for (const tier of TIERS) {
5439
- const tierPath = (0, import_node_path12.join)(memoryRoot, tier.dir);
5583
+ const tierPath = (0, import_node_path14.join)(memoryRoot, tier.dir);
5440
5584
  let entries;
5441
5585
  try {
5442
5586
  entries = await (0, import_promises6.readdir)(tierPath);
@@ -5445,7 +5589,7 @@ function createCleanMemoryCommand() {
5445
5589
  }
5446
5590
  for (const entry of entries) {
5447
5591
  try {
5448
- const s = await (0, import_promises6.stat)((0, import_node_path12.join)(tierPath, entry));
5592
+ const s = await (0, import_promises6.stat)((0, import_node_path14.join)(tierPath, entry));
5449
5593
  if (!s.isDirectory()) continue;
5450
5594
  } catch {
5451
5595
  continue;
@@ -5456,17 +5600,17 @@ function createCleanMemoryCommand() {
5456
5600
  }
5457
5601
  }
5458
5602
  if (orphans.length === 0) {
5459
- console.log(import_picocolors16.default.dim("No orphaned memory directories found."));
5603
+ console.log(import_picocolors17.default.dim("No orphaned memory directories found."));
5460
5604
  return;
5461
5605
  }
5462
5606
  const maxPathLen = Math.max("Path".length, ...orphans.map((o) => o.path.length));
5463
5607
  console.log("");
5464
- console.log(import_picocolors16.default.bold("Orphaned memory directories:"));
5608
+ console.log(import_picocolors17.default.bold("Orphaned memory directories:"));
5465
5609
  console.log("");
5466
- console.log(" " + import_picocolors16.default.dim("Path".padEnd(maxPathLen + 2) + "Files".padStart(7) + " " + "Last Confirmed".padEnd(20) + "Size".padStart(10)));
5467
- console.log(" " + import_picocolors16.default.dim("-".repeat(maxPathLen + 2 + 7 + 2 + 20 + 10)));
5610
+ console.log(" " + import_picocolors17.default.dim("Path".padEnd(maxPathLen + 2) + "Files".padStart(7) + " " + "Last Confirmed".padEnd(20) + "Size".padStart(10)));
5611
+ console.log(" " + import_picocolors17.default.dim("-".repeat(maxPathLen + 2 + 7 + 2 + 20 + 10)));
5468
5612
  for (const o of orphans) {
5469
- const confirmed = o.lastConfirmed ?? import_picocolors16.default.dim("never");
5613
+ const confirmed = o.lastConfirmed ?? import_picocolors17.default.dim("never");
5470
5614
  console.log(" " + o.path.padEnd(maxPathLen + 2) + String(o.fileCount).padStart(7) + " " + String(confirmed).padEnd(20) + formatSize(o.size).padStart(10));
5471
5615
  }
5472
5616
  console.log("");
@@ -5482,23 +5626,23 @@ function createCleanMemoryCommand() {
5482
5626
  }
5483
5627
  }
5484
5628
  for (const orphan of orphans) {
5485
- await (0, import_promises6.rm)((0, import_node_path12.join)(memoryRoot, orphan.tier, orphan.scope), { recursive: true, force: true });
5629
+ await (0, import_promises6.rm)((0, import_node_path14.join)(memoryRoot, orphan.tier, orphan.scope), { recursive: true, force: true });
5486
5630
  }
5487
- console.log(import_picocolors16.default.green(`Deleted ${orphans.length} orphaned director${orphans.length === 1 ? "y" : "ies"}.`));
5631
+ console.log(import_picocolors17.default.green(`Deleted ${orphans.length} orphaned director${orphans.length === 1 ? "y" : "ies"}.`));
5488
5632
  });
5489
5633
  return cmd;
5490
5634
  }
5491
5635
 
5492
5636
  // src/commands/skills.ts
5493
- var import_commander17 = require("commander");
5637
+ var import_commander18 = require("commander");
5494
5638
  var import_node_fs8 = require("fs");
5495
- var import_node_path13 = require("path");
5639
+ var import_node_path15 = require("path");
5496
5640
  var import_node_url = require("url");
5497
5641
  var import_meta4 = {};
5498
5642
  function findPackageRoot(startDir) {
5499
- let current = (0, import_node_path13.resolve)(startDir);
5643
+ let current = (0, import_node_path15.resolve)(startDir);
5500
5644
  while (true) {
5501
- const packagePath = (0, import_node_path13.join)(current, "package.json");
5645
+ const packagePath = (0, import_node_path15.join)(current, "package.json");
5502
5646
  if ((0, import_node_fs8.existsSync)(packagePath)) {
5503
5647
  try {
5504
5648
  const pkg = JSON.parse((0, import_node_fs8.readFileSync)(packagePath, "utf-8"));
@@ -5508,9 +5652,9 @@ function findPackageRoot(startDir) {
5508
5652
  } catch {
5509
5653
  }
5510
5654
  }
5511
- const parent = (0, import_node_path13.dirname)(current);
5655
+ const parent = (0, import_node_path15.dirname)(current);
5512
5656
  if (parent === current) {
5513
- return (0, import_node_path13.resolve)(startDir);
5657
+ return (0, import_node_path15.resolve)(startDir);
5514
5658
  }
5515
5659
  current = parent;
5516
5660
  }
@@ -5519,20 +5663,20 @@ function hasSkillDirectories(dir) {
5519
5663
  if (!(0, import_node_fs8.existsSync)(dir)) return false;
5520
5664
  try {
5521
5665
  return (0, import_node_fs8.readdirSync)(dir).some((entry) => {
5522
- const skillDir = (0, import_node_path13.join)(dir, entry);
5523
- return (0, import_node_fs8.statSync)(skillDir).isDirectory() && (0, import_node_fs8.existsSync)((0, import_node_path13.join)(skillDir, "SKILL.md"));
5666
+ const skillDir = (0, import_node_path15.join)(dir, entry);
5667
+ return (0, import_node_fs8.statSync)(skillDir).isDirectory() && (0, import_node_fs8.existsSync)((0, import_node_path15.join)(skillDir, "SKILL.md"));
5524
5668
  });
5525
5669
  } catch {
5526
5670
  return false;
5527
5671
  }
5528
5672
  }
5529
- function resolveAgentQaSkillsDirectory(startDir = (0, import_node_path13.dirname)((0, import_node_url.fileURLToPath)(import_meta4.url))) {
5673
+ function resolveAgentQaSkillsDirectory(startDir = (0, import_node_path15.dirname)((0, import_node_url.fileURLToPath)(import_meta4.url))) {
5530
5674
  const packageRoot = findPackageRoot(startDir);
5531
- const packagedSkills = (0, import_node_path13.join)(packageRoot, "skills");
5675
+ const packagedSkills = (0, import_node_path15.join)(packageRoot, "skills");
5532
5676
  if (hasSkillDirectories(packagedSkills)) {
5533
5677
  return { path: packagedSkills, source: "package" };
5534
5678
  }
5535
- const sourceSkills = (0, import_node_path13.resolve)(packageRoot, "../..", "skills");
5679
+ const sourceSkills = (0, import_node_path15.resolve)(packageRoot, "../..", "skills");
5536
5680
  if (hasSkillDirectories(sourceSkills)) {
5537
5681
  return { path: sourceSkills, source: "source" };
5538
5682
  }
@@ -5546,21 +5690,21 @@ function parseSkillDescription(skillPath) {
5546
5690
  function listAgentQaSkills(skillsDir) {
5547
5691
  if (!hasSkillDirectories(skillsDir)) return [];
5548
5692
  return (0, import_node_fs8.readdirSync)(skillsDir).filter((entry) => {
5549
- const skillDir = (0, import_node_path13.join)(skillsDir, entry);
5550
- return (0, import_node_fs8.statSync)(skillDir).isDirectory() && (0, import_node_fs8.existsSync)((0, import_node_path13.join)(skillDir, "SKILL.md"));
5693
+ const skillDir = (0, import_node_path15.join)(skillsDir, entry);
5694
+ return (0, import_node_fs8.statSync)(skillDir).isDirectory() && (0, import_node_fs8.existsSync)((0, import_node_path15.join)(skillDir, "SKILL.md"));
5551
5695
  }).sort().map((entry) => {
5552
- const skillDir = (0, import_node_path13.join)(skillsDir, entry);
5696
+ const skillDir = (0, import_node_path15.join)(skillsDir, entry);
5553
5697
  return {
5554
5698
  name: entry,
5555
5699
  path: skillDir,
5556
- description: parseSkillDescription((0, import_node_path13.join)(skillDir, "SKILL.md"))
5700
+ description: parseSkillDescription((0, import_node_path15.join)(skillDir, "SKILL.md"))
5557
5701
  };
5558
5702
  });
5559
5703
  }
5560
5704
  function createSkillsCommand(options = {}) {
5561
5705
  const stdout = options.stdout ?? process.stdout;
5562
5706
  const stderr = options.stderr ?? process.stderr;
5563
- return new import_commander17.Command("skills").description("List packaged agent-qa skills").option("--json", "print machine-readable output").action((opts) => {
5707
+ return new import_commander18.Command("skills").description("List packaged agent-qa skills").option("--json", "print machine-readable output").action((opts) => {
5564
5708
  const resolved = resolveAgentQaSkillsDirectory(options.startDir);
5565
5709
  const skills = listAgentQaSkills(resolved.path);
5566
5710
  if (resolved.source === "missing" || skills.length === 0) {
@@ -5641,13 +5785,14 @@ function rejectRemovedCliSurface(input3 = {}) {
5641
5785
 
5642
5786
  // src/cli.ts
5643
5787
  rejectRemovedCliSurface();
5644
- var program = new import_commander18.Command();
5788
+ var program = new import_commander19.Command();
5645
5789
  program.enablePositionalOptions();
5646
5790
  program.name("agent-qa").description("AI-powered testing framework").version(VERSION);
5647
5791
  program.option("--config <path>", "config file path", "agent-qa.config.yaml").option("--log-level <level>", "log verbosity: silent|error|warn|info|debug").option("--verbose", "shorthand for --log-level debug").option("--quiet", "shorthand for --log-level silent");
5648
5792
  program.addCommand(createRunCommand());
5649
5793
  program.addCommand(createInitCommand());
5650
5794
  program.addCommand(createInstallBrowsersCommand());
5795
+ program.addCommand(createInstallMobileDriversCommand());
5651
5796
  program.addCommand(createDoctorCommand());
5652
5797
  program.addCommand(createDashboardCommand());
5653
5798
  program.addCommand(createServeCommand());