executable-stories-formatters 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { parseArgs } from "util";
5
- import * as fs8 from "fs";
6
- import * as path9 from "path";
5
+ import * as fs9 from "fs";
6
+ import * as path10 from "path";
7
7
 
8
8
  // src/validation/schema-validator.ts
9
9
  import Ajv from "ajv/dist/2020.js";
@@ -539,17 +539,17 @@ function validateRawRun(data) {
539
539
  return { valid: true, errors: [] };
540
540
  }
541
541
  const errors = (validate.errors ?? []).map((err) => {
542
- const path10 = err.instancePath || "/";
542
+ const path11 = err.instancePath || "/";
543
543
  const message = err.message ?? "unknown error";
544
544
  if (err.keyword === "additionalProperties") {
545
545
  const extra = err.params.additionalProperty;
546
- return `${path10}: ${message} \u2014 '${extra}'`;
546
+ return `${path11}: ${message} \u2014 '${extra}'`;
547
547
  }
548
548
  if (err.keyword === "enum") {
549
549
  const allowed = err.params.allowedValues;
550
- return `${path10}: ${message} \u2014 allowed: ${JSON.stringify(allowed)}`;
550
+ return `${path11}: ${message} \u2014 allowed: ${JSON.stringify(allowed)}`;
551
551
  }
552
- return `${path10}: ${message}`;
552
+ return `${path11}: ${message}`;
553
553
  });
554
554
  return { valid: false, errors };
555
555
  }
