@hasna/testers 0.0.23 → 0.0.25

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/index.js CHANGED
@@ -17171,6 +17171,10 @@ async function runByFilter(options) {
17171
17171
  if (options.scenarioIds && options.scenarioIds.length > 0) {
17172
17172
  const all = listScenarios({ projectId: options.projectId });
17173
17173
  scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
17174
+ if (scenarios.length === 0 && options.projectId) {
17175
+ const global2 = listScenarios({});
17176
+ scenarios = global2.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
17177
+ }
17174
17178
  } else {
17175
17179
  scenarios = listScenarios({
17176
17180
  projectId: options.projectId,
@@ -27115,7 +27119,7 @@ import chalk6 from "chalk";
27115
27119
  // package.json
27116
27120
  var package_default = {
27117
27121
  name: "@hasna/testers",
27118
- version: "0.0.23",
27122
+ version: "0.0.25",
27119
27123
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
27120
27124
  type: "module",
27121
27125
  main: "dist/index.js",
@@ -27473,7 +27477,14 @@ function initProject(options) {
27473
27477
  const projectPath = options.path ?? dir;
27474
27478
  const project = ensureProject(name, projectPath);
27475
27479
  const starterInputs = getStarterScenarios(framework ?? { name: "Unknown", features: [] }, project.id);
27476
- const scenarios = starterInputs.map((input) => createScenario(input));
27480
+ const existingScenarios = listScenarios({ projectId: project.id });
27481
+ const scenarios = existingScenarios.length > 0 ? existingScenarios : starterInputs.map((input) => {
27482
+ try {
27483
+ return createScenario(input);
27484
+ } catch {
27485
+ return null;
27486
+ }
27487
+ }).filter((s) => s !== null);
27477
27488
  const configDir = getTestersDir();
27478
27489
  const configPath = join13(configDir, "config.json");
27479
27490
  if (!existsSync11(configDir)) {
@@ -29833,7 +29844,11 @@ projectCmd.command("list").description("List all projects").action(() => {
29833
29844
  });
29834
29845
  projectCmd.command("show <id>").description("Show project details").action((id) => {
29835
29846
  try {
29836
- const project = getProject(id);
29847
+ let project = getProject(id);
29848
+ if (!project) {
29849
+ const all = listProjects();
29850
+ project = all.find((p) => p.id.startsWith(id) || p.name === id) ?? null;
29851
+ }
29837
29852
  if (!project) {
29838
29853
  logError(chalk6.red(`Project not found: ${id}`));
29839
29854
  process.exit(1);
@@ -30084,7 +30099,7 @@ Shutting down scheduler daemon...`));
30084
30099
  process.exit(1);
30085
30100
  }
30086
30101
  });
30087
- program2.command("init").description("Initialize a new testing project").option("-n, --name <name>", "Project name").option("-u, --url <url>", "Base URL").option("-p, --path <path>", "Project path").option("--ci <provider>", "Generate CI workflow (github)").action(async (opts) => {
30102
+ program2.command("init").description("Initialize a new testing project").option("-n, --name <name>", "Project name").option("-u, --url <url>", "Base URL").option("-p, --path <path>", "Project path").option("--ci <provider>", "Generate CI workflow (github)").option("-y, --yes", "Skip interactive prompts (non-interactive mode)", false).action(async (opts) => {
30088
30103
  try {
30089
30104
  const { project, scenarios, framework, url } = initProject({
30090
30105
  name: opts.name,
@@ -30120,8 +30135,10 @@ program2.command("init").description("Initialize a new testing project").option(
30120
30135
  log(chalk6.yellow(` Unknown CI provider: ${opts.ci}. Supported: github`));
30121
30136
  }
30122
30137
  log("");
30123
- const rl = createInterface({ input: process.stdin, output: process.stdout });
30124
- const ask = (q) => new Promise((resolve2) => rl.question(q, resolve2));
30138
+ if (opts.yes)
30139
+ return;
30140
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
30141
+ const ask = (q) => new Promise((resolve2) => rl2.question(q, resolve2));
30125
30142
  try {
30126
30143
  const envAnswer = await ask(" Would you like to configure environments? [y/N] ");
30127
30144
  if (envAnswer.trim().toLowerCase() === "y") {
@@ -30151,7 +30168,7 @@ program2.command("init").description("Initialize a new testing project").option(
30151
30168
  log("");
30152
30169
  }
30153
30170
  } finally {
30154
- rl.close();
30171
+ rl2.close();
30155
30172
  }
30156
30173
  log(chalk6.bold(" Next steps:"));
30157
30174
  log(` 1. Start your dev server`);
@@ -30427,16 +30444,21 @@ program2.command("chain <scenario-id>").description("Add a dependency to a scena
30427
30444
  process.exit(1);
30428
30445
  }
30429
30446
  });
30430
- program2.command("unchain <scenario-id>").description("Remove a dependency from a scenario").requiredOption("--from <id>", "Dependency to remove").action((scenarioId, opts) => {
30447
+ program2.command("unchain <scenario-id>").description("Remove a dependency from a scenario").requiredOption("--depends-on <id>", "Dependency to remove (alias: --from)").option("--from <id>", "Dependency to remove (alias for --depends-on)").action((scenarioId, opts) => {
30431
30448
  try {
30432
30449
  const scenario = getScenario(scenarioId) ?? getScenarioByShortId(scenarioId);
30433
30450
  if (!scenario) {
30434
30451
  logError(chalk6.red(`Scenario not found: ${scenarioId}`));
30435
30452
  process.exit(1);
30436
30453
  }
30437
- const dep = getScenario(opts.from) ?? getScenarioByShortId(opts.from);
30454
+ const depId = opts.dependsOn ?? opts.from;
30455
+ if (!depId) {
30456
+ logError(chalk6.red("Specify the dependency to remove with --depends-on <id>"));
30457
+ process.exit(1);
30458
+ }
30459
+ const dep = getScenario(depId) ?? getScenarioByShortId(depId);
30438
30460
  if (!dep) {
30439
- logError(chalk6.red(`Dependency not found: ${opts.from}`));
30461
+ logError(chalk6.red(`Dependency not found: ${depId}`));
30440
30462
  process.exit(1);
30441
30463
  }
30442
30464
  removeDependency(scenario.id, dep.id);
@@ -30504,8 +30526,12 @@ flowCmd.command("create <name>").description("Create a flow from scenario IDs").
30504
30526
  process.exit(1);
30505
30527
  }
30506
30528
  });
30507
- flowCmd.command("list").description("List all flows").option("--project <id>", "Project ID").action((opts) => {
30529
+ flowCmd.command("list").description("List all flows").option("--project <id>", "Project ID").option("--json", "Output as JSON", false).action((opts) => {
30508
30530
  const flows = listFlows(resolveProject(opts.project) ?? undefined);
30531
+ if (opts.json) {
30532
+ log(JSON.stringify(flows, null, 2));
30533
+ return;
30534
+ }
30509
30535
  if (flows.length === 0) {
30510
30536
  log(chalk6.dim(`
30511
30537
  No flows found.
@@ -30952,10 +30978,14 @@ SCAN_COMMON_OPTIONS(scanCmd.command("all <url>").description("Run all scanners:
30952
30978
  process.exit(1);
30953
30979
  }
30954
30980
  });
30955
- scanCmd.command("issues").description("List tracked scan issues").option("--status <status>", "Filter by status: open|resolved|regressed").option("--type <type>", "Filter by type: console_error|network_error|broken_link|performance").option("--project <id>", "Filter by project ID").option("--limit <n>", "Max results", "50").action((opts) => {
30981
+ scanCmd.command("issues").description("List tracked scan issues").option("--status <status>", "Filter by status: open|resolved|regressed").option("--type <type>", "Filter by type: console_error|network_error|broken_link|performance").option("--project <id>", "Filter by project ID").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON", false).action((opts) => {
30956
30982
  try {
30957
30983
  const { listScanIssues: listScanIssues2 } = (init_scan_issues(), __toCommonJS(exports_scan_issues));
30958
30984
  const issues = listScanIssues2({ status: opts.status, type: opts.type, projectId: opts.project, limit: parseInt(opts.limit) });
30985
+ if (opts.json) {
30986
+ log(JSON.stringify(issues, null, 2));
30987
+ return;
30988
+ }
30959
30989
  if (issues.length === 0) {
30960
30990
  log(chalk6.dim("No scan issues found."));
30961
30991
  return;
@@ -31215,10 +31245,27 @@ apiCmd.command("list").description("List API checks").option("--project <id>", "
31215
31245
  process.exit(1);
31216
31246
  }
31217
31247
  });
31218
- apiCmd.command("add").description("Add a new API check interactively").option("--project <id>", "Project ID").action(async (opts) => {
31219
- const rl = createInterface({ input: process.stdin, output: process.stdout });
31220
- const ask = (q) => new Promise((res) => rl.question(q, res));
31248
+ apiCmd.command("add").description("Add a new API check (interactive if no --url given)").option("--project <id>", "Project ID").option("-n, --name <name>", "Check name (non-interactive)").option("-u, --url <url>", "URL to check, full or path (non-interactive)").option("-m, --method <method>", "HTTP method (default: GET)").option("--status <code>", "Expected HTTP status code (default: 200)").option("--contains <text>", "Body must contain this string").option("--response-time <ms>", "Max acceptable response time in ms").option("-t, --tag <tag>", "Tag (repeatable)", []).action(async (opts) => {
31221
31249
  try {
31250
+ if (opts.url) {
31251
+ const projectId2 = resolveProject(opts.project);
31252
+ const check2 = createApiCheck({
31253
+ name: opts.name?.trim() || opts.url,
31254
+ method: opts.method?.toUpperCase() ?? "GET",
31255
+ url: opts.url.trim(),
31256
+ expectedStatus: opts.status ? parseInt(opts.status, 10) : 200,
31257
+ expectedBodyContains: opts.contains || undefined,
31258
+ expectedResponseTimeMs: opts.responseTime ? parseInt(opts.responseTime, 10) : undefined,
31259
+ tags: opts.tag ?? [],
31260
+ projectId: projectId2
31261
+ });
31262
+ log("");
31263
+ log(chalk6.green(`\u2713 Created API check ${chalk6.bold(check2.name)} (${check2.shortId})`));
31264
+ log(chalk6.dim(` ${check2.method} ${check2.url} \u2192 expect ${check2.expectedStatus}`));
31265
+ return;
31266
+ }
31267
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
31268
+ const ask = (q) => new Promise((res) => rl2.question(q, res));
31222
31269
  const name = await ask("Name: ");
31223
31270
  if (!name.trim()) {
31224
31271
  logError(chalk6.red("Name is required"));
@@ -31237,7 +31284,7 @@ apiCmd.command("add").description("Add a new API check interactively").option("-
31237
31284
  const expectedStatus = statusInput.trim() ? parseInt(statusInput.trim(), 10) : 200;
31238
31285
  const bodyContains = await ask("Body must contain (optional, press enter to skip): ");
31239
31286
  const tagsInput = await ask("Tags (comma-separated, optional): ");
31240
- rl.close();
31287
+ rl2.close();
31241
31288
  const projectId = resolveProject(opts.project);
31242
31289
  const check = createApiCheck({
31243
31290
  name: name.trim(),
@@ -31252,7 +31299,6 @@ apiCmd.command("add").description("Add a new API check interactively").option("-
31252
31299
  log(chalk6.green(`\u2713 Created API check ${chalk6.bold(check.name)} (${check.shortId})`));
31253
31300
  log(chalk6.dim(` ${check.method} ${check.url} \u2192 expect ${check.expectedStatus}`));
31254
31301
  } catch (error) {
31255
- rl.close();
31256
31302
  logError(chalk6.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
31257
31303
  process.exit(1);
31258
31304
  }
@@ -31366,9 +31412,9 @@ apiCmd.command("delete <id>").description("Delete an API check").option("-y, --y
31366
31412
  process.exit(1);
31367
31413
  }
31368
31414
  if (!opts.yes) {
31369
- const rl = createInterface({ input: process.stdin, output: process.stdout });
31370
- const answer = await new Promise((res) => rl.question(`Delete "${check.name}" (${check.shortId})? [y/N] `, res));
31371
- rl.close();
31415
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
31416
+ const answer = await new Promise((res) => rl2.question(`Delete "${check.name}" (${check.shortId})? [y/N] `, res));
31417
+ rl2.close();
31372
31418
  if (answer.toLowerCase() !== "y") {
31373
31419
  log(chalk6.dim("Cancelled."));
31374
31420
  return;
@@ -31589,27 +31635,49 @@ personaCmd.command("list").description("List personas").option("--project <id>",
31589
31635
  process.exit(1);
31590
31636
  }
31591
31637
  });
31592
- personaCmd.command("add").description("Create a persona interactively").option("--global", "Create as a global persona (no project scope)", false).option("--project <id>", "Project ID").action(async (opts) => {
31593
- const rl = createInterface({ input: process.stdin, output: process.stdout });
31594
- const ask = (q) => new Promise((res) => rl.question(q, res));
31638
+ personaCmd.command("add").description("Create a persona (interactive if no --name/--role given)").option("--global", "Create as a global persona (no project scope)", false).option("--project <id>", "Project ID").option("-n, --name <name>", "Persona name (non-interactive)").option("-r, --role <role>", "Persona role (non-interactive)").option("-d, --description <text>", "Persona description").option("-i, --instructions <text>", "Behavior instructions").option("--traits <list>", "Comma-separated traits (e.g. impatient,curious)").option("--goals <list>", "Comma-separated goals").option("--auth-email <email>", "Login email for auth testing").option("--auth-password <pass>", "Login password for auth testing").option("--auth-login-path <path>", "Login page path (default: /login)").action(async (opts) => {
31595
31639
  try {
31640
+ if (opts.name && opts.role) {
31641
+ const projectId2 = opts.global ? undefined : resolveProject(opts.project);
31642
+ const traits2 = opts.traits ? opts.traits.split(",").map((t) => t.trim()).filter(Boolean) : [];
31643
+ const goals2 = opts.goals ? opts.goals.split(",").map((g) => g.trim()).filter(Boolean) : [];
31644
+ const persona2 = createPersona({
31645
+ name: opts.name.trim(),
31646
+ role: opts.role.trim(),
31647
+ description: opts.description?.trim() ?? "",
31648
+ instructions: opts.instructions?.trim() ?? "",
31649
+ traits: traits2,
31650
+ goals: goals2,
31651
+ projectId: projectId2,
31652
+ authEmail: opts.authEmail,
31653
+ authPassword: opts.authPassword,
31654
+ authLoginPath: opts.authLoginPath
31655
+ });
31656
+ log("");
31657
+ log(chalk6.green(`Created persona ${chalk6.bold(persona2.shortId)}: ${persona2.name}`));
31658
+ log(chalk6.dim(` Role: ${persona2.role}`));
31659
+ log(chalk6.dim(` Scope: ${persona2.projectId ? "project" : "global"}`));
31660
+ return;
31661
+ }
31662
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
31663
+ const ask = (q) => new Promise((res) => rl2.question(q, res));
31596
31664
  const name = await ask("Name: ");
31597
31665
  if (!name.trim()) {
31598
31666
  logError(chalk6.red("Name is required"));
31599
- rl.close();
31667
+ rl2.close();
31600
31668
  process.exit(1);
31601
31669
  }
31602
31670
  const role = await ask("Role (e.g. first-time user, admin, power user): ");
31603
31671
  if (!role.trim()) {
31604
31672
  logError(chalk6.red("Role is required"));
31605
- rl.close();
31673
+ rl2.close();
31606
31674
  process.exit(1);
31607
31675
  }
31608
31676
  const description = await ask("Description (optional): ");
31609
31677
  const instructions = await ask("Instructions \u2014 how should this persona behave? (optional): ");
31610
31678
  const traitsInput = await ask("Traits (comma-separated, e.g. impatient,curious): ");
31611
31679
  const goalsInput = await ask("Goals (comma-separated): ");
31612
- rl.close();
31680
+ rl2.close();
31613
31681
  const projectId = opts.global ? undefined : resolveProject(opts.project);
31614
31682
  const traits = traitsInput.trim() ? traitsInput.split(",").map((t) => t.trim()).filter(Boolean) : [];
31615
31683
  const goals = goalsInput.trim() ? goalsInput.split(",").map((g) => g.trim()).filter(Boolean) : [];
@@ -31909,13 +31977,28 @@ evalCmd.command("rag <url>").description("Run RAG quality evaluation \u2014 fait
31909
31977
  }
31910
31978
  });
31911
31979
  var goldenCmd = program2.command("golden").description("Manage golden answer checks for hallucination detection");
31912
- goldenCmd.command("add").description("Add a golden answer check interactively").option("--project <id>", "Project ID").action(async (opts) => {
31980
+ goldenCmd.command("add").description("Add a golden answer check (interactive if no --question given)").option("--project <id>", "Project ID").option("-q, --question <text>", "Question the endpoint should answer (non-interactive)").option("-a, --answer <text>", "Expected golden answer (non-interactive)").option("-e, --endpoint <path>", "Endpoint path or URL (non-interactive)").option("--judge-model <model>", "Model to use as judge").action(async (opts) => {
31913
31981
  try {
31914
31982
  const { createGoldenAnswer: createGoldenAnswer2 } = await Promise.resolve().then(() => (init_golden_answers(), exports_golden_answers));
31983
+ if (opts.question && opts.answer && opts.endpoint) {
31984
+ const projectId2 = resolveProject(opts.project);
31985
+ const golden2 = createGoldenAnswer2({
31986
+ question: opts.question,
31987
+ goldenAnswer: opts.answer,
31988
+ endpoint: opts.endpoint,
31989
+ judgeModel: opts.judgeModel || undefined,
31990
+ projectId: projectId2
31991
+ });
31992
+ log(chalk6.green(`
31993
+ Created golden answer check ${chalk6.bold(golden2.shortId)}`));
31994
+ log(` Endpoint: ${golden2.endpoint}`);
31995
+ log(` Question: ${golden2.question.slice(0, 60)}`);
31996
+ return;
31997
+ }
31915
31998
  const ask = (prompt) => {
31916
- const rl = createInterface({ input: process.stdin, output: process.stdout });
31917
- return new Promise((resolve2) => rl.question(prompt, (ans) => {
31918
- rl.close();
31999
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
32000
+ return new Promise((resolve2) => rl2.question(prompt, (ans) => {
32001
+ rl2.close();
31919
32002
  resolve2(ans.trim());
31920
32003
  }));
31921
32004
  };
package/dist/index.js CHANGED
@@ -15528,6 +15528,10 @@ async function runByFilter(options) {
15528
15528
  if (options.scenarioIds && options.scenarioIds.length > 0) {
15529
15529
  const all = listScenarios({ projectId: options.projectId });
15530
15530
  scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
15531
+ if (scenarios.length === 0 && options.projectId) {
15532
+ const global2 = listScenarios({});
15533
+ scenarios = global2.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
15534
+ }
15531
15535
  } else {
15532
15536
  scenarios = listScenarios({
15533
15537
  projectId: options.projectId,
@@ -16329,7 +16333,14 @@ function initProject(options) {
16329
16333
  const projectPath = options.path ?? dir;
16330
16334
  const project = ensureProject(name, projectPath);
16331
16335
  const starterInputs = getStarterScenarios(framework ?? { name: "Unknown", features: [] }, project.id);
16332
- const scenarios = starterInputs.map((input) => createScenario(input));
16336
+ const existingScenarios = listScenarios({ projectId: project.id });
16337
+ const scenarios = existingScenarios.length > 0 ? existingScenarios : starterInputs.map((input) => {
16338
+ try {
16339
+ return createScenario(input);
16340
+ } catch {
16341
+ return null;
16342
+ }
16343
+ }).filter((s) => s !== null);
16333
16344
  const configDir = getTestersDir();
16334
16345
  const configPath = join13(configDir, "config.json");
16335
16346
  if (!existsSync11(configDir)) {
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAmDjE;AAID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAC/C,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAkNvB;AAID,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;IAC1C,SAAS,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,EAAE,CAAC;IAC/C,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAuC5D"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAiB,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAmDjE;AAID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAC/C,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAkNvB;AAID,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;IAC1C,SAAS,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,EAAE,CAAC;IAC/C,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CA0C5D"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -16178,6 +16178,10 @@ async function runByFilter(options) {
16178
16178
  if (options.scenarioIds && options.scenarioIds.length > 0) {
16179
16179
  const all = listScenarios({ projectId: options.projectId });
16180
16180
  scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
16181
+ if (scenarios.length === 0 && options.projectId) {
16182
+ const global2 = listScenarios({});
16183
+ scenarios = global2.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
16184
+ }
16181
16185
  } else {
16182
16186
  scenarios = listScenarios({
16183
16187
  projectId: options.projectId,
@@ -18871,6 +18871,10 @@ async function runByFilter(options) {
18871
18871
  if (options.scenarioIds && options.scenarioIds.length > 0) {
18872
18872
  const all = listScenarios({ projectId: options.projectId });
18873
18873
  scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
18874
+ if (scenarios.length === 0 && options.projectId) {
18875
+ const global2 = listScenarios({});
18876
+ scenarios = global2.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
18877
+ }
18874
18878
  } else {
18875
18879
  scenarios = listScenarios({
18876
18880
  projectId: options.projectId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",