@xera-ai/core 0.9.1 → 0.9.3

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.
@@ -513,9 +513,9 @@ function extractPomUsage(specContent) {
513
513
  }
514
514
  async function recordScriptImpl(repoRoot, ticket) {
515
515
  const ticketDir = join5(repoRoot, ".xera", ticket);
516
- const featurePath = join5(ticketDir, "feature", `${ticket}.feature`);
517
- const specPath = join5(ticketDir, "tests", `${ticket}.spec.ts`);
518
- const pomDir = join5(ticketDir, "poms");
516
+ const featurePath = existsSync6(join5(ticketDir, "test.feature")) ? join5(ticketDir, "test.feature") : join5(ticketDir, "feature", `${ticket}.feature`);
517
+ const specPath = existsSync6(join5(ticketDir, "spec.ts")) ? join5(ticketDir, "spec.ts") : join5(ticketDir, "tests", `${ticket}.spec.ts`);
518
+ const pomDir = existsSync6(join5(ticketDir, "page-objects")) ? join5(ticketDir, "page-objects") : join5(ticketDir, "poms");
519
519
  if (!existsSync6(featurePath)) {
520
520
  console.error(`[graph-record script] feature missing`);
521
521
  return 1;
@@ -7612,6 +7612,46 @@ var init_dist = __esm(() => {
7612
7612
  $visitAsync = visit.visitAsync;
7613
7613
  });
7614
7614
 
7615
+ // src/artifact/paths.ts
7616
+ import { join as join6 } from "path";
7617
+ function resolveArtifactPaths(repoRoot, ticket) {
7618
+ if (!TICKET_RE.test(ticket)) {
7619
+ throw new Error(`Invalid ticket key: "${ticket}" (expected e.g. JIRA-123 or SAMPLE-001)`);
7620
+ }
7621
+ const ticketDir = join6(repoRoot, ".xera", ticket);
7622
+ return {
7623
+ ticketDir,
7624
+ storyPath: join6(ticketDir, "story.md"),
7625
+ featurePath: join6(ticketDir, "test.feature"),
7626
+ specPath: join6(ticketDir, "spec.ts"),
7627
+ pageObjectsDir: join6(ticketDir, "page-objects"),
7628
+ runsDir: join6(ticketDir, "runs"),
7629
+ metaPath: join6(ticketDir, "meta.json"),
7630
+ statusPath: join6(ticketDir, "status.json"),
7631
+ logPath: join6(ticketDir, "xera.log"),
7632
+ lockPath: join6(ticketDir, ".lock"),
7633
+ authDir: join6(repoRoot, ".xera", ".auth"),
7634
+ runPath: (runId) => {
7635
+ const runDir = join6(ticketDir, "runs", runId);
7636
+ return {
7637
+ runDir,
7638
+ reportJsonPath: join6(runDir, "report.json"),
7639
+ tracePath: join6(runDir, "trace.zip"),
7640
+ normalizedPath: join6(runDir, "normalized.json"),
7641
+ screenshotsDir: join6(runDir, "screenshots"),
7642
+ videoDir: join6(runDir, "videos")
7643
+ };
7644
+ }
7645
+ };
7646
+ }
7647
+ function generateRunId(now = new Date) {
7648
+ return now.toISOString().replace(/[:.]/g, "-").replace("Z", "");
7649
+ }
7650
+ var TICKET_RE;
7651
+ var init_paths2 = __esm(() => {
7652
+ TICKET_RE = /^[A-Z][A-Z0-9_]*-\d+$|^SAMPLE-\d+$/;
7653
+ });
7654
+
7615
7655
  // src/bin-internal/graph-record.ts
7616
7656
  var exports_graph_record = {};
7617
7657
  __export(exports_graph_record, {
@@ -7620,7 +7660,7 @@ __export(exports_graph_record, {
7620
7660
  });
7621
7661
  import { createHash as createHash3 } from "crypto";
7622
7662
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
7623
- import { basename as basename2, join as join6 } from "path";
7663
+ import { basename as basename2, join as join7 } from "path";
7624
7664
  function nowIso2() {
7625
7665
  return new Date().toISOString();
7626
7666
  }
@@ -7644,7 +7684,7 @@ function makeEvent(actor, type, payload) {
7644
7684
  };
7645
7685
  }
7646
7686
  function readStoryFrontmatter(repoRoot, ticket) {
7647
- const path = join6(repoRoot, ".xera", ticket, "story.md");
7687
+ const path = join7(repoRoot, ".xera", ticket, "story.md");
7648
7688
  if (!existsSync7(path))
7649
7689
  return null;
7650
7690
  const raw = readFileSync5(path, "utf8");
@@ -7654,7 +7694,7 @@ function readStoryFrontmatter(repoRoot, ticket) {
7654
7694
  return $parse(m[1]);
7655
7695
  }
7656
7696
  function readGraphInput(repoRoot, ticket) {
7657
- const path = join6(repoRoot, ".xera", ticket, "graph-input.json");
7697
+ const path = join7(repoRoot, ".xera", ticket, "graph-input.json");
7658
7698
  if (!existsSync7(path))
7659
7699
  return { modifiesAreas: [] };
7660
7700
  try {
@@ -7706,32 +7746,33 @@ async function recordScript(repoRoot, ticket) {
7706
7746
  return recordScriptImpl2(repoRoot, ticket);
7707
7747
  }
7708
7748
  async function recordExec(repoRoot, ticket, runId) {
7709
- const reporterPath = join6(repoRoot, ".xera", ticket, "runs", runId, "reporter.json");
7710
- if (!existsSync7(reporterPath)) {
7711
- console.error(`[graph-record exec] reporter.json missing`);
7749
+ const { normalizedPath } = resolveArtifactPaths(repoRoot, ticket).runPath(runId);
7750
+ if (!existsSync7(normalizedPath)) {
7751
+ console.error(`[graph-record exec] normalized.json missing`);
7712
7752
  return 1;
7713
7753
  }
7714
- const data = JSON.parse(readFileSync5(reporterPath, "utf8"));
7754
+ const data = JSON.parse(readFileSync5(normalizedPath, "utf8"));
7715
7755
  const events = [];
7716
7756
  for (const s of data.scenarios) {
7757
+ if (s.outcome === "SKIPPED")
7758
+ continue;
7717
7759
  const p = {
7718
7760
  scenarioId: scenarioId(ticket, s.name),
7719
7761
  ticketId: ticket,
7720
7762
  runId,
7721
- status: s.status,
7722
- runtime: s.runtime
7763
+ status: s.outcome === "PASS" ? "pass" : "fail",
7764
+ runtime: 0
7723
7765
  };
7724
- if (s.traceId)
7725
- p.traceId = s.traceId;
7726
7766
  events.push(makeEvent("xera-exec", "run.completed", p));
7727
7767
  }
7728
7768
  appendEvents(repoRoot, events, { skill: "xera-exec", ticketId: ticket });
7729
7769
  return 0;
7730
7770
  }
7731
7771
  async function recordClassify(repoRoot, ticket, runId) {
7732
- const classifyPath = join6(repoRoot, ".xera", ticket, "runs", runId, "classifier-output.json");
7772
+ const { ticketDir } = resolveArtifactPaths(repoRoot, ticket);
7773
+ const classifyPath = join7(ticketDir, "classifier-input.json");
7733
7774
  if (!existsSync7(classifyPath)) {
7734
- console.error(`[graph-record classify] classifier-output.json missing`);
7775
+ console.error(`[graph-record classify] classifier-input.json missing`);
7735
7776
  return 1;
7736
7777
  }
7737
7778
  const data = JSON.parse(readFileSync5(classifyPath, "utf8"));
@@ -7866,6 +7907,7 @@ async function graphRecordCmd(argv) {
7866
7907
  }
7867
7908
  var init_graph_record = __esm(() => {
7868
7909
  init_dist();
7910
+ init_paths2();
7869
7911
  init_store();
7870
7912
  init_ulid();
7871
7913
  });
@@ -7876,9 +7918,9 @@ __export(exports_graph_backfill, {
7876
7918
  graphBackfillCmd: () => graphBackfillCmd
7877
7919
  });
7878
7920
  import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
7879
- import { join as join7 } from "path";
7921
+ import { join as join8 } from "path";
7880
7922
  async function backfillTicket(repoRoot, ticket, dryRun) {
7881
- const storyPath = join7(repoRoot, ".xera", ticket, "story.md");
7923
+ const storyPath = join8(repoRoot, ".xera", ticket, "story.md");
7882
7924
  if (!existsSync8(storyPath))
7883
7925
  return 0;
7884
7926
  const { recordFetch: recordFetch2 } = await Promise.resolve().then(() => (init_graph_record(), exports_graph_record));
@@ -7886,14 +7928,14 @@ async function backfillTicket(repoRoot, ticket, dryRun) {
7886
7928
  console.log(`[backfill dry-run] would backfill ${ticket}`);
7887
7929
  return 0;
7888
7930
  }
7889
- await recordScriptImpl(repoRoot, ticket);
7890
7931
  await recordFetch2(repoRoot, ticket);
7932
+ await recordScriptImpl(repoRoot, ticket);
7891
7933
  return 0;
7892
7934
  }
7893
7935
  async function graphBackfillCmd(argv) {
7894
7936
  const dryRun = argv.includes("--dry-run");
7895
7937
  const repoRoot = process.cwd();
7896
- const xeraDir = join7(repoRoot, ".xera");
7938
+ const xeraDir = join8(repoRoot, ".xera");
7897
7939
  if (!existsSync8(xeraDir)) {
7898
7940
  console.log("[backfill] no .xera/ directory");
7899
7941
  return 0;
@@ -8203,7 +8245,7 @@ async function disputesCmd(argv) {
8203
8245
 
8204
8246
  // src/bin-internal/doctor.ts
8205
8247
  import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "fs";
8206
- import { join as join8 } from "path";
8248
+ import { join as join9 } from "path";
8207
8249
 
8208
8250
  // src/graph/cost.ts
8209
8251
  import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "fs";
@@ -8314,7 +8356,7 @@ function frontmatterField(content, field) {
8314
8356
  return m?.[1] ?? null;
8315
8357
  }
8316
8358
  function checkGoldenEvalDir(repoRoot) {
8317
- const root = join8(repoRoot, "fixtures/golden-eval");
8359
+ const root = join9(repoRoot, "fixtures/golden-eval");
8318
8360
  if (!existsSync9(root))
8319
8361
  return [{ ok: false, message: "fixtures/golden-eval/ does not exist" }];
8320
8362
  const dirs = readdirSync4(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("."));
@@ -8326,8 +8368,8 @@ function checkGoldenEvalDir(repoRoot) {
8326
8368
  });
8327
8369
  }
8328
8370
  for (const entry of dirs) {
8329
- const dir = join8(root, entry.name);
8330
- const metaPath = join8(dir, "meta.json");
8371
+ const dir = join9(root, entry.name);
8372
+ const metaPath = join9(dir, "meta.json");
8331
8373
  if (!existsSync9(metaPath)) {
8332
8374
  results.push({ ok: false, message: `${entry.name}: meta.json missing` });
8333
8375
  continue;
@@ -8345,12 +8387,12 @@ function checkGoldenEvalDir(repoRoot) {
8345
8387
  const stages = Array.isArray(meta.stages) ? meta.stages : [];
8346
8388
  if (stages.length === 0)
8347
8389
  results.push({ ok: false, message: `${entry.name}: meta.stages is empty` });
8348
- if (!existsSync9(join8(dir, "story.md")))
8390
+ if (!existsSync9(join9(dir, "story.md")))
8349
8391
  results.push({ ok: false, message: `${entry.name}: story.md missing` });
8350
8392
  for (const stage of stages) {
8351
8393
  const required = REQUIRED_FILES_PER_STAGE[stage] ?? [];
8352
8394
  for (const rel of required) {
8353
- if (!existsSync9(join8(dir, rel))) {
8395
+ if (!existsSync9(join9(dir, rel))) {
8354
8396
  results.push({
8355
8397
  ok: false,
8356
8398
  message: `${meta.id ?? entry.name}: stage "${stage}" declared but ${rel} missing`
@@ -8362,7 +8404,7 @@ function checkGoldenEvalDir(repoRoot) {
8362
8404
  return results;
8363
8405
  }
8364
8406
  function checkRubricPrompt(repoRoot) {
8365
- const path = join8(repoRoot, "packages/prompts/eval-rubric.md");
8407
+ const path = join9(repoRoot, "packages/prompts/eval-rubric.md");
8366
8408
  if (!existsSync9(path))
8367
8409
  return [{ ok: false, message: "packages/prompts/eval-rubric.md missing" }];
8368
8410
  const text = readFileSync6(path, "utf8");
@@ -8375,7 +8417,7 @@ function checkRubricPrompt(repoRoot) {
8375
8417
  return [];
8376
8418
  }
8377
8419
  function checkEvalSkill(repoRoot) {
8378
- const path = join8(repoRoot, "packages/skills/xera-eval.md");
8420
+ const path = join9(repoRoot, "packages/skills/xera-eval.md");
8379
8421
  if (!existsSync9(path))
8380
8422
  return [{ ok: false, message: "packages/skills/xera-eval.md missing" }];
8381
8423
  const text = readFileSync6(path, "utf8");
@@ -8387,7 +8429,7 @@ function checkPromptInjectionPreamble(repoRoot) {
8387
8429
  return verifyPrompts(repoRoot);
8388
8430
  }
8389
8431
  function checkRootScripts(repoRoot) {
8390
- const path = join8(repoRoot, "package.json");
8432
+ const path = join9(repoRoot, "package.json");
8391
8433
  if (!existsSync9(path))
8392
8434
  return [{ ok: false, message: "root package.json missing" }];
8393
8435
  const pkg = JSON.parse(readFileSync6(path, "utf8"));
@@ -8396,7 +8438,7 @@ function checkRootScripts(repoRoot) {
8396
8438
  return missing.map((s) => ({ ok: false, message: `root package.json missing script: ${s}` }));
8397
8439
  }
8398
8440
  function isXeraMonorepo(repoRoot) {
8399
- return existsSync9(join8(repoRoot, "packages/skills")) && existsSync9(join8(repoRoot, "packages/prompts"));
8441
+ return existsSync9(join9(repoRoot, "packages/skills")) && existsSync9(join9(repoRoot, "packages/prompts"));
8400
8442
  }
8401
8443
  async function doctorCmd(argv, opts = {}) {
8402
8444
  const repoRoot = opts.cwd ?? process.cwd();
@@ -8418,7 +8460,7 @@ async function doctorCmd(argv, opts = {}) {
8418
8460
  if (top)
8419
8461
  console.log(` Top skill: ${top[0]} (${top[1].calls} calls, $${top[1].usd.toFixed(2)})`);
8420
8462
  }
8421
- const xeraDir = join8(repoRoot, ".xera");
8463
+ const xeraDir = join9(repoRoot, ".xera");
8422
8464
  if (existsSync9(xeraDir)) {
8423
8465
  const ticketDirs = readdirSync4(xeraDir, { withFileTypes: true }).filter((e) => e.isDirectory() && /^[A-Z]+-\d+$/.test(e.name));
8424
8466
  if (ticketDirs.length > 0) {
@@ -8455,25 +8497,25 @@ async function doctorCmd(argv, opts = {}) {
8455
8497
 
8456
8498
  // src/bin-internal/eval-deterministic.ts
8457
8499
  import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
8458
- import { join as join10 } from "path";
8500
+ import { join as join11 } from "path";
8459
8501
  import { validateGherkin } from "@xera-ai/web";
8460
8502
 
8461
8503
  // src/eval/paths.ts
8462
- import { join as join9 } from "path";
8504
+ import { join as join10 } from "path";
8463
8505
  function resolveEvalPaths(cwd, runId) {
8464
- const root = join9(cwd, ".xera", "eval", runId);
8506
+ const root = join10(cwd, ".xera", "eval", runId);
8465
8507
  return {
8466
8508
  root,
8467
- manifest: join9(root, "manifest.json"),
8468
- lock: join9(root, ".lock"),
8469
- deterministicScores: join9(root, "deterministic-scores.json"),
8470
- judgeScores: join9(root, "judge-scores.json"),
8471
- report: join9(root, "report.md"),
8472
- summary: join9(root, "summary.json"),
8473
- inputsDir: join9(root, "inputs"),
8474
- actualDir: join9(root, "actual"),
8475
- ticketInputsDir: (ticket) => join9(root, "inputs", ticket),
8476
- ticketActualDir: (ticket) => join9(root, "actual", ticket)
8509
+ manifest: join10(root, "manifest.json"),
8510
+ lock: join10(root, ".lock"),
8511
+ deterministicScores: join10(root, "deterministic-scores.json"),
8512
+ judgeScores: join10(root, "judge-scores.json"),
8513
+ report: join10(root, "report.md"),
8514
+ summary: join10(root, "summary.json"),
8515
+ inputsDir: join10(root, "inputs"),
8516
+ actualDir: join10(root, "actual"),
8517
+ ticketInputsDir: (ticket) => join10(root, "inputs", ticket),
8518
+ ticketActualDir: (ticket) => join10(root, "actual", ticket)
8477
8519
  };
8478
8520
  }
8479
8521
 
@@ -8575,15 +8617,15 @@ function checkFeatureFromStory(actualFeaturePath) {
8575
8617
  }
8576
8618
  }
8577
8619
  function checkScriptFromFeature(actualTicketDir) {
8578
- const specPath = join10(actualTicketDir, "spec.ts");
8620
+ const specPath = join11(actualTicketDir, "spec.ts");
8579
8621
  if (!existsSync10(specPath)) {
8580
8622
  return { passed: false, checks: ["file-presence"], error: "actual missing: spec.ts" };
8581
8623
  }
8582
8624
  return { passed: true, checks: ["file-presence"] };
8583
8625
  }
8584
8626
  function checkDiagnoseFailure(inputsTicketDir, actualTicketDir) {
8585
- const inputPath = join10(inputsTicketDir, "classifier-input.json");
8586
- const actualPath = join10(actualTicketDir, "classification.json");
8627
+ const inputPath = join11(inputsTicketDir, "classifier-input.json");
8628
+ const actualPath = join11(actualTicketDir, "classification.json");
8587
8629
  if (!existsSync10(actualPath)) {
8588
8630
  return {
8589
8631
  passed: false,
@@ -8641,7 +8683,7 @@ async function evalDeterministicCmd(argv, opts = {}) {
8641
8683
  const actualDir = paths.ticketActualDir(ticket);
8642
8684
  let result;
8643
8685
  if (stage === "feature-from-story") {
8644
- result = checkFeatureFromStory(join10(actualDir, "test.feature"));
8686
+ result = checkFeatureFromStory(join11(actualDir, "test.feature"));
8645
8687
  } else if (stage === "script-from-feature") {
8646
8688
  result = checkScriptFromFeature(actualDir);
8647
8689
  } else {
@@ -8674,7 +8716,7 @@ import {
8674
8716
  readFileSync as readFileSync9,
8675
8717
  writeFileSync as writeFileSync4
8676
8718
  } from "fs";
8677
- import { join as join11 } from "path";
8719
+ import { join as join12 } from "path";
8678
8720
 
8679
8721
  // src/eval/run-id.ts
8680
8722
  import { execSync } from "child_process";
@@ -8688,7 +8730,7 @@ function defaultGetGitSha() {
8688
8730
  function pad(n) {
8689
8731
  return n.toString().padStart(2, "0");
8690
8732
  }
8691
- function generateRunId(opts = {}) {
8733
+ function generateRunId2(opts = {}) {
8692
8734
  const getGitSha = opts.getGitSha ?? defaultGetGitSha;
8693
8735
  const now = (opts.now ?? (() => new Date))();
8694
8736
  const date = `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}`;
@@ -8767,7 +8809,7 @@ function parseFlags2(argv) {
8767
8809
  return flags;
8768
8810
  }
8769
8811
  function readPromptVersion(repoRoot, name) {
8770
- const path = join11(repoRoot, "packages/prompts", `${name}.md`);
8812
+ const path = join12(repoRoot, "packages/prompts", `${name}.md`);
8771
8813
  if (!existsSync12(path))
8772
8814
  return "0.0.0";
8773
8815
  const text = readFileSync9(path, "utf8");
@@ -8775,7 +8817,7 @@ function readPromptVersion(repoRoot, name) {
8775
8817
  return m?.[1] ?? "0.0.0";
8776
8818
  }
8777
8819
  function discoverEvalTickets(repoRoot) {
8778
- const root = join11(repoRoot, "fixtures/golden-eval");
8820
+ const root = join12(repoRoot, "fixtures/golden-eval");
8779
8821
  if (!existsSync12(root))
8780
8822
  return [];
8781
8823
  const out = [];
@@ -8784,8 +8826,8 @@ function discoverEvalTickets(repoRoot) {
8784
8826
  continue;
8785
8827
  if (entry.name === "README.md" || entry.name.startsWith("."))
8786
8828
  continue;
8787
- const dir = join11(root, entry.name);
8788
- const metaPath = join11(dir, "meta.json");
8829
+ const dir = join12(root, entry.name);
8830
+ const metaPath = join12(dir, "meta.json");
8789
8831
  if (!existsSync12(metaPath))
8790
8832
  continue;
8791
8833
  const meta = JSON.parse(readFileSync9(metaPath, "utf8"));
@@ -8794,14 +8836,14 @@ function discoverEvalTickets(repoRoot) {
8794
8836
  return out.sort((a, b) => a.id.localeCompare(b.id));
8795
8837
  }
8796
8838
  function discoverClassifierTickets(repoRoot) {
8797
- const root = join11(repoRoot, "fixtures/golden-tickets");
8839
+ const root = join12(repoRoot, "fixtures/golden-tickets");
8798
8840
  if (!existsSync12(root))
8799
8841
  return [];
8800
8842
  const out = [];
8801
8843
  for (const entry of readdirSync5(root, { withFileTypes: true })) {
8802
8844
  if (!entry.isFile() || !entry.name.endsWith(".json"))
8803
8845
  continue;
8804
- const path = join11(root, entry.name);
8846
+ const path = join12(root, entry.name);
8805
8847
  const data = JSON.parse(readFileSync9(path, "utf8"));
8806
8848
  if (typeof data.ticket === "string")
8807
8849
  out.push({ id: data.ticket, path });
@@ -8856,7 +8898,7 @@ async function evalPrepareCmd(argv, opts = {}) {
8856
8898
  console.error("[xera:eval-prepare] No tickets applicable to requested stages.");
8857
8899
  return 1;
8858
8900
  }
8859
- const runId = generateRunId({
8901
+ const runId = generateRunId2({
8860
8902
  ...opts.now ? { now: opts.now } : {},
8861
8903
  ...opts.getGitSha ? { getGitSha: opts.getGitSha } : {}
8862
8904
  });
@@ -8873,13 +8915,13 @@ async function evalPrepareCmd(argv, opts = {}) {
8873
8915
  const evalT = evalTickets.find((t) => t.id === ticket);
8874
8916
  const classT = classifierTickets.find((t) => t.id === ticket);
8875
8917
  if (evalT) {
8876
- copyFileSync(join11(evalT.dir, "story.md"), join11(ticketInputs, "story.md"));
8877
- const featurePath = join11(evalT.dir, "golden/test.feature");
8918
+ copyFileSync(join12(evalT.dir, "story.md"), join12(ticketInputs, "story.md"));
8919
+ const featurePath = join12(evalT.dir, "golden/test.feature");
8878
8920
  if (existsSync12(featurePath))
8879
- copyFileSync(featurePath, join11(ticketInputs, "test.feature"));
8921
+ copyFileSync(featurePath, join12(ticketInputs, "test.feature"));
8880
8922
  }
8881
8923
  if (classT) {
8882
- copyFileSync(classT.path, join11(ticketInputs, "classifier-input.json"));
8924
+ copyFileSync(classT.path, join12(ticketInputs, "classifier-input.json"));
8883
8925
  }
8884
8926
  }
8885
8927
  const now = (opts.now ?? (() => new Date))();
@@ -9099,42 +9141,8 @@ function updateMeta(path, patch) {
9099
9141
  return next;
9100
9142
  }
9101
9143
 
9102
- // src/artifact/paths.ts
9103
- import { join as join12 } from "path";
9104
- var TICKET_RE = /^[A-Z][A-Z0-9_]*-\d+$|^SAMPLE-\d+$/;
9105
- function resolveArtifactPaths(repoRoot, ticket) {
9106
- if (!TICKET_RE.test(ticket)) {
9107
- throw new Error(`Invalid ticket key: "${ticket}" (expected e.g. JIRA-123 or SAMPLE-001)`);
9108
- }
9109
- const ticketDir = join12(repoRoot, ".xera", ticket);
9110
- return {
9111
- ticketDir,
9112
- storyPath: join12(ticketDir, "story.md"),
9113
- featurePath: join12(ticketDir, "test.feature"),
9114
- specPath: join12(ticketDir, "spec.ts"),
9115
- pageObjectsDir: join12(ticketDir, "page-objects"),
9116
- runsDir: join12(ticketDir, "runs"),
9117
- metaPath: join12(ticketDir, "meta.json"),
9118
- statusPath: join12(ticketDir, "status.json"),
9119
- logPath: join12(ticketDir, "xera.log"),
9120
- lockPath: join12(ticketDir, ".lock"),
9121
- authDir: join12(repoRoot, ".xera", ".auth"),
9122
- runPath: (runId) => {
9123
- const runDir = join12(ticketDir, "runs", runId);
9124
- return {
9125
- runDir,
9126
- reportJsonPath: join12(runDir, "report.json"),
9127
- tracePath: join12(runDir, "trace.zip"),
9128
- normalizedPath: join12(runDir, "normalized.json"),
9129
- screenshotsDir: join12(runDir, "screenshots"),
9130
- videoDir: join12(runDir, "videos")
9131
- };
9132
- }
9133
- };
9134
- }
9135
- function generateRunId2(now = new Date) {
9136
- return now.toISOString().replace(/[:.]/g, "-").replace("Z", "");
9137
- }
9144
+ // src/bin-internal/exec.ts
9145
+ init_paths2();
9138
9146
 
9139
9147
  // src/auth/refresh.ts
9140
9148
  var RE = /^(\d+)([hms])$/;
@@ -9286,7 +9294,7 @@ async function execCmd(argv) {
9286
9294
  const cwd = process.cwd();
9287
9295
  const config = await loadConfig(cwd);
9288
9296
  const paths = resolveArtifactPaths(cwd, ticket);
9289
- const runId = generateRunId2();
9297
+ const runId = generateRunId();
9290
9298
  const log = new NdjsonLogger(paths.logPath);
9291
9299
  if (!acquireLock(paths.lockPath, runId)) {
9292
9300
  if (isLockStale(paths.lockPath)) {
@@ -9413,6 +9421,9 @@ function hashFileIfExists(path) {
9413
9421
  return hashFile(path);
9414
9422
  }
9415
9423
 
9424
+ // src/bin-internal/fetch.ts
9425
+ init_paths2();
9426
+
9416
9427
  // src/jira/mcp-backend.ts
9417
9428
  import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
9418
9429
  import { tmpdir } from "os";
@@ -9543,6 +9554,19 @@ async function createJiraClient(opts) {
9543
9554
  return createRestBackend(opts.baseUrl, opts.rest);
9544
9555
  }
9545
9556
 
9557
+ // src/versions.ts
9558
+ import { createRequire } from "module";
9559
+ var _require = createRequire(import.meta.url);
9560
+ var XERA_VERSION = _require("../package.json").version;
9561
+ function loadPromptsVersion() {
9562
+ try {
9563
+ return _require("@xera-ai/prompts/version.json").prompts;
9564
+ } catch {
9565
+ return "unknown";
9566
+ }
9567
+ }
9568
+ var PROMPTS_VERSION = loadPromptsVersion();
9569
+
9546
9570
  // src/bin-internal/fetch.ts
9547
9571
  async function fetchCmd(argv, opts = {}) {
9548
9572
  const cwd = opts.cwd ?? process.cwd();
@@ -9578,8 +9602,8 @@ async function fetchCmd(argv, opts = {}) {
9578
9602
  writeMeta(paths.metaPath, {
9579
9603
  ticket,
9580
9604
  adapter: "web",
9581
- xera_version: "0.1.0",
9582
- prompts_version: "1.0.0",
9605
+ xera_version: XERA_VERSION,
9606
+ prompts_version: PROMPTS_VERSION,
9583
9607
  ...existing ?? {},
9584
9608
  story_hash: storyHash,
9585
9609
  fetched_at: new Date().toISOString()
@@ -10104,8 +10128,8 @@ import { join as join19 } from "path";
10104
10128
  import { scrubFreeText } from "@xera-ai/web";
10105
10129
 
10106
10130
  // ../../node_modules/fflate/esm/index.mjs
10107
- import { createRequire } from "module";
10108
- var require2 = createRequire("/");
10131
+ import { createRequire as createRequire2 } from "module";
10132
+ var require2 = createRequire2("/");
10109
10133
  var _a;
10110
10134
  var Worker;
10111
10135
  var isMarkedAsUntransferable;
@@ -10513,6 +10537,7 @@ function unzipSync(data, opts) {
10513
10537
  }
10514
10538
 
10515
10539
  // src/bin-internal/heal-prepare.ts
10540
+ init_paths2();
10516
10541
  var LOCATOR_LINE_RE = /^Locator:\s*(.+)$/m;
10517
10542
  function classifyKind(raw) {
10518
10543
  if (/^getByRole\b/.test(raw))
@@ -10936,6 +10961,7 @@ async function impactPrepareCmd(argv) {
10936
10961
  }
10937
10962
 
10938
10963
  // src/bin-internal/lint.ts
10964
+ init_paths2();
10939
10965
  import { lintTicket } from "@xera-ai/web";
10940
10966
  async function lintCmd(argv) {
10941
10967
  const ticket = argv[0];
@@ -10957,6 +10983,7 @@ async function lintCmd(argv) {
10957
10983
  // src/bin-internal/normalize.ts
10958
10984
  import { existsSync as existsSync22, readdirSync as readdirSync7 } from "fs";
10959
10985
  import { join as join21 } from "path";
10986
+ init_paths2();
10960
10987
  async function normalizeCmd(argv) {
10961
10988
  const ticket = argv[0];
10962
10989
  if (!ticket) {
@@ -10990,6 +11017,7 @@ async function normalizeCmd(argv) {
10990
11017
  }
10991
11018
 
10992
11019
  // src/bin-internal/post.ts
11020
+ init_paths2();
10993
11021
  import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
10994
11022
  import { join as join22 } from "path";
10995
11023
 
@@ -11099,6 +11127,7 @@ async function promoteCmd(argv) {
11099
11127
  // src/bin-internal/report.ts
11100
11128
  import { existsSync as existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
11101
11129
  import { join as join23 } from "path";
11130
+ init_paths2();
11102
11131
 
11103
11132
  // src/classifier/aggregate.ts
11104
11133
  var CLASS_PRIORITY = [
@@ -11508,8 +11537,8 @@ async function reportCmd(argv) {
11508
11537
  overall: reAggregated.overall,
11509
11538
  overallConfidence: reAggregated.overallConfidence,
11510
11539
  scenarios: reAggregated.scenarios,
11511
- xeraVersion: "0.1.0",
11512
- promptsVersion: "1.0.0"
11540
+ xeraVersion: XERA_VERSION,
11541
+ promptsVersion: PROMPTS_VERSION
11513
11542
  });
11514
11543
  const draftPath = join23(paths.ticketDir, "jira-comment.draft.md");
11515
11544
  writeFileSync14(draftPath, md);
@@ -11518,6 +11547,7 @@ async function reportCmd(argv) {
11518
11547
  }
11519
11548
 
11520
11549
  // src/bin-internal/status-cmd.ts
11550
+ init_paths2();
11521
11551
  async function statusCmd(argv) {
11522
11552
  const ticket = argv[0];
11523
11553
  if (!ticket) {
@@ -11537,6 +11567,7 @@ async function statusCmd(argv) {
11537
11567
  }
11538
11568
 
11539
11569
  // src/bin-internal/typecheck.ts
11570
+ init_paths2();
11540
11571
  import { typecheckTicket } from "@xera-ai/web";
11541
11572
  async function typecheckCmd(argv) {
11542
11573
  const ticket = argv[0];
@@ -11556,6 +11587,7 @@ async function typecheckCmd(argv) {
11556
11587
  }
11557
11588
 
11558
11589
  // src/bin-internal/unlock.ts
11590
+ init_paths2();
11559
11591
  async function unlockCmd(argv) {
11560
11592
  const ticket = argv[0];
11561
11593
  if (!ticket) {
@@ -11579,6 +11611,7 @@ async function unlockCmd(argv) {
11579
11611
  }
11580
11612
 
11581
11613
  // src/bin-internal/validate-feature.ts
11614
+ init_paths2();
11582
11615
  import { existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
11583
11616
  import { validateGherkin as validateGherkin2 } from "@xera-ai/web";
11584
11617
  async function validateFeatureCmd(argv) {