@xera-ai/core 0.9.2 → 0.9.4

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";
@@ -9546,7 +9557,15 @@ async function createJiraClient(opts) {
9546
9557
  // src/versions.ts
9547
9558
  import { createRequire } from "module";
9548
9559
  var _require = createRequire(import.meta.url);
9549
- var XERA_VERSION = _require("../package.json").version;
9560
+ function loadPackageVersion() {
9561
+ for (const rel of ["../package.json", "../../package.json"]) {
9562
+ try {
9563
+ return _require(rel).version;
9564
+ } catch {}
9565
+ }
9566
+ return "unknown";
9567
+ }
9568
+ var XERA_VERSION = loadPackageVersion();
9550
9569
  function loadPromptsVersion() {
9551
9570
  try {
9552
9571
  return _require("@xera-ai/prompts/version.json").prompts;
@@ -10526,6 +10545,7 @@ function unzipSync(data, opts) {
10526
10545
  }
10527
10546
 
10528
10547
  // src/bin-internal/heal-prepare.ts
10548
+ init_paths2();
10529
10549
  var LOCATOR_LINE_RE = /^Locator:\s*(.+)$/m;
10530
10550
  function classifyKind(raw) {
10531
10551
  if (/^getByRole\b/.test(raw))
@@ -10949,6 +10969,7 @@ async function impactPrepareCmd(argv) {
10949
10969
  }
10950
10970
 
10951
10971
  // src/bin-internal/lint.ts
10972
+ init_paths2();
10952
10973
  import { lintTicket } from "@xera-ai/web";
10953
10974
  async function lintCmd(argv) {
10954
10975
  const ticket = argv[0];
@@ -10970,6 +10991,7 @@ async function lintCmd(argv) {
10970
10991
  // src/bin-internal/normalize.ts
10971
10992
  import { existsSync as existsSync22, readdirSync as readdirSync7 } from "fs";
10972
10993
  import { join as join21 } from "path";
10994
+ init_paths2();
10973
10995
  async function normalizeCmd(argv) {
10974
10996
  const ticket = argv[0];
10975
10997
  if (!ticket) {
@@ -11003,6 +11025,7 @@ async function normalizeCmd(argv) {
11003
11025
  }
11004
11026
 
11005
11027
  // src/bin-internal/post.ts
11028
+ init_paths2();
11006
11029
  import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
11007
11030
  import { join as join22 } from "path";
11008
11031
 
@@ -11112,6 +11135,7 @@ async function promoteCmd(argv) {
11112
11135
  // src/bin-internal/report.ts
11113
11136
  import { existsSync as existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
11114
11137
  import { join as join23 } from "path";
11138
+ init_paths2();
11115
11139
 
11116
11140
  // src/classifier/aggregate.ts
11117
11141
  var CLASS_PRIORITY = [
@@ -11531,6 +11555,7 @@ async function reportCmd(argv) {
11531
11555
  }
11532
11556
 
11533
11557
  // src/bin-internal/status-cmd.ts
11558
+ init_paths2();
11534
11559
  async function statusCmd(argv) {
11535
11560
  const ticket = argv[0];
11536
11561
  if (!ticket) {
@@ -11550,6 +11575,7 @@ async function statusCmd(argv) {
11550
11575
  }
11551
11576
 
11552
11577
  // src/bin-internal/typecheck.ts
11578
+ init_paths2();
11553
11579
  import { typecheckTicket } from "@xera-ai/web";
11554
11580
  async function typecheckCmd(argv) {
11555
11581
  const ticket = argv[0];
@@ -11569,6 +11595,7 @@ async function typecheckCmd(argv) {
11569
11595
  }
11570
11596
 
11571
11597
  // src/bin-internal/unlock.ts
11598
+ init_paths2();
11572
11599
  async function unlockCmd(argv) {
11573
11600
  const ticket = argv[0];
11574
11601
  if (!ticket) {
@@ -11592,6 +11619,7 @@ async function unlockCmd(argv) {
11592
11619
  }
11593
11620
 
11594
11621
  // src/bin-internal/validate-feature.ts
11622
+ init_paths2();
11595
11623
  import { existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
11596
11624
  import { validateGherkin as validateGherkin2 } from "@xera-ai/web";
11597
11625
  async function validateFeatureCmd(argv) {