@@ -1024,7 +1024,7 @@ ${result.errors.join("\n")}`);
1024
1024
 
1025
1025
  // src/index.ts
1026
1026
  import "fs";
1027
- import * as path7 from "path";
1027
+ import * as path8 from "path";
1028
1028
  import * as fsPromises from "fs/promises";
1029
1029
 
1030
1030
  // src/converters/acl/lines.ts
@@ -1569,6 +1569,9 @@ function buildScenario(tc, featureId) {
1569
1569
  if (tickets && tickets.length > 0) {
1570
1570
  scenario.tickets = tickets.map((t) => t.url ? { id: t.id, url: t.url } : { id: t.id });
1571
1571
  }
1572
+ if (tc.story.covers && tc.story.covers.length > 0) {
1573
+ scenario.covers = [...tc.story.covers];
1574
+ }
1572
1575
  return scenario;
1573
1576
  }
1574
1577
  function deriveFeatureTitle(group, relSourceFile) {
@@ -1692,6 +1695,181 @@ var StoryReportJsonFormatter = class {
1692
1695
  }
1693
1696
  };
1694
1697
 
1698
+ // src/formatters/scenario-index-json.ts
1699
+ var ScenarioIndexJsonFormatter = class {
1700
+ options;
1701
+ constructor(options = {}) {
1702
+ this.options = {
1703
+ pretty: options.pretty ?? true,
1704
+ filters: options.filters
1705
+ };
1706
+ }
1707
+ toIndex(run) {
1708
+ return toScenarioIndex(toStoryReport(run), this.options.filters);
1709
+ }
1710
+ format(run) {
1711
+ const index = this.toIndex(run);
1712
+ return this.options.pretty ? JSON.stringify(index, null, 2) : JSON.stringify(index);
1713
+ }
1714
+ };
1715
+ function toScenarioIndex(report, filters = {}) {
1716
+ const scenarios = report.features.flatMap(
1717
+ (feature) => feature.scenarios.map((scenario) => toScenarioIndexItem(feature, scenario))
1718
+ ).filter((scenario) => matchesFilters(scenario, filters));
1719
+ return {
1720
+ schemaVersion: "1.0",
1721
+ runId: report.runId,
1722
+ generatedAtMs: report.finishedAtMs,
1723
+ summary: summarize(scenarios),
1724
+ scenarios
1725
+ };
1726
+ }
1727
+ function toScenarioIndexItem(feature, scenario) {
1728
+ return {
1729
+ id: scenario.id,
1730
+ title: scenario.title,
1731
+ status: scenario.status,
1732
+ feature: feature.title,
1733
+ sourceFile: feature.sourceFile,
1734
+ sourceLine: scenario.sourceLine,
1735
+ tags: scenario.tags,
1736
+ tickets: scenario.tickets ?? [],
1737
+ covers: scenario.covers ?? [],
1738
+ durationMs: scenario.durationMs,
1739
+ steps: scenario.steps.map((step) => ({
1740
+ id: step.id,
1741
+ index: step.index,
1742
+ keyword: step.keyword,
1743
+ text: step.text,
1744
+ status: step.status,
1745
+ durationMs: step.durationMs,
1746
+ errorMessage: step.errorMessage,
1747
+ docKinds: step.docEntries.map((entry) => entry.kind)
1748
+ })),
1749
+ docKinds: scenario.docEntries.map((entry) => entry.kind),
1750
+ error: scenario.errorMessage ? { message: scenario.errorMessage, stack: scenario.errorStack } : void 0
1751
+ };
1752
+ }
1753
+ function matchesFilters(scenario, filters) {
1754
+ if (filters.statuses?.length && !filters.statuses.includes(scenario.status)) {
1755
+ return false;
1756
+ }
1757
+ if (filters.tags?.length && !filters.tags.some((tag) => scenario.tags.includes(tag))) {
1758
+ return false;
1759
+ }
1760
+ if (filters.sourceFiles?.length && !filters.sourceFiles.some((sourceFile) => scenario.sourceFile.includes(sourceFile))) {
1761
+ return false;
1762
+ }
1763
+ return true;
1764
+ }
1765
+ function summarize(scenarios) {
1766
+ return {
1767
+ total: scenarios.length,
1768
+ passed: scenarios.filter((scenario) => scenario.status === "passed").length,
1769
+ failed: scenarios.filter((scenario) => scenario.status === "failed").length,
1770
+ skipped: scenarios.filter((scenario) => scenario.status === "skipped").length,
1771
+ pending: scenarios.filter((scenario) => scenario.status === "pending").length,
1772
+ durationMs: scenarios.reduce((total, scenario) => total + scenario.durationMs, 0)
1773
+ };
1774
+ }
1775
+
1776
+ // src/formatters/behavior-manifest-json.ts
1777
+ var BehaviorManifestJsonFormatter = class {
1778
+ pretty;
1779
+ constructor(options = {}) {
1780
+ this.pretty = options.pretty ?? true;
1781
+ }
1782
+ toManifest(run) {
1783
+ return toBehaviorManifest(toStoryReport(run));
1784
+ }
1785
+ format(run) {
1786
+ const manifest = this.toManifest(run);
1787
+ return this.pretty ? JSON.stringify(manifest, null, 2) : JSON.stringify(manifest);
1788
+ }
1789
+ };
1790
+ function toBehaviorManifest(report) {
1791
+ const index = toScenarioIndex(report);
1792
+ const bySource = /* @__PURE__ */ new Map();
1793
+ const byTag = /* @__PURE__ */ new Map();
1794
+ const docKinds = /* @__PURE__ */ new Set();
1795
+ const debuggerIssues = [];
1796
+ for (const scenario of index.scenarios) {
1797
+ const source = bySource.get(scenario.sourceFile) ?? {
1798
+ path: scenario.sourceFile,
1799
+ scenarioCount: 0,
1800
+ failed: 0,
1801
+ tags: []
1802
+ };
1803
+ source.scenarioCount += 1;
1804
+ if (scenario.status === "failed") source.failed += 1;
1805
+ source.tags = [.../* @__PURE__ */ new Set([...source.tags, ...scenario.tags])].sort();
1806
+ bySource.set(scenario.sourceFile, source);
1807
+ for (const tag of scenario.tags) {
1808
+ const tagEntry = byTag.get(tag) ?? { name: tag, scenarioCount: 0 };
1809
+ tagEntry.scenarioCount += 1;
1810
+ byTag.set(tag, tagEntry);
1811
+ }
1812
+ for (const kind of scenario.docKinds) docKinds.add(kind);
1813
+ for (const step of scenario.steps) {
1814
+ for (const kind of step.docKinds) docKinds.add(kind);
1815
+ }
1816
+ if (!scenarioHasDocs(scenario)) {
1817
+ debuggerIssues.push({
1818
+ severity: "warning",
1819
+ code: "missing-docs",
1820
+ scenarioId: scenario.id,
1821
+ title: scenario.title,
1822
+ message: "Scenario has no doc entries."
1823
+ });
1824
+ }
1825
+ if (scenario.tags.length === 0) {
1826
+ debuggerIssues.push({
1827
+ severity: "warning",
1828
+ code: "missing-tags",
1829
+ scenarioId: scenario.id,
1830
+ title: scenario.title,
1831
+ message: "Scenario has no tags."
1832
+ });
1833
+ }
1834
+ if (scenario.covers.length === 0) {
1835
+ debuggerIssues.push({
1836
+ severity: "warning",
1837
+ code: "missing-covers",
1838
+ scenarioId: scenario.id,
1839
+ title: scenario.title,
1840
+ message: "Scenario declares no covers (product-code paths), so code\u2192scenario lookup cannot find it."
1841
+ });
1842
+ }
1843
+ if (scenario.sourceLine === void 0) {
1844
+ debuggerIssues.push({
1845
+ severity: "warning",
1846
+ code: "missing-source-line",
1847
+ scenarioId: scenario.id,
1848
+ title: scenario.title,
1849
+ message: "Scenario has no source line."
1850
+ });
1851
+ }
1852
+ }
1853
+ const scenariosWithDocs = index.scenarios.filter(scenarioHasDocs).length;
1854
+ return {
1855
+ schemaVersion: "1.0",
1856
+ runId: report.runId,
1857
+ generatedAtMs: report.finishedAtMs,
1858
+ summary: index.summary,
1859
+ sourceFiles: [...bySource.values()].sort((a, b) => a.path.localeCompare(b.path)),
1860
+ tags: [...byTag.values()].sort((a, b) => a.name.localeCompare(b.name)),
1861
+ docCoverage: {
1862
+ scenariosWithDocs,
1863
+ scenariosWithoutDocs: index.scenarios.length - scenariosWithDocs,
1864
+ docKinds: [...docKinds].sort()
1865
+ },
1866
+ debugger: debuggerIssues
1867
+ };
1868
+ }
1869
+ function scenarioHasDocs(scenario) {
1870
+ return scenario.docKinds.length > 0 || scenario.steps.some((step) => step.docKinds.length > 0);
1871
+ }
1872
+
1695
1873
  // src/formatters/html/renderers/index.ts
1696
1874
  import * as fs2 from "fs";
1697
1875
  import * as path3 from "path";
@@ -16526,8 +16704,8 @@ function extractDocAttachments(step) {
16526
16704
  }
16527
16705
  return attachments;
16528
16706
  }
16529
- function guessMediaType(path10) {
16530
- const lower = path10.toLowerCase();
16707
+ function guessMediaType(path11) {
16708
+ const lower = path11.toLowerCase();
16531
16709
  if (lower.endsWith(".png")) return "image/png";
16532
16710
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
16533
16711
  if (lower.endsWith(".gif")) return "image/gif";
@@ -16668,11 +16846,11 @@ var CucumberHtmlFormatter = class {
16668
16846
  for (const envelope of envelopes) {
16669
16847
  const accepted = htmlStream.write(envelope);
16670
16848
  if (!accepted) {
16671
- await new Promise((resolve8) => htmlStream.once("drain", resolve8));
16849
+ await new Promise((resolve9) => htmlStream.once("drain", resolve9));
16672
16850
  }
16673
16851
  }
16674
- await new Promise((resolve8, reject) => {
16675
- collector.on("finish", resolve8);
16852
+ await new Promise((resolve9, reject) => {
16853
+ collector.on("finish", resolve9);
16676
16854
  collector.on("error", reject);
16677
16855
  htmlStream.end();
16678
16856
  });
@@ -18283,6 +18461,68 @@ function copyMarkdownAssets(options) {
18283
18461
  };
18284
18462
  }
18285
18463
 
18464
+ // src/watch.ts
18465
+ import * as fs6 from "fs";
18466
+ import * as path7 from "path";
18467
+ function toRun(data, inputType, synthesize) {
18468
+ if (inputType === "canonical") return data;
18469
+ let raw = data;
18470
+ if (synthesize) raw = synthesizeStories(raw);
18471
+ return canonicalizeRun(raw);
18472
+ }
18473
+ async function regenerateArtifacts(options, deps = {}) {
18474
+ const read = deps.readFile ?? ((filePath) => fs6.readFileSync(filePath, "utf8"));
18475
+ const data = JSON.parse(read(path7.resolve(options.input)));
18476
+ const run = toRun(data, options.inputType ?? "raw", options.synthesize !== false);
18477
+ const generator = new ReportGenerator({
18478
+ formats: options.formats,
18479
+ outputDir: options.outputDir,
18480
+ outputName: options.outputName
18481
+ });
18482
+ const result = await generator.generate(run);
18483
+ return [...result.values()].flat();
18484
+ }
18485
+ function startWatch(options, deps = {}) {
18486
+ const log = deps.log ?? ((message) => console.log(message));
18487
+ const regenerate = deps.regenerate ?? ((input) => regenerateArtifacts({ ...options, input }, deps));
18488
+ const watchFn = deps.watch ?? ((filePath, listener) => fs6.watch(filePath, listener));
18489
+ const debounceMs = options.debounceMs ?? 150;
18490
+ let timer;
18491
+ let running = false;
18492
+ let pending = false;
18493
+ const run = async () => {
18494
+ if (running) {
18495
+ pending = true;
18496
+ return;
18497
+ }
18498
+ running = true;
18499
+ try {
18500
+ const files = await regenerate(options.input);
18501
+ log(`Regenerated ${files.length} artifact file(s) from ${options.input}`);
18502
+ } catch (error) {
18503
+ log(`Watch regeneration failed: ${error.message}`);
18504
+ } finally {
18505
+ running = false;
18506
+ if (pending) {
18507
+ pending = false;
18508
+ trigger();
18509
+ }
18510
+ }
18511
+ };
18512
+ const trigger = () => {
18513
+ if (timer) clearTimeout(timer);
18514
+ timer = setTimeout(() => void run(), debounceMs);
18515
+ };
18516
+ trigger();
18517
+ const watcher = watchFn(path7.resolve(options.input), trigger);
18518
+ return {
18519
+ close: () => {
18520
+ if (timer) clearTimeout(timer);
18521
+ watcher.close();
18522
+ }
18523
+ };
18524
+ }
18525
+
18286
18526
  // src/publishers/confluence.ts
18287
18527
  function parseAdf(adf) {
18288
18528
  let parsed;
@@ -19451,12 +19691,22 @@ function listScenarios(args, _deps) {
19451
19691
  const { testCases, format } = args;
19452
19692
  if (format === "json") {
19453
19693
  const items = testCases.map((tc) => ({
19694
+ id: tc.id,
19454
19695
  scenario: tc.story.scenario,
19455
19696
  status: tc.status,
19456
19697
  sourceFile: tc.sourceFile,
19457
19698
  sourceLine: tc.sourceLine,
19699
+ suitePath: tc.story.suitePath ?? tc.titlePath.slice(0, -1),
19458
19700
  tags: tc.tags,
19459
- id: tc.id
19701
+ tickets: tc.story.tickets ?? [],
19702
+ covers: tc.story.covers ?? [],
19703
+ durationMs: tc.durationMs,
19704
+ error: tc.errorMessage ? {
19705
+ message: tc.errorMessage,
19706
+ stack: tc.errorStack
19707
+ } : void 0,
19708
+ steps: tc.story.steps.map((step, index) => toScenarioStep(step, index, tc)),
19709
+ docKinds: collectDocKinds(tc)
19460
19710
  }));
19461
19711
  return JSON.stringify(items, null, 2);
19462
19712
  }
@@ -19529,6 +19779,34 @@ function listScenarios(args, _deps) {
19529
19779
  ];
19530
19780
  return lines.join("\n");
19531
19781
  }
19782
+ function toScenarioStep(step, index, testCase) {
19783
+ const result = testCase.stepResults.find(
19784
+ (candidate) => candidate.index === index || candidate.stepId === step.id
19785
+ );
19786
+ return {
19787
+ id: step.id,
19788
+ index,
19789
+ keyword: step.keyword,
19790
+ text: step.text,
19791
+ status: result?.status ?? testCase.status,
19792
+ durationMs: result?.durationMs ?? step.durationMs ?? 0,
19793
+ errorMessage: result?.errorMessage,
19794
+ mode: step.mode,
19795
+ docKinds: (step.docs ?? []).map((doc) => doc.kind)
19796
+ };
19797
+ }
19798
+ function collectDocKinds(testCase) {
19799
+ const kinds = /* @__PURE__ */ new Set();
19800
+ for (const doc of testCase.story.docs ?? []) {
19801
+ kinds.add(doc.kind);
19802
+ }
19803
+ for (const step of testCase.story.steps) {
19804
+ for (const doc of step.docs ?? []) {
19805
+ kinds.add(doc.kind);
19806
+ }
19807
+ }
19808
+ return [...kinds].sort();
19809
+ }
19532
19810
 
19533
19811
  // src/review/conventions.ts
19534
19812
  var CHANGE_TAG_PREFIX = "change:";
@@ -19576,18 +19854,18 @@ function deriveChangeType(tags) {
19576
19854
  }
19577
19855
  return "unknown";
19578
19856
  }
19579
- function extensionOf(path10) {
19580
- const base = path10.split("/").pop() ?? path10;
19857
+ function extensionOf(path11) {
19858
+ const base = path11.split("/").pop() ?? path11;
19581
19859
  const dot = base.lastIndexOf(".");
19582
19860
  return dot === -1 ? "" : base.slice(dot + 1).toLowerCase();
19583
19861
  }
19584
- function isTestFile(path10) {
19585
- return TEST_INFIX.test(path10);
19862
+ function isTestFile(path11) {
19863
+ return TEST_INFIX.test(path11);
19586
19864
  }
19587
- function isReviewableSource(path10) {
19588
- if (isTestFile(path10)) return false;
19589
- if (path10.endsWith(".d.ts")) return false;
19590
- return CODE_EXTENSIONS.has(extensionOf(path10));
19865
+ function isReviewableSource(path11) {
19866
+ if (isTestFile(path11)) return false;
19867
+ if (path11.endsWith(".d.ts")) return false;
19868
+ return CODE_EXTENSIONS.has(extensionOf(path11));
19591
19869
  }
19592
19870
  function testBaseKey(testFile) {
19593
19871
  return testFile.replace(TEST_INFIX, "");
@@ -19691,7 +19969,7 @@ function toClaim(testCase, changedSourcePaths) {
19691
19969
  const { strength, reasons } = gradeEvidence(testCase, audience);
19692
19970
  const key = testBaseKey(testCase.sourceFile);
19693
19971
  const coversFiles = changedSourcePaths.filter(
19694
- (path10) => sourceBaseKey(path10) === key
19972
+ (path11) => sourceBaseKey(path11) === key
19695
19973
  );
19696
19974
  return {
19697
19975
  id: testCase.id,
@@ -20224,6 +20502,7 @@ applyTheme(getEffectiveTheme());` : "";
20224
20502
  // src/index.ts
20225
20503
  var FORMAT_EXTENSIONS = {
20226
20504
  astro: ".md",
20505
+ "behavior-manifest-json": ".behavior-manifest.json",
20227
20506
  markdown: ".md",
20228
20507
  html: ".html",
20229
20508
  "cucumber-html": ".cucumber.html",
@@ -20231,6 +20510,7 @@ var FORMAT_EXTENSIONS = {
20231
20510
  "cucumber-json": ".cucumber.json",
20232
20511
  "cucumber-messages": ".ndjson",
20233
20512
  confluence: ".adf.json",
20513
+ "scenario-index-json": ".scenarios-index.json",
20234
20514
  "story-report-json": ".story-report.json"
20235
20515
  };
20236
20516
  var TEST_EXTENSIONS = [
@@ -20258,11 +20538,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
20258
20538
  const ext = FORMAT_EXTENSIONS[format];
20259
20539
  const effectiveName = outputName + (outputNameSuffix ?? "");
20260
20540
  if (mode === "aggregated") {
20261
- return toPosix(path7.join(baseOutputDir, `${effectiveName}${ext}`));
20541
+ return toPosix(path8.join(baseOutputDir, `${effectiveName}${ext}`));
20262
20542
  }
20263
20543
  const normalizedSource = toPosix(sourceFile);
20264
- const dirOfSource = path7.posix.dirname(normalizedSource);
20265
- let baseName = path7.posix.basename(normalizedSource);
20544
+ const dirOfSource = path8.posix.dirname(normalizedSource);
20545
+ let baseName = path8.posix.basename(normalizedSource);
20266
20546
  for (const testExt of TEST_EXTENSIONS) {
20267
20547
  if (baseName.endsWith(testExt)) {
20268
20548
  baseName = baseName.slice(0, -testExt.length);
@@ -20271,9 +20551,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
20271
20551
  }
20272
20552
  const fileName = `${baseName}.${effectiveName}${ext}`;
20273
20553
  if (colocatedStyle === "adjacent") {
20274
- return toPosix(path7.posix.join(dirOfSource, fileName));
20554
+ return toPosix(path8.posix.join(dirOfSource, fileName));
20275
20555
  }
20276
- return toPosix(path7.posix.join(baseOutputDir, dirOfSource, fileName));
20556
+ return toPosix(path8.posix.join(baseOutputDir, dirOfSource, fileName));
20277
20557
  }
20278
20558
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
20279
20559
  const groups = /* @__PURE__ */ new Map();
@@ -20359,6 +20639,12 @@ var ReportGenerator = class {
20359
20639
  storyReportJson: {
20360
20640
  pretty: options.storyReportJson?.pretty ?? true
20361
20641
  },
20642
+ scenarioIndexJson: {
20643
+ pretty: options.scenarioIndexJson?.pretty ?? true
20644
+ },
20645
+ behaviorManifestJson: {
20646
+ pretty: options.behaviorManifestJson?.pretty ?? true
20647
+ },
20362
20648
  cucumberMessages: {
20363
20649
  uriStrategy: options.cucumberMessages?.uriStrategy ?? "sourceFile",
20364
20650
  includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,
@@ -20474,8 +20760,8 @@ var ReportGenerator = class {
20474
20760
  if (astroPaths) {
20475
20761
  for (const mdPath of astroPaths) {
20476
20762
  const content = await fsPromises.readFile(mdPath, "utf8");
20477
- const mdDir = path7.dirname(mdPath);
20478
- const assetsDir = path7.resolve(this.options.astro.assetsDir);
20763
+ const mdDir = path8.dirname(mdPath);
20764
+ const assetsDir = path8.resolve(this.options.astro.assetsDir);
20479
20765
  const result = copyMarkdownAssets({
20480
20766
  markdown: content,
20481
20767
  markdownDir: mdDir,
@@ -20506,9 +20792,9 @@ var ReportGenerator = class {
20506
20792
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
20507
20793
  const ext = FORMAT_EXTENSIONS[format];
20508
20794
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
20509
- const outputPath = toPosix(path7.join(this.options.outputDir, `${effectiveName}${ext}`));
20795
+ const outputPath = toPosix(path8.join(this.options.outputDir, `${effectiveName}${ext}`));
20510
20796
  const content = await this.formatContent(run, format);
20511
- const dir = path7.dirname(outputPath);
20797
+ const dir = path8.dirname(outputPath);
20512
20798
  await fsPromises.mkdir(dir, { recursive: true });
20513
20799
  await this.deps.writeFile(outputPath, content);
20514
20800
  return [outputPath];
@@ -20520,7 +20806,7 @@ var ReportGenerator = class {
20520
20806
  testCases
20521
20807
  };
20522
20808
  const content = await this.formatContent(groupRun, format);
20523
- const dir = path7.dirname(outputPath);
20809
+ const dir = path8.dirname(outputPath);
20524
20810
  await fsPromises.mkdir(dir, { recursive: true });
20525
20811
  await this.deps.writeFile(outputPath, content);
20526
20812
  writtenPaths.push(outputPath);
@@ -20633,6 +20919,18 @@ var ReportGenerator = class {
20633
20919
  });
20634
20920
  return formatter.format(run);
20635
20921
  }
20922
+ case "scenario-index-json": {
20923
+ const formatter = new ScenarioIndexJsonFormatter({
20924
+ pretty: this.options.scenarioIndexJson.pretty
20925
+ });
20926
+ return formatter.format(run);
20927
+ }
20928
+ case "behavior-manifest-json": {
20929
+ const formatter = new BehaviorManifestJsonFormatter({
20930
+ pretty: this.options.behaviorManifestJson.pretty
20931
+ });
20932
+ return formatter.format(run);
20933
+ }
20636
20934
  default:
20637
20935
  throw new Error(`Unknown format: ${format}`);
20638
20936
  }
@@ -20646,7 +20944,7 @@ async function generateRunComparison(args) {
20646
20944
  await fsPromises.mkdir(outputDir, { recursive: true });
20647
20945
  for (const format of args.formats) {
20648
20946
  const ext = format === "html" ? ".html" : ".md";
20649
- const outputPath = toPosix(path7.join(outputDir, `${outputName}${ext}`));
20947
+ const outputPath = toPosix(path8.join(outputDir, `${outputName}${ext}`));
20650
20948
  const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
20651
20949
  await fsPromises.writeFile(outputPath, content, "utf8");
20652
20950
  files.push(outputPath);
@@ -20655,23 +20953,23 @@ async function generateRunComparison(args) {
20655
20953
  }
20656
20954
 
20657
20955
  // src/init-astro.ts
20658
- import * as fs7 from "fs";
20659
- import * as path8 from "path";
20956
+ import * as fs8 from "fs";
20957
+ import * as path9 from "path";
20660
20958
  import { fileURLToPath } from "url";
20661
- var __dirname = path8.dirname(fileURLToPath(import.meta.url));
20959
+ var __dirname = path9.dirname(fileURLToPath(import.meta.url));
20662
20960
  function initAstro(options = {}) {
20663
20961
  const targetDir = options.targetDir ?? "./story-docs";
20664
20962
  const force = options.force ?? false;
20665
- if (fs7.existsSync(targetDir)) {
20666
- const entries = fs7.readdirSync(targetDir);
20963
+ if (fs8.existsSync(targetDir)) {
20964
+ const entries = fs8.readdirSync(targetDir);
20667
20965
  if (entries.length > 0 && !force) {
20668
20966
  throw new Error(
20669
20967
  `Directory "${targetDir}" already exists and is not empty. Use --force to overwrite.`
20670
20968
  );
20671
20969
  }
20672
20970
  }
20673
- const templateDir = path8.resolve(__dirname, "..", "templates", "astro-starlight");
20674
- if (!fs7.existsSync(templateDir)) {
20971
+ const templateDir = path9.resolve(__dirname, "..", "templates", "astro-starlight");
20972
+ if (!fs8.existsSync(templateDir)) {
20675
20973
  throw new Error(
20676
20974
  `Template directory not found at ${templateDir}. Ensure the package is installed correctly.`
20677
20975
  );
@@ -20680,24 +20978,24 @@ function initAstro(options = {}) {
20680
20978
  return { targetDir };
20681
20979
  }
20682
20980
  function copyDirRecursive(src, dest) {
20683
- fs7.mkdirSync(dest, { recursive: true });
20684
- const entries = fs7.readdirSync(src, { withFileTypes: true });
20981
+ fs8.mkdirSync(dest, { recursive: true });
20982
+ const entries = fs8.readdirSync(src, { withFileTypes: true });
20685
20983
  for (const entry of entries) {
20686
- const srcPath = path8.join(src, entry.name);
20687
- const destPath = path8.join(dest, entry.name);
20984
+ const srcPath = path9.join(src, entry.name);
20985
+ const destPath = path9.join(dest, entry.name);
20688
20986
  if (entry.isDirectory()) {
20689
20987
  copyDirRecursive(srcPath, destPath);
20690
20988
  } else {
20691
- fs7.copyFileSync(srcPath, destPath);
20989
+ fs8.copyFileSync(srcPath, destPath);
20692
20990
  }
20693
20991
  }
20694
20992
  }
20695
20993
 
20696
20994
  // src/config.ts
20697
20995
  import { existsSync as existsSync7 } from "fs";
20698
- import { resolve as resolve6 } from "path";
20996
+ import { resolve as resolve7 } from "path";
20699
20997
  async function loadConfig(configPath) {
20700
- const resolved = configPath ? resolve6(configPath) : resolve6(process.cwd(), "executable-stories.config.js");
20998
+ const resolved = configPath ? resolve7(configPath) : resolve7(process.cwd(), "executable-stories.config.js");
20701
20999
  if (!existsSync7(resolved)) return {};
20702
21000
  const mod = await import(resolved);
20703
21001
  const config = mod.default;
@@ -20734,6 +21032,7 @@ USAGE
20734
21032
 
20735
21033
  SUBCOMMANDS
20736
21034
  format Read raw test results and generate reports
21035
+ watch Regenerate reports whenever the raw-run file changes (live agent index)
20737
21036
  compare Compare two runs and generate a diff report
20738
21037
  review Generate an Evidence Review of AI-authored changes (correlate a run to the diff)
20739
21038
  list List scenarios from a test run (text table or JSON)
@@ -20743,9 +21042,10 @@ SUBCOMMANDS
20743
21042
  publish-jira Publish an ADF JSON file to a Jira issue (as comment or description)
20744
21043
 
20745
21044
  OPTIONS
20746
- --format <formats> Comma-separated formats: html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html, astro, confluence, story-report-json, or custom names from config (default: html)
21045
+ --format <formats> Comma-separated formats: html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html, astro, confluence, story-report-json, scenario-index-json, behavior-manifest-json, or custom names from config (default: html)
20747
21046
  astro Themed Markdown (for Astro docs sites with matching CSS)
20748
21047
  confluence Atlassian Document Format (ADF) JSON for Confluence / Jira
21048
+ behavior-manifest-json Agent-readable behavior manifest and debugger warnings
20749
21049
  html Custom HTML report (accessible, dark mode, mermaid)
20750
21050
  cucumber-html Official Cucumber HTML report
20751
21051
  markdown Markdown documentation
@@ -20753,6 +21053,7 @@ OPTIONS
20753
21053
  cucumber-json Cucumber JSON
20754
21054
  cucumber-messages Raw NDJSON (Cucumber Messages)
20755
21055
  story-report-json StoryReport v1 JSON (consumed by executable-stories-react and other UI renderers)
21056
+ scenario-index-json Storybook-like scenario index for agents and explorers
20756
21057
  --config <path> Path to executable-stories.config.js (default: ./executable-stories.config.js)
20757
21058
  --input-type <type> Input type: raw, canonical, or ndjson (default: raw)
20758
21059
  --output-dir <dir> Output directory (default: reports)
@@ -20863,9 +21164,9 @@ async function parseCliArgs(argv) {
20863
21164
  process.exit(EXIT_SUCCESS);
20864
21165
  }
20865
21166
  const subcommand = args[0];
20866
- if (subcommand !== "format" && subcommand !== "compare" && subcommand !== "review" && subcommand !== "list" && subcommand !== "validate" && subcommand !== "init-astro" && subcommand !== "publish-confluence" && subcommand !== "publish-jira") {
21167
+ if (subcommand !== "format" && subcommand !== "watch" && subcommand !== "compare" && subcommand !== "review" && subcommand !== "list" && subcommand !== "validate" && subcommand !== "init-astro" && subcommand !== "publish-confluence" && subcommand !== "publish-jira") {
20867
21168
  console.error(
20868
- `Unknown subcommand: "${subcommand}". Use "format", "compare", "review", "list", "validate", "init-astro", "publish-confluence", or "publish-jira".`
21169
+ `Unknown subcommand: "${subcommand}". Use "format", "watch", "compare", "review", "list", "validate", "init-astro", "publish-confluence", or "publish-jira".`
20869
21170
  );
20870
21171
  process.exit(EXIT_USAGE);
20871
21172
  }
@@ -20903,6 +21204,9 @@ async function parseCliArgs(argv) {
20903
21204
  console.log("");
20904
21205
  console.log("Generate story docs with:");
20905
21206
  console.log(` executable-stories format run.json --format astro --output-dir ${result.targetDir}/src/content/docs/stories --asset-mode copy`);
21207
+ console.log("");
21208
+ console.log("Generate the Storybook-like scenario explorer data with:");
21209
+ console.log(` executable-stories format run.json --format story-report-json --output-dir ${result.targetDir}/public/stories --output-name story-report`);
20906
21210
  process.exit(EXIT_SUCCESS);
20907
21211
  } catch (err) {
20908
21212
  console.error(`Error: ${err.message}`);
@@ -21004,7 +21308,7 @@ async function parseCliArgs(argv) {
21004
21308
  }
21005
21309
  const pluginConfig = await loadConfig(values["config"]);
21006
21310
  const customFormatterNames = new Set(Object.keys(pluginConfig.formatters ?? {}));
21007
- const builtInFormats = /* @__PURE__ */ new Set(["astro", "confluence", "html", "markdown", "junit", "cucumber-json", "cucumber-messages", "cucumber-html", "story-report-json"]);
21311
+ const builtInFormats = /* @__PURE__ */ new Set(["astro", "behavior-manifest-json", "confluence", "html", "markdown", "junit", "cucumber-json", "cucumber-messages", "cucumber-html", "scenario-index-json", "story-report-json"]);
21008
21312
  const formatStr = values.format;
21009
21313
  const allRequestedFormats = formatStr.split(",").map((f) => f.trim());
21010
21314
  const builtInRequested = allRequestedFormats.filter((f) => builtInFormats.has(f));
@@ -21012,7 +21316,7 @@ async function parseCliArgs(argv) {
21012
21316
  const unknownFormats = allRequestedFormats.filter((f) => !builtInFormats.has(f) && !customFormatterNames.has(f));
21013
21317
  if (unknownFormats.length > 0) {
21014
21318
  const knownCustom = customFormatterNames.size > 0 ? `, ${[...customFormatterNames].join(", ")}` : "";
21015
- console.error(`Error: Unknown format(s): ${unknownFormats.join(", ")}. Valid built-in: astro, confluence, html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html, story-report-json${knownCustom}.`);
21319
+ console.error(`Error: Unknown format(s): ${unknownFormats.join(", ")}. Valid built-in: astro, behavior-manifest-json, confluence, html, markdown, junit, cucumber-json, cucumber-messages, cucumber-html, scenario-index-json, story-report-json${knownCustom}.`);
21016
21320
  process.exit(EXIT_USAGE);
21017
21321
  }
21018
21322
  const formats = builtInRequested;
@@ -21164,27 +21468,27 @@ async function readInput(args) {
21164
21468
  if (args.stdin) {
21165
21469
  return readStdin();
21166
21470
  }
21167
- const filePath = path9.resolve(args.inputFile);
21168
- if (!fs8.existsSync(filePath)) {
21471
+ const filePath = path10.resolve(args.inputFile);
21472
+ if (!fs9.existsSync(filePath)) {
21169
21473
  console.error(`Error: File not found: ${filePath}`);
21170
21474
  process.exit(EXIT_USAGE);
21171
21475
  }
21172
- return fs8.readFileSync(filePath, "utf8");
21476
+ return fs9.readFileSync(filePath, "utf8");
21173
21477
  }
21174
21478
  function readFileInput(filePath) {
21175
- const resolved = path9.resolve(filePath);
21176
- if (!fs8.existsSync(resolved)) {
21479
+ const resolved = path10.resolve(filePath);
21480
+ if (!fs9.existsSync(resolved)) {
21177
21481
  console.error(`Error: File not found: ${resolved}`);
21178
21482
  process.exit(EXIT_USAGE);
21179
21483
  }
21180
- return fs8.readFileSync(resolved, "utf8");
21484
+ return fs9.readFileSync(resolved, "utf8");
21181
21485
  }
21182
21486
  function readStdin() {
21183
- return new Promise((resolve8, reject) => {
21487
+ return new Promise((resolve9, reject) => {
21184
21488
  const chunks = [];
21185
21489
  process.stdin.setEncoding("utf8");
21186
21490
  process.stdin.on("data", (chunk) => chunks.push(chunk));
21187
- process.stdin.on("end", () => resolve8(chunks.join("")));
21491
+ process.stdin.on("end", () => resolve9(chunks.join("")));
21188
21492
  process.stdin.on("error", reject);
21189
21493
  });
21190
21494
  }
@@ -21310,14 +21614,14 @@ function tryNormalizeRunFromText(text2, args) {
21310
21614
  }
21311
21615
  }
21312
21616
  function listBaselineCandidates(currentFile, args) {
21313
- const baselineDir = path9.resolve(args.baselineDir ?? path9.dirname(currentFile));
21314
- const currentResolved = path9.resolve(currentFile);
21315
- if (!fs8.existsSync(baselineDir)) {
21617
+ const baselineDir = path10.resolve(args.baselineDir ?? path10.dirname(currentFile));
21618
+ const currentResolved = path10.resolve(currentFile);
21619
+ if (!fs9.existsSync(baselineDir)) {
21316
21620
  console.error(`Error: baseline directory not found: ${baselineDir}`);
21317
21621
  process.exit(EXIT_USAGE);
21318
21622
  }
21319
- const entries = fs8.readdirSync(baselineDir, { withFileTypes: true });
21320
- return entries.filter((entry) => entry.isFile()).map((entry) => path9.join(baselineDir, entry.name)).filter((candidate) => path9.resolve(candidate) !== currentResolved).filter(
21623
+ const entries = fs9.readdirSync(baselineDir, { withFileTypes: true });
21624
+ return entries.filter((entry) => entry.isFile()).map((entry) => path10.join(baselineDir, entry.name)).filter((candidate) => path10.resolve(candidate) !== currentResolved).filter(
21321
21625
  (candidate) => args.inputType === "ndjson" ? candidate.endsWith(".ndjson") : candidate.endsWith(".json")
21322
21626
  );
21323
21627
  }
@@ -21325,14 +21629,14 @@ function resolveBaselineAuto(currentFile, currentRun, args) {
21325
21629
  const candidates = listBaselineCandidates(currentFile, args);
21326
21630
  const comparable = [];
21327
21631
  for (const candidate of candidates) {
21328
- const run = tryNormalizeRunFromText(fs8.readFileSync(candidate, "utf8"), args);
21632
+ const run = tryNormalizeRunFromText(fs9.readFileSync(candidate, "utf8"), args);
21329
21633
  if (run) {
21330
21634
  comparable.push({ file: candidate, run });
21331
21635
  }
21332
21636
  }
21333
21637
  if (comparable.length === 0) {
21334
21638
  console.error(
21335
- `Error: no compatible baseline files found in ${path9.resolve(args.baselineDir ?? path9.dirname(currentFile))}.`
21639
+ `Error: no compatible baseline files found in ${path10.resolve(args.baselineDir ?? path10.dirname(currentFile))}.`
21336
21640
  );
21337
21641
  process.exit(EXIT_USAGE);
21338
21642
  }
@@ -21409,6 +21713,24 @@ async function main() {
21409
21713
  console.log(output);
21410
21714
  process.exit(EXIT_SUCCESS);
21411
21715
  }
21716
+ if (args.subcommand === "watch") {
21717
+ if (!args.inputFile) {
21718
+ console.error("Error: watch requires an input file (the raw-run JSON the framework writes).");
21719
+ process.exit(EXIT_USAGE);
21720
+ }
21721
+ console.log(
21722
+ `Watching ${args.inputFile} \u2192 regenerating [${args.formats.join(", ")}] into ${args.outputDir}/ (Ctrl+C to stop)`
21723
+ );
21724
+ startWatch({
21725
+ input: args.inputFile,
21726
+ outputDir: args.outputDir,
21727
+ outputName: args.outputName,
21728
+ formats: args.formats,
21729
+ inputType: args.inputType === "canonical" ? "canonical" : "raw",
21730
+ synthesize: args.synthesizeStories
21731
+ });
21732
+ return;
21733
+ }
21412
21734
  const text2 = await readInput(args);
21413
21735
  if (args.inputType === "ndjson") {
21414
21736
  if (args.subcommand === "validate") {
@@ -21452,9 +21774,9 @@ async function main() {
21452
21774
  process.exit(EXIT_SCHEMA_VALIDATION);
21453
21775
  }
21454
21776
  if (args.emitCanonical) {
21455
- const outPath = path9.resolve(args.emitCanonical);
21456
- fs8.mkdirSync(path9.dirname(outPath), { recursive: true });
21457
- fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
21777
+ const outPath = path10.resolve(args.emitCanonical);
21778
+ fs9.mkdirSync(path10.dirname(outPath), { recursive: true });
21779
+ fs9.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
21458
21780
  }
21459
21781
  try {
21460
21782
  const result = await generateReports(run, args);
@@ -21511,9 +21833,9 @@ ${msg}`);
21511
21833
  }
21512
21834
  const run = data;
21513
21835
  if (args.emitCanonical) {
21514
- const outPath = path9.resolve(args.emitCanonical);
21515
- fs8.mkdirSync(path9.dirname(outPath), { recursive: true });
21516
- fs8.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
21836
+ const outPath = path10.resolve(args.emitCanonical);
21837
+ fs9.mkdirSync(path10.dirname(outPath), { recursive: true });
21838
+ fs9.writeFileSync(outPath, JSON.stringify(run, null, 2), "utf8");
21517
21839
  }
21518
21840
  try {
21519
21841
  const result = await generateReports(run, args);
@@ -21569,9 +21891,9 @@ ${msg}`);
21569
21891
  process.exit(EXIT_CANONICAL_VALIDATION);
21570
21892
  }
21571
21893
  if (args.emitCanonical) {
21572
- const outPath = path9.resolve(args.emitCanonical);
21573
- fs8.mkdirSync(path9.dirname(outPath), { recursive: true });
21574
- fs8.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
21894
+ const outPath = path10.resolve(args.emitCanonical);
21895
+ fs9.mkdirSync(path10.dirname(outPath), { recursive: true });
21896
+ fs9.writeFileSync(outPath, JSON.stringify(canonical, null, 2), "utf8");
21575
21897
  }
21576
21898
  try {
21577
21899
  const result = await generateReports(canonical, args, droppedMissingStory);
@@ -21596,9 +21918,9 @@ function runCustomFormatters(run, customRequested, formatters, args) {
21596
21918
  const ext = formatter.fileExtension ?? formatName;
21597
21919
  const baseName = args.outputName ?? "report";
21598
21920
  const filename = args.outputNameTimestamp ? `${baseName}-${Math.floor(run.startedAtMs / 1e3)}.${ext}` : `${baseName}.${ext}`;
21599
- const filepath = path9.join(outputDir, filename);
21600
- fs8.mkdirSync(outputDir, { recursive: true });
21601
- fs8.writeFileSync(filepath, content, "utf8");
21921
+ const filepath = path10.join(outputDir, filename);
21922
+ fs9.mkdirSync(outputDir, { recursive: true });
21923
+ fs9.writeFileSync(filepath, content, "utf8");
21602
21924
  console.log(`Generated: ${filepath}`);
21603
21925
  } catch (err) {
21604
21926
  console.error(`Error running custom formatter "${formatName}": ${err instanceof Error ? err.message : String(err)}`);
@@ -21648,13 +21970,13 @@ async function dispatchNotifications(run, args) {
21648
21970
  }
21649
21971
  function runHistoryPipeline(run, args) {
21650
21972
  if (!args.historyFile) return;
21651
- const historyPath = path9.resolve(args.historyFile);
21973
+ const historyPath = path10.resolve(args.historyFile);
21652
21974
  const store = loadHistory(
21653
21975
  { filePath: historyPath },
21654
21976
  {
21655
21977
  readFile: (p) => {
21656
21978
  try {
21657
- return fs8.readFileSync(p, "utf8");
21979
+ return fs9.readFileSync(p, "utf8");
21658
21980
  } catch {
21659
21981
  return void 0;
21660
21982
  }
@@ -21667,11 +21989,11 @@ function runHistoryPipeline(run, args) {
21667
21989
  run,
21668
21990
  maxRuns: args.maxHistoryRuns
21669
21991
  });
21670
- const dir = path9.dirname(historyPath);
21671
- fs8.mkdirSync(dir, { recursive: true });
21992
+ const dir = path10.dirname(historyPath);
21993
+ fs9.mkdirSync(dir, { recursive: true });
21672
21994
  saveHistory(
21673
21995
  { filePath: historyPath, store: updated },
21674
- { writeFile: (p, content) => fs8.writeFileSync(p, content, "utf8") }
21996
+ { writeFile: (p, content) => fs9.writeFileSync(p, content, "utf8") }
21675
21997
  );
21676
21998
  let metricsCount = 0;
21677
21999
  for (const testId of Object.keys(updated.tests)) {
@@ -21818,11 +22140,11 @@ function writeReviewReport(review, args) {
21818
22140
  const outputDir = args.outputDir ?? "reports";
21819
22141
  const baseName = args.outputName ?? "evidence-review";
21820
22142
  const suffix = args.outputNameTimestamp ? `-${Math.floor(review.run.startedAtMs / 1e3)}` : "";
21821
- fs8.mkdirSync(outputDir, { recursive: true });
21822
- const mdPath = path9.join(outputDir, `${baseName}${suffix}.md`);
21823
- const htmlPath = path9.join(outputDir, `${baseName}${suffix}.html`);
21824
- fs8.writeFileSync(mdPath, markdown, "utf8");
21825
- fs8.writeFileSync(htmlPath, html, "utf8");
22143
+ fs9.mkdirSync(outputDir, { recursive: true });
22144
+ const mdPath = path10.join(outputDir, `${baseName}${suffix}.md`);
22145
+ const htmlPath = path10.join(outputDir, `${baseName}${suffix}.html`);
22146
+ fs9.writeFileSync(mdPath, markdown, "utf8");
22147
+ fs9.writeFileSync(htmlPath, html, "utf8");
21826
22148
  return [mdPath, htmlPath];
21827
22149
  }
21828
22150
  function evaluateReviewGate(review, args) {
@@ -21868,9 +22190,9 @@ function printResult(result, args, startMs, droppedMissingStory = 0) {
21868
22190
  function printCompareResult(result, args, startMs) {
21869
22191
  const durationMs = Date.now() - startMs;
21870
22192
  if (result.prSummary && args.prSummaryFile) {
21871
- const outputPath = path9.resolve(args.prSummaryFile);
21872
- fs8.mkdirSync(path9.dirname(outputPath), { recursive: true });
21873
- fs8.writeFileSync(outputPath, result.prSummary, "utf8");
22193
+ const outputPath = path10.resolve(args.prSummaryFile);
22194
+ fs9.mkdirSync(path10.dirname(outputPath), { recursive: true });
22195
+ fs9.writeFileSync(outputPath, result.prSummary, "utf8");
21874
22196
  }
21875
22197
  if (args.jsonSummary) {
21876
22198
  console.log(
@@ -21961,7 +22283,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
21961
22283
  console.error("Error: missing ADF file argument. Run with --help for usage.");
21962
22284
  process.exit(EXIT_USAGE);
21963
22285
  }
21964
- if (!fs8.existsSync(inputFile)) {
22286
+ if (!fs9.existsSync(inputFile)) {
21965
22287
  console.error(`Error: file not found: ${inputFile}`);
21966
22288
  process.exit(EXIT_USAGE);
21967
22289
  }
@@ -21989,7 +22311,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
21989
22311
  console.error("Error: --title is required when creating a new page");
21990
22312
  process.exit(EXIT_USAGE);
21991
22313
  }
21992
- const adf = fs8.readFileSync(path9.resolve(inputFile), "utf8");
22314
+ const adf = fs9.readFileSync(path10.resolve(inputFile), "utf8");
21993
22315
  if (dryRun) {
21994
22316
  console.log(
21995
22317
  JSON.stringify(
@@ -22068,7 +22390,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
22068
22390
  console.error("Error: missing ADF file argument. Run with --help for usage.");
22069
22391
  process.exit(EXIT_USAGE);
22070
22392
  }
22071
- if (!fs8.existsSync(inputFile)) {
22393
+ if (!fs9.existsSync(inputFile)) {
22072
22394
  console.error(`Error: file not found: ${inputFile}`);
22073
22395
  process.exit(EXIT_USAGE);
22074
22396
  }
@@ -22095,7 +22417,7 @@ Generate an API token at https://id.atlassian.com/manage-profile/security/api-to
22095
22417
  process.exit(EXIT_USAGE);
22096
22418
  }
22097
22419
  const mode = modeRaw;
22098
- const adf = fs8.readFileSync(path9.resolve(inputFile), "utf8");
22420
+ const adf = fs9.readFileSync(path10.resolve(inputFile), "utf8");
22099
22421
  if (dryRun) {
22100
22422
  console.log(
22101
22423
  JSON.stringify(