headlamp 0.1.6 → 0.1.8

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/index.js CHANGED
@@ -67,7 +67,8 @@ var init_args = __esm({
67
67
  coverageMode: (value) => ({ type: "coverageMode", value }),
68
68
  coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
69
69
  coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
70
- coveragePageFit: (value) => ({ type: "coveragePageFit", value })
70
+ coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
71
+ changed: (value) => ({ type: "changed", value })
71
72
  };
72
73
  Some = (value) => ({ _tag: "some", value });
73
74
  None = { _tag: "none" };
@@ -264,6 +265,18 @@ var init_args = __esm({
264
265
  "--coverage.root=",
265
266
  (value) => step([ActionBuilders.coverageRoot((value.split("=")[1] ?? "").trim())])
266
267
  ),
268
+ // --changed flag: selects changed files via git (all|staged|unstaged)
269
+ rule.eq("--changed", () => step([ActionBuilders.changed("all")])),
270
+ rule.startsWith("--changed=", (value) => {
271
+ const raw = (value.split("=")[1] ?? "").trim().toLowerCase();
272
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
273
+ return step([ActionBuilders.changed(mode)]);
274
+ }),
275
+ rule.withLookahead("--changed", (_flag, lookahead) => {
276
+ const raw = String(lookahead).trim().toLowerCase();
277
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
278
+ return step([ActionBuilders.changed(mode)], true);
279
+ }),
267
280
  rule.withLookahead(
268
281
  "-t",
269
282
  (flag, lookahead) => step(
@@ -366,6 +379,8 @@ var init_args = __esm({
366
379
  return { vitest: [], jest: [], coverage: false, coverageMaxHotspots: action.value };
367
380
  case "coveragePageFit":
368
381
  return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
382
+ case "changed":
383
+ return { vitest: [], jest: [], coverage: false, changed: action.value };
369
384
  case "jestArg":
370
385
  return { vitest: [], jest: [action.value], coverage: false };
371
386
  case "vitestArg":
@@ -405,6 +420,7 @@ var init_args = __esm({
405
420
  }
406
421
  return {
407
422
  ...next,
423
+ ...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
408
424
  ...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
409
425
  ...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
410
426
  ...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
@@ -490,7 +506,8 @@ var init_args = __esm({
490
506
  ...coverageMaxHotspotsLocal !== void 0 ? { coverageMaxHotspots: coverageMaxHotspotsLocal } : {},
491
507
  coveragePageFit,
492
508
  ...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
493
- ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {}
509
+ ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
510
+ ...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
494
511
  };
495
512
  return out;
496
513
  };
@@ -557,7 +574,7 @@ var init_TimeoutError = __esm({
557
574
 
558
575
  // node_modules/es-toolkit/dist/promise/delay.mjs
559
576
  function delay(ms, { signal } = {}) {
560
- return new Promise((resolve8, reject) => {
577
+ return new Promise((resolve9, reject) => {
561
578
  const abortError = () => {
562
579
  reject(new AbortError());
563
580
  };
@@ -570,7 +587,7 @@ function delay(ms, { signal } = {}) {
570
587
  }
571
588
  const timeoutId = setTimeout(() => {
572
589
  signal?.removeEventListener("abort", abortHandler);
573
- resolve8();
590
+ resolve9();
574
591
  }, ms);
575
592
  signal?.addEventListener("abort", abortHandler, { once: true });
576
593
  });
@@ -635,11 +652,11 @@ var init_exec = __esm({
635
652
  child.stderr?.on("data", (chunk) => {
636
653
  stderr += String(chunk);
637
654
  });
638
- const exec = new Promise((resolve8, reject) => {
655
+ const exec = new Promise((resolve9, reject) => {
639
656
  child.on("error", reject);
640
657
  child.on(
641
658
  "close",
642
- (code) => Number(code) === 0 ? resolve8(stdout) : reject(new Error(stderr || `exit ${code}`))
659
+ (code) => Number(code) === 0 ? resolve9(stdout) : reject(new Error(stderr || `exit ${code}`))
643
660
  );
644
661
  });
645
662
  try {
@@ -659,7 +676,7 @@ var init_exec = __esm({
659
676
  throw caughtError;
660
677
  }
661
678
  };
662
- runExitCode = async (cmd, args, opts = {}) => new Promise((resolve8, reject) => {
679
+ runExitCode = async (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
663
680
  const child = spawn(cmd, [...args], {
664
681
  cwd: opts.cwd,
665
682
  env: opts.env,
@@ -668,9 +685,9 @@ var init_exec = __esm({
668
685
  windowsHide: true
669
686
  });
670
687
  child.on("error", reject);
671
- child.on("close", (code) => resolve8(Number(code)));
688
+ child.on("close", (code) => resolve9(Number(code)));
672
689
  });
673
- runWithCapture = async (cmd, args, opts) => new Promise((resolve8, reject) => {
690
+ runWithCapture = async (cmd, args, opts) => new Promise((resolve9, reject) => {
674
691
  const child = spawn(cmd, [...args], {
675
692
  cwd: opts.cwd,
676
693
  env: opts.env,
@@ -686,7 +703,7 @@ var init_exec = __esm({
686
703
  buf += String(chunk);
687
704
  });
688
705
  child.on("error", reject);
689
- child.on("close", (code) => resolve8({ code: Number(code), output: buf }));
706
+ child.on("close", (code) => resolve9({ code: Number(code), output: buf }));
690
707
  });
691
708
  }
692
709
  });
@@ -1097,8 +1114,8 @@ var require_utils = __commonJS({
1097
1114
  }
1098
1115
  return output;
1099
1116
  };
1100
- exports.basename = (path10, { windows } = {}) => {
1101
- const segs = path10.split(windows ? /[\\/]/ : "/");
1117
+ exports.basename = (path11, { windows } = {}) => {
1118
+ const segs = path11.split(windows ? /[\\/]/ : "/");
1102
1119
  const last = segs[segs.length - 1];
1103
1120
  if (last === "") {
1104
1121
  return segs[segs.length - 2];
@@ -4688,8 +4705,75 @@ var compositeBarPct = (summary, hotspots) => {
4688
4705
  // src/lib/coverage-print.ts
4689
4706
  init_env_utils();
4690
4707
  init_exec();
4691
- import * as path7 from "node:path";
4708
+ import * as path8 from "node:path";
4692
4709
  import * as fsSync2 from "node:fs";
4710
+
4711
+ // src/lib/relevance.ts
4712
+ init_args();
4713
+ init_fast_related();
4714
+ import * as path7 from "node:path";
4715
+ var normalizeAbs = (inputPath) => path7.resolve(inputPath).replace(/\\/g, "/");
4716
+ var compareBooleanDesc = (left, right) => {
4717
+ if (left === right) {
4718
+ return 0;
4719
+ }
4720
+ return right ? 1 : -1;
4721
+ };
4722
+ var compareNumberAsc = (left, right) => left - right;
4723
+ var compareStringAsc = (left, right) => left.localeCompare(right);
4724
+ var fileFailed = (file) => Boolean(
4725
+ (file.status ?? "") === "failed" || (file.testResults ?? []).some((assertion) => (assertion.status ?? "") === "failed")
4726
+ );
4727
+ var composeComparators = (...comparators) => (left, right) => {
4728
+ for (const cmp of comparators) {
4729
+ const result = cmp(left, right);
4730
+ if (result !== 0) {
4731
+ return result;
4732
+ }
4733
+ }
4734
+ return 0;
4735
+ };
4736
+ var comparatorForRank = (rankByPath) => {
4737
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4738
+ return composeComparators(
4739
+ (left, right) => compareBooleanDesc(fileFailed(left), fileFailed(right)),
4740
+ (left, right) => compareNumberAsc(
4741
+ rankOrInf(normalizeAbs(left.testFilePath)),
4742
+ rankOrInf(normalizeAbs(right.testFilePath))
4743
+ ),
4744
+ (left, right) => compareStringAsc(normalizeAbs(left.testFilePath), normalizeAbs(right.testFilePath))
4745
+ );
4746
+ };
4747
+ var computeDirectnessRank = async (opts) => {
4748
+ const selectionKey = opts.productionSeeds.map((abs) => path7.relative(opts.repoRoot, abs).replace(/\\/g, "/")).sort((left, right) => left.localeCompare(right)).join("|");
4749
+ const related = await cachedRelated({
4750
+ repoRoot: opts.repoRoot,
4751
+ selectionKey,
4752
+ compute: () => findRelatedTestsFast({
4753
+ repoRoot: opts.repoRoot,
4754
+ productionPaths: opts.productionSeeds,
4755
+ testGlobs: DEFAULT_TEST_GLOBS,
4756
+ excludeGlobs: opts.excludeGlobs ?? DEFAULT_EXCLUDE,
4757
+ timeoutMs: 1500
4758
+ })
4759
+ });
4760
+ const out = /* @__PURE__ */ new Map();
4761
+ related.forEach((abs, index) => {
4762
+ out.set(normalizeAbs(abs), index);
4763
+ });
4764
+ return out;
4765
+ };
4766
+ var sortTestResultsWithRank = (rankByPath, results) => results.slice().sort(comparatorForRank(rankByPath));
4767
+ var comparatorForPathRank = (rankByPath) => {
4768
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
4769
+ return composeComparators(
4770
+ (left, right) => compareNumberAsc(rankOrInf(normalizeAbs(left)), rankOrInf(normalizeAbs(right))),
4771
+ (left, right) => compareStringAsc(normalizeAbs(left), normalizeAbs(right))
4772
+ );
4773
+ };
4774
+ var sortPathsWithRank = (rankByPath, paths) => paths.slice().sort(comparatorForPathRank(rankByPath));
4775
+
4776
+ // src/lib/coverage-print.ts
4693
4777
  var printDetailedCoverage = async (opts) => {
4694
4778
  const files = opts.map.files().sort((fileA, fileB) => {
4695
4779
  const summaryA = opts.map.fileCoverageFor(fileA).toSummary();
@@ -4699,7 +4783,7 @@ var printDetailedCoverage = async (opts) => {
4699
4783
  for (const abs of files) {
4700
4784
  const fc = opts.map.fileCoverageFor(abs);
4701
4785
  const sum = fc.toSummary();
4702
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4786
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4703
4787
  const blocks = computeUncoveredBlocks(fc);
4704
4788
  const misses = missedBranches(fc);
4705
4789
  const missFns = missedFunctions(fc);
@@ -4708,9 +4792,9 @@ var printDetailedCoverage = async (opts) => {
4708
4792
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4709
4793
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4710
4794
  linesPctText
4711
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4712
- sum.functions.pct
4713
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4795
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4796
+ funcsPctText
4797
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4714
4798
  console.info(header);
4715
4799
  const max = opts.limitPerFile === "all" ? Number.POSITIVE_INFINITY : opts.limitPerFile ?? 5;
4716
4800
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
@@ -4737,10 +4821,8 @@ var printDetailedCoverage = async (opts) => {
4737
4821
  abs,
4738
4822
  block.start,
4739
4823
  opts.editorCmd
4740
- )}\x07${path7.basename(abs)}:${block.start}\x1B]8;;\x07`;
4741
- const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(
4742
- `L${block.end}`
4743
- )} ${link}`;
4824
+ )}\x07${path8.basename(abs)}:${block.start}\x1B]8;;\x07`;
4825
+ const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(`L${block.end}`)} ${link}`;
4744
4826
  console.info(label);
4745
4827
  if (opts.showCode && src.length) {
4746
4828
  const lines = src.split(/\r?\n/);
@@ -4760,7 +4842,7 @@ var printDetailedCoverage = async (opts) => {
4760
4842
  abs,
4761
4843
  fn.line,
4762
4844
  opts.editorCmd
4763
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4845
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`;
4764
4846
  console.info(` - ${fn.name} @ ${link}`);
4765
4847
  }
4766
4848
  }
@@ -4771,12 +4853,8 @@ var printDetailedCoverage = async (opts) => {
4771
4853
  abs,
4772
4854
  br.line,
4773
4855
  opts.editorCmd
4774
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`;
4775
- console.info(
4776
- ` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(
4777
- ", "
4778
- )}]`
4779
- );
4856
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`;
4857
+ console.info(` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(", ")}]`);
4780
4858
  }
4781
4859
  }
4782
4860
  console.info("");
@@ -4796,7 +4874,7 @@ var printCompactCoverage = async (opts) => {
4796
4874
  for (const abs of files.slice(0, fileCap)) {
4797
4875
  const fc = opts.map.fileCoverageFor(abs);
4798
4876
  const sum = fc.toSummary();
4799
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
4877
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
4800
4878
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
4801
4879
  const secondLength = secondRange.end - secondRange.start;
4802
4880
  const firstLength = firstRange.end - firstRange.start;
@@ -4810,9 +4888,9 @@ var printCompactCoverage = async (opts) => {
4810
4888
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
4811
4889
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
4812
4890
  linesPctText
4813
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
4814
- sum.functions.pct
4815
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4891
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
4892
+ funcsPctText
4893
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
4816
4894
  console.info(header);
4817
4895
  const hotspots = blocks.slice(0, maxHotspotsDerived);
4818
4896
  if (hotspots.length) {
@@ -4823,10 +4901,8 @@ var printCompactCoverage = async (opts) => {
4823
4901
  abs,
4824
4902
  hotspot.start,
4825
4903
  opts.editorCmd
4826
- )}\x07${path7.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
4827
- console.info(
4828
- ` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`
4829
- );
4904
+ )}\x07${path8.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
4905
+ console.info(` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`);
4830
4906
  }
4831
4907
  }
4832
4908
  const functionsList = missFns.slice(0, maxFunctionsDerived);
@@ -4838,7 +4914,7 @@ var printCompactCoverage = async (opts) => {
4838
4914
  abs,
4839
4915
  fn.line,
4840
4916
  opts.editorCmd
4841
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`
4917
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`
4842
4918
  );
4843
4919
  }
4844
4920
  }
@@ -4853,7 +4929,7 @@ var printCompactCoverage = async (opts) => {
4853
4929
  abs,
4854
4930
  br.line,
4855
4931
  opts.editorCmd
4856
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`
4932
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`
4857
4933
  );
4858
4934
  }
4859
4935
  }
@@ -4862,9 +4938,7 @@ var printCompactCoverage = async (opts) => {
4862
4938
  const restBrs = Math.max(0, misses.length - branchesList.length);
4863
4939
  if (restHs + restFns + restBrs > 0) {
4864
4940
  console.info(
4865
- ansi.dim(
4866
- ` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`
4867
- )
4941
+ ansi.dim(` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`)
4868
4942
  );
4869
4943
  }
4870
4944
  console.info("");
@@ -4901,7 +4975,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4901
4975
  return { stem: base.slice(0, -ending.length), ext: ending };
4902
4976
  }
4903
4977
  }
4904
- const ext2 = path7.extname(base);
4978
+ const ext2 = path8.extname(base);
4905
4979
  return { stem: base.slice(0, -ext2.length), ext: ext2 };
4906
4980
  };
4907
4981
  const sliceBalanced = (input, width) => {
@@ -4984,12 +5058,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
4984
5058
  const tailParts = tailSrc.map((segment) => segment);
4985
5059
  let hidAny = false;
4986
5060
  const build = () => {
4987
- const label2 = joinParts(
4988
- headParts,
4989
- tailParts,
4990
- hideMiddle2 || hidAny,
4991
- baseLabel
4992
- );
5061
+ const label2 = joinParts(headParts, tailParts, hideMiddle2 || hidAny, baseLabel);
4993
5062
  return { label: label2, width: visibleWidth(label2) };
4994
5063
  };
4995
5064
  let { label, width } = build();
@@ -5082,13 +5151,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5082
5151
  return { headRaw: headRaw2, tailRaw: tailRaw2, hideMiddle: hideMiddle2 };
5083
5152
  };
5084
5153
  let { headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tailCount);
5085
- let candidate = tryTrimDirsToFit(
5086
- headRaw,
5087
- tailRaw,
5088
- hideMiddle,
5089
- baseFull,
5090
- maxWidth
5091
- );
5154
+ let candidate = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5092
5155
  if (!candidate) {
5093
5156
  return baseFull;
5094
5157
  }
@@ -5097,13 +5160,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5097
5160
  if (headCount + tailCount < total) {
5098
5161
  const tryTail = Math.min(tailCount + 1, total - headCount);
5099
5162
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tryTail));
5100
- const candTail = tryTrimDirsToFit(
5101
- headRaw,
5102
- tailRaw,
5103
- hideMiddle,
5104
- baseFull,
5105
- maxWidth
5106
- );
5163
+ const candTail = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5107
5164
  if (candTail) {
5108
5165
  tailCount = tryTail;
5109
5166
  candidate = candTail;
@@ -5113,13 +5170,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
5113
5170
  if (!advanced && headCount + tailCount < total) {
5114
5171
  const tryHead = Math.min(headCount + 1, total - tailCount);
5115
5172
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(tryHead, tailCount));
5116
- const candHead = tryTrimDirsToFit(
5117
- headRaw,
5118
- tailRaw,
5119
- hideMiddle,
5120
- baseFull,
5121
- maxWidth
5122
- );
5173
+ const candHead = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
5123
5174
  if (candHead) {
5124
5175
  headCount = tryHead;
5125
5176
  candidate = candHead;
@@ -5181,7 +5232,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5181
5232
  const queue = [];
5182
5233
  const seen = /* @__PURE__ */ new Set();
5183
5234
  for (const testAbs of executedTestsAbs) {
5184
- const testPathNormalized = path7.resolve(testAbs).replace(/\\/g, "/");
5235
+ const testPathNormalized = path8.resolve(testAbs).replace(/\\/g, "/");
5185
5236
  dist.set(testPathNormalized, 0);
5186
5237
  queue.push([testPathNormalized, 0]);
5187
5238
  }
@@ -5200,12 +5251,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5200
5251
  const specs = await extractImportSpecs2(currentFile, specsCache);
5201
5252
  const nextDistance = currentDistance + 1;
5202
5253
  for (const spec of specs) {
5203
- const resolved = resolveImportWithRoot(
5204
- currentFile,
5205
- spec,
5206
- rootDir,
5207
- resolutionCache
5208
- );
5254
+ const resolved = resolveImportWithRoot(currentFile, spec, rootDir, resolutionCache);
5209
5255
  const usable = resolved && !resolved.includes("/node_modules/");
5210
5256
  if (usable) {
5211
5257
  const existing = dist.get(resolved);
@@ -5220,13 +5266,10 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
5220
5266
  return dist;
5221
5267
  };
5222
5268
  var renderPerFileCompositeTable = async (opts) => {
5223
- const rel = path7.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5269
+ const rel = path8.relative(opts.root, opts.absPath).replace(/\\/g, "/");
5224
5270
  const sum = opts.file.toSummary();
5225
5271
  const rowsAvail = typeof process.stdout.rows === "number" && process.stdout.rows > 10 ? process.stdout.rows : 40;
5226
- const tableBudget = Math.max(
5227
- 14,
5228
- Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8)
5229
- );
5272
+ const tableBudget = Math.max(14, Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8));
5230
5273
  const rowBudget = Math.max(6, tableBudget - 6);
5231
5274
  const blocks = computeUncoveredBlocks(opts.file).slice().sort((firstRange, secondRange) => {
5232
5275
  const firstLength = firstRange.end - firstRange.start;
@@ -5270,9 +5313,7 @@ var renderPerFileCompositeTable = async (opts) => {
5270
5313
  rows.push([
5271
5314
  cell(
5272
5315
  rel,
5273
- (padded) => ansi.dim(
5274
- shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length)
5275
- )
5316
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5276
5317
  ),
5277
5318
  cell("Totals", ansi.dim),
5278
5319
  cell("\u2014", ansi.dim),
@@ -5291,11 +5332,7 @@ var renderPerFileCompositeTable = async (opts) => {
5291
5332
  rows.push([
5292
5333
  cell(
5293
5334
  rel,
5294
- (padded) => ansi.dim(
5295
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5296
- padded.length
5297
- )
5298
- )
5335
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5299
5336
  ),
5300
5337
  cell("Hotspots", ansi.dim),
5301
5338
  cell("", ansi.dim),
@@ -5309,14 +5346,8 @@ var renderPerFileCompositeTable = async (opts) => {
5309
5346
  rows.push([
5310
5347
  cell(rel, (padded) => {
5311
5348
  const width = padded.length;
5312
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5313
- width
5314
- );
5315
- return linkifyPadded(
5316
- opts.absPath,
5317
- hotspotRange.start,
5318
- opts.editorCmd
5319
- )(display);
5349
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5350
+ return linkifyPadded(opts.absPath, hotspotRange.start, opts.editorCmd)(display);
5320
5351
  }),
5321
5352
  cell("Hotspot"),
5322
5353
  cell(`L${hotspotRange.start}\u2013L${hotspotRange.end}`),
@@ -5333,11 +5364,7 @@ var renderPerFileCompositeTable = async (opts) => {
5333
5364
  rows.push([
5334
5365
  cell(
5335
5366
  rel,
5336
- (padded) => ansi.dim(
5337
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5338
- padded.length
5339
- )
5340
- )
5367
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5341
5368
  ),
5342
5369
  cell("Functions", ansi.dim),
5343
5370
  cell("", ansi.dim),
@@ -5351,14 +5378,8 @@ var renderPerFileCompositeTable = async (opts) => {
5351
5378
  rows.push([
5352
5379
  cell(rel, (padded) => {
5353
5380
  const width = padded.length;
5354
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5355
- width
5356
- );
5357
- return linkifyPadded(
5358
- opts.absPath,
5359
- missedFunction.line,
5360
- opts.editorCmd
5361
- )(display);
5381
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5382
+ return linkifyPadded(opts.absPath, missedFunction.line, opts.editorCmd)(display);
5362
5383
  }),
5363
5384
  cell("Func"),
5364
5385
  cell(`L${missedFunction.line}`),
@@ -5375,11 +5396,7 @@ var renderPerFileCompositeTable = async (opts) => {
5375
5396
  rows.push([
5376
5397
  cell(
5377
5398
  rel,
5378
- (padded) => ansi.dim(
5379
- shortenPathPreservingFilename(rel, padded.length).padEnd(
5380
- padded.length
5381
- )
5382
- )
5399
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
5383
5400
  ),
5384
5401
  cell("Branches", ansi.dim),
5385
5402
  cell("", ansi.dim),
@@ -5393,14 +5410,8 @@ var renderPerFileCompositeTable = async (opts) => {
5393
5410
  rows.push([
5394
5411
  cell(rel, (padded) => {
5395
5412
  const width = padded.length;
5396
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5397
- width
5398
- );
5399
- return linkifyPadded(
5400
- opts.absPath,
5401
- missedBranch.line,
5402
- opts.editorCmd
5403
- )(display);
5413
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5414
+ return linkifyPadded(opts.absPath, missedBranch.line, opts.editorCmd)(display);
5404
5415
  }),
5405
5416
  cell("Branch"),
5406
5417
  cell(`L${missedBranch.line}`),
@@ -5408,9 +5419,7 @@ var renderPerFileCompositeTable = async (opts) => {
5408
5419
  cell(""),
5409
5420
  cell(""),
5410
5421
  cell(""),
5411
- cell(
5412
- `#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`
5413
- )
5422
+ cell(`#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`)
5414
5423
  ]);
5415
5424
  }
5416
5425
  }
@@ -5430,9 +5439,7 @@ var renderPerFileCompositeTable = async (opts) => {
5430
5439
  rows.push([
5431
5440
  cell(rel, (padded) => {
5432
5441
  const width = padded.length;
5433
- const display = shortenPathPreservingFilename(rel, width).padEnd(
5434
- width
5435
- );
5442
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
5436
5443
  return linkifyPadded(opts.absPath, ln, opts.editorCmd)(display);
5437
5444
  }),
5438
5445
  cell("Line"),
@@ -5445,16 +5452,7 @@ var renderPerFileCompositeTable = async (opts) => {
5445
5452
  ]);
5446
5453
  }
5447
5454
  while (rows.length < target) {
5448
- rows.push([
5449
- cell(""),
5450
- cell(""),
5451
- cell(""),
5452
- cell(""),
5453
- cell(""),
5454
- cell(""),
5455
- cell(""),
5456
- cell("")
5457
- ]);
5455
+ rows.push([cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell("")]);
5458
5456
  }
5459
5457
  }
5460
5458
  }
@@ -5462,20 +5460,17 @@ var renderPerFileCompositeTable = async (opts) => {
5462
5460
  console.info(table);
5463
5461
  const sep = ansi.gray(
5464
5462
  "\u2500".repeat(
5465
- Math.max(
5466
- 20,
5467
- typeof process.stdout.columns === "number" ? process.stdout.columns : 100
5468
- )
5463
+ Math.max(20, typeof process.stdout.columns === "number" ? process.stdout.columns : 100)
5469
5464
  )
5470
5465
  );
5471
5466
  console.info(sep);
5472
5467
  };
5473
5468
  var printPerFileCompositeTables = async (opts) => {
5474
5469
  const selectionAbs = (opts.selectionPaths ?? []).map(
5475
- (selPath) => path7.resolve(selPath).replace(/\\/g, "/")
5470
+ (selPath) => path8.resolve(selPath).replace(/\\/g, "/")
5476
5471
  );
5477
5472
  const changedAbs = (opts.changedFiles ?? []).map(
5478
- (chgPath) => path7.resolve(chgPath).replace(/\\/g, "/")
5473
+ (chgPath) => path8.resolve(chgPath).replace(/\\/g, "/")
5479
5474
  );
5480
5475
  const tokenizeForSimilarity = (filePathForTokens) => new Set(
5481
5476
  filePathForTokens.toLowerCase().replace(/[^a-z0-9/_\-.]/g, " ").split(/[/_.-]+/).filter(Boolean)
@@ -5491,15 +5486,15 @@ var printPerFileCompositeTables = async (opts) => {
5491
5486
  return intersectionCount / unionSize;
5492
5487
  };
5493
5488
  const isSameDirOrChild = (firstAbs, secondAbs) => {
5494
- const dirA = path7.dirname(firstAbs).replace(/\\/g, "/");
5495
- const dirB = path7.dirname(secondAbs).replace(/\\/g, "/");
5489
+ const dirA = path8.dirname(firstAbs).replace(/\\/g, "/");
5490
+ const dirB = path8.dirname(secondAbs).replace(/\\/g, "/");
5496
5491
  return dirA === dirB || dirB.startsWith(`${dirA}/`) || dirA.startsWith(`${dirB}/`);
5497
5492
  };
5498
5493
  const selectionTokens = selectionAbs.map(tokenizeForSimilarity);
5499
5494
  const changedTokens = changedAbs.map(tokenizeForSimilarity);
5500
- const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path7.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5495
+ const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path8.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
5501
5496
  const testTokens = executedTestsAbs.map(tokenizeForSimilarity);
5502
- const allMapFilesAbs = opts.map.files().map((absPath) => path7.resolve(absPath).replace(/\\/g, "/"));
5497
+ const allMapFilesAbs = opts.map.files().map((absPath) => path8.resolve(absPath).replace(/\\/g, "/"));
5503
5498
  const uncoveredCandidates = allMapFilesAbs.filter((absPath) => {
5504
5499
  const sum = opts.map.fileCoverageFor(absPath).toSummary();
5505
5500
  return !(sum.lines.pct >= 100 && sum.functions.pct >= 100 && sum.branches.pct >= 100);
@@ -5516,33 +5511,24 @@ var printPerFileCompositeTables = async (opts) => {
5516
5511
  const selectionSetAbs = new Set(selectionAbs);
5517
5512
  const scored = await Promise.all(
5518
5513
  candidates.map(async (abs) => {
5519
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
5514
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
5520
5515
  const sum = opts.map.fileCoverageFor(abs).toSummary();
5521
5516
  const pct = Number.isFinite(sum.lines.pct) ? sum.lines.pct : 0;
5522
- const absNorm = path7.resolve(abs).replace(/\\/g, "/");
5517
+ const absNorm = path8.resolve(abs).replace(/\\/g, "/");
5523
5518
  const selfTokens = tokenizeForSimilarity(absNorm);
5524
5519
  const selSim = Math.max(
5525
5520
  0,
5526
- ...selectionTokens.map(
5527
- (selectionTokenSet) => jaccard(selfTokens, selectionTokenSet)
5528
- )
5521
+ ...selectionTokens.map((selectionTokenSet) => jaccard(selfTokens, selectionTokenSet))
5529
5522
  );
5530
5523
  const chgSim = Math.max(
5531
5524
  0,
5532
- ...changedTokens.map(
5533
- (changedTokenSet) => jaccard(selfTokens, changedTokenSet)
5534
- )
5535
- );
5536
- const tstSim = Math.max(
5537
- 0,
5538
- ...testTokens.map((tset) => jaccard(selfTokens, tset))
5525
+ ...changedTokens.map((changedTokenSet) => jaccard(selfTokens, changedTokenSet))
5539
5526
  );
5527
+ const tstSim = Math.max(0, ...testTokens.map((tset) => jaccard(selfTokens, tset)));
5540
5528
  const nearSelection = selectionAbs.some(
5541
5529
  (selectionPath) => isSameDirOrChild(absNorm, selectionPath)
5542
5530
  );
5543
- const nearChanged = changedAbs.some(
5544
- (changedPath) => isSameDirOrChild(absNorm, changedPath)
5545
- );
5531
+ const nearChanged = changedAbs.some((changedPath) => isSameDirOrChild(absNorm, changedPath));
5546
5532
  const related = selSim > 0 || chgSim > 0 || nearSelection || nearChanged;
5547
5533
  const distance = selectionSetAbs.has(absNorm) ? 0 : distFromTests.get(absNorm) ?? INF;
5548
5534
  let group = 6;
@@ -5566,9 +5552,12 @@ var printPerFileCompositeTables = async (opts) => {
5566
5552
  return { abs: absNorm, rel, linesPct: pct, group, score, distance };
5567
5553
  })
5568
5554
  );
5569
- let files = scored.sort(
5570
- (left, right) => left.group - right.group || left.distance - right.distance || right.score - left.score || right.linesPct - left.linesPct || left.rel.localeCompare(right.rel)
5571
- ).map((scoredItem) => scoredItem.abs);
5555
+ const prodSeeds = selectionAbs.length > 0 ? selectionAbs : changedAbs;
5556
+ const rank = await computeDirectnessRank({ repoRoot: opts.root, productionSeeds: prodSeeds });
5557
+ let files = sortPathsWithRank(
5558
+ rank,
5559
+ scored.map((s) => s.abs)
5560
+ );
5572
5561
  if (selectionAbs.length > 0) {
5573
5562
  const selectionSet = new Set(selectionAbs);
5574
5563
  const selectedHead = files.filter((filePath) => selectionSet.has(filePath));
@@ -5592,7 +5581,7 @@ var printPerFileCompositeTables = async (opts) => {
5592
5581
 
5593
5582
  // src/lib/jest-bridge.ts
5594
5583
  var import_json5 = __toESM(require_lib(), 1);
5595
- import * as path8 from "node:path";
5584
+ import * as path9 from "node:path";
5596
5585
  import * as fs4 from "node:fs";
5597
5586
  import * as util from "node:util";
5598
5587
  var extractBridgePath = (raw, cwd) => {
@@ -5603,7 +5592,7 @@ var extractBridgePath = (raw, cwd) => {
5603
5592
  return null;
5604
5593
  }
5605
5594
  const jsonPath = matches[matches.length - 1][1].trim().replace(/^["'`]|["'`]$/g, "");
5606
- return path8.isAbsolute(jsonPath) ? jsonPath : path8.resolve(cwd, jsonPath);
5595
+ return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
5607
5596
  };
5608
5597
  var drawRule = (label) => {
5609
5598
  const width = Math.max(
@@ -6251,7 +6240,7 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
6251
6240
  const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
6252
6241
  if (loc) {
6253
6242
  const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
6254
- out.push(` ${ansi.dim("at")} ${osc8(`${path8.basename(loc.file)}:${loc.line}`, href)}`);
6243
+ out.push(` ${ansi.dim("at")} ${osc8(`${path9.basename(loc.file)}:${loc.line}`, href)}`);
6255
6244
  }
6256
6245
  out.push("");
6257
6246
  return out;
@@ -6530,7 +6519,7 @@ function renderVitestFromJestJSON(data, opts) {
6530
6519
  const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
6531
6520
  const locLink = deepestLoc && (() => {
6532
6521
  const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, opts?.editorCmd);
6533
- const base = `${path8.basename(deepestLoc.file)}:${deepestLoc.line}`;
6522
+ const base = `${path9.basename(deepestLoc.file)}:${deepestLoc.line}`;
6534
6523
  return osc8(base, href);
6535
6524
  })();
6536
6525
  const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
@@ -6578,7 +6567,7 @@ ${footer}`;
6578
6567
  init_env_utils();
6579
6568
  init_exec();
6580
6569
  init_args();
6581
- import * as path9 from "node:path";
6570
+ import * as path10 from "node:path";
6582
6571
  import * as os2 from "node:os";
6583
6572
  import * as fsSync3 from "node:fs";
6584
6573
  import * as fs5 from "node:fs/promises";
@@ -6587,10 +6576,6 @@ import * as Reports from "istanbul-reports";
6587
6576
  import { createCoverageMap as createCoverageMap2 } from "istanbul-lib-coverage";
6588
6577
  var jestBin = "./node_modules/.bin/jest";
6589
6578
  var babelNodeBin = "./node_modules/.bin/babel-node";
6590
- var moduleSpecifierForRequire = (
6591
- // @ts-ignore
6592
- typeof __filename !== "undefined" ? __filename : import.meta.url
6593
- );
6594
6579
  var registerSignalHandlersOnce = () => {
6595
6580
  let handled = false;
6596
6581
  const on = (sig) => {
@@ -6608,7 +6593,6 @@ Received ${sig}, exiting...
6608
6593
  };
6609
6594
  var isDebug = () => Boolean(process.env.TEST_CLI_DEBUG);
6610
6595
  var mergeLcov = async () => {
6611
- const jestLcovPath = "coverage/jest/lcov.info";
6612
6596
  const vitestLcovPath = "coverage/vitest/lcov.info";
6613
6597
  const mergedOutPath = "coverage/lcov.info";
6614
6598
  const readOrEmpty = async (filePath) => {
@@ -6619,7 +6603,7 @@ var mergeLcov = async () => {
6619
6603
  }
6620
6604
  };
6621
6605
  let vitestContent = "";
6622
- let jestContent = "";
6606
+ const jestParts = [];
6623
6607
  try {
6624
6608
  vitestContent = await readOrEmpty(vitestLcovPath);
6625
6609
  } catch (readVitestError) {
@@ -6627,20 +6611,46 @@ var mergeLcov = async () => {
6627
6611
  console.info(`read vitest lcov failed: ${String(readVitestError)}`);
6628
6612
  }
6629
6613
  }
6614
+ const collectLcovs = (dir) => {
6615
+ const out = [];
6616
+ try {
6617
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6618
+ for (const entry of entries) {
6619
+ const full = path10.join(dir, entry.name);
6620
+ if (entry.isDirectory()) {
6621
+ out.push(...collectLcovs(full));
6622
+ } else if (entry.isFile() && entry.name === "lcov.info") {
6623
+ out.push(full);
6624
+ }
6625
+ }
6626
+ } catch {
6627
+ }
6628
+ return out;
6629
+ };
6630
6630
  try {
6631
- jestContent = await readOrEmpty(jestLcovPath);
6631
+ const jestRoot = path10.join("coverage", "jest");
6632
+ const candidates = [path10.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
6633
+ for (const filePath of candidates) {
6634
+ try {
6635
+ const content = await readOrEmpty(filePath);
6636
+ if (content.trim()) {
6637
+ jestParts.push(content.trim());
6638
+ }
6639
+ } catch {
6640
+ }
6641
+ }
6632
6642
  } catch (readJestError) {
6633
6643
  if (isDebug()) {
6634
- console.info(`read jest lcov failed: ${String(readJestError)}`);
6644
+ console.info(`scan jest lcov failed: ${String(readJestError)}`);
6635
6645
  }
6636
6646
  }
6637
- if (!vitestContent && !jestContent) {
6647
+ if (!vitestContent && jestParts.length === 0) {
6638
6648
  if (isDebug()) {
6639
6649
  console.info("No coverage outputs found to merge.");
6640
6650
  }
6641
6651
  return;
6642
6652
  }
6643
- const merged = [vitestContent.trim(), jestContent.trim()].filter(Boolean).join("\n");
6653
+ const merged = [vitestContent.trim(), ...jestParts].filter(Boolean).join("\n");
6644
6654
  if (merged.length > 0) {
6645
6655
  await (await import("node:fs/promises")).mkdir("coverage", { recursive: true });
6646
6656
  await (await import("node:fs/promises")).writeFile(mergedOutPath, `${merged}
@@ -6653,23 +6663,40 @@ var mergeLcov = async () => {
6653
6663
  }
6654
6664
  };
6655
6665
  var emitMergedCoverage = async (ui, opts) => {
6656
- const jestJson = path9.join("coverage", "jest", "coverage-final.json");
6657
- const jSize = fsSync3.existsSync(jestJson) ? fsSync3.statSync(jestJson).size : -1;
6658
- const jestSizeLabel = jSize >= 0 ? `${jSize} bytes` : "missing";
6659
- if (isDebug()) {
6660
- console.info(`Coverage JSON probe \u2192 jest: ${jestSizeLabel}`);
6661
- }
6662
- const jestData = await readCoverageJson(jestJson);
6663
- const jestFilesCount = Object.keys(jestData).length;
6664
- if (isDebug()) {
6665
- console.info(`Decoded coverage entries \u2192 jest: ${jestFilesCount}`);
6666
- }
6667
6666
  const map = createCoverageMap2({});
6668
- if (jestFilesCount > 0) {
6667
+ const listJsons = (dir) => {
6668
+ const out = [];
6669
6669
  try {
6670
- map.merge(jestData);
6670
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
6671
+ for (const entry of entries) {
6672
+ const full = path10.join(dir, entry.name);
6673
+ if (entry.isDirectory()) {
6674
+ out.push(...listJsons(full));
6675
+ } else if (entry.isFile() && entry.name === "coverage-final.json") {
6676
+ out.push(full);
6677
+ }
6678
+ }
6679
+ } catch {
6680
+ }
6681
+ return out;
6682
+ };
6683
+ const coverageRoot = path10.join("coverage", "jest");
6684
+ const jsonCandidates = [
6685
+ path10.join(coverageRoot, "coverage-final.json"),
6686
+ ...listJsons(coverageRoot)
6687
+ ].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => {
6688
+ const isFirst = arr.indexOf(absolutePath) === index;
6689
+ const exists = fsSync3.existsSync(absolutePath);
6690
+ return isFirst && exists;
6691
+ });
6692
+ for (const jsonPath of jsonCandidates) {
6693
+ try {
6694
+ const data = await readCoverageJson(jsonPath);
6695
+ if (Object.keys(data).length) {
6696
+ map.merge(data);
6697
+ }
6671
6698
  } catch (mergeJestError) {
6672
- console.warn(`Failed merging jest coverage JSON: ${String(mergeJestError)}`);
6699
+ console.warn(`Failed merging jest coverage JSON @ ${jsonPath}: ${String(mergeJestError)}`);
6673
6700
  }
6674
6701
  }
6675
6702
  if (map.files().length === 0) {
@@ -6719,7 +6746,7 @@ var emitMergedCoverage = async (ui, opts) => {
6719
6746
  executedTests: opts.executedTests ?? []
6720
6747
  });
6721
6748
  const context = LibReport.createContext({
6722
- dir: path9.resolve("coverage", "merged"),
6749
+ dir: path10.resolve("coverage", "merged"),
6723
6750
  coverageMap: filteredMap,
6724
6751
  defaultSummarizer: "nested"
6725
6752
  });
@@ -6787,8 +6814,8 @@ var emitMergedCoverage = async (ui, opts) => {
6787
6814
  for (const reporter of reporters) {
6788
6815
  reporter.execute(context);
6789
6816
  }
6790
- const textPath = path9.resolve("coverage", "merged", "coverage.txt");
6791
- const summaryPath = path9.resolve("coverage", "merged", "coverage-summary.txt");
6817
+ const textPath = path10.resolve("coverage", "merged", "coverage.txt");
6818
+ const summaryPath = path10.resolve("coverage", "merged", "coverage-summary.txt");
6792
6819
  const filesToPrint = [];
6793
6820
  if (fsSync3.existsSync(textPath)) {
6794
6821
  filesToPrint.push(textPath);
@@ -6870,17 +6897,43 @@ var program = async () => {
6870
6897
  coverageMode,
6871
6898
  coverageMaxFiles: coverageMaxFilesArg,
6872
6899
  coverageMaxHotspots: coverageMaxHotspotsArg,
6873
- coveragePageFit
6900
+ coveragePageFit,
6901
+ changed
6874
6902
  } = deriveArgs(argv);
6875
- console.info(`Selection \u2192 specified=${selectionSpecified} paths=${selectionPaths.length}`);
6903
+ const getChangedFiles = async (mode, cwd) => {
6904
+ const collect = async (cmd, args) => {
6905
+ try {
6906
+ const out = await runText(cmd, args, {
6907
+ cwd,
6908
+ env: safeEnv(process.env, {}),
6909
+ timeoutMs: 4e3
6910
+ });
6911
+ return out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6912
+ } catch {
6913
+ return [];
6914
+ }
6915
+ };
6916
+ const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
6917
+ const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
6918
+ const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
6919
+ const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
6920
+ return rels.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
6921
+ };
6922
+ const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
6923
+ const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
6924
+ const selectionPathsAugmented = changedSelectionAbs.length ? Array.from(/* @__PURE__ */ new Set([...selectionPaths, ...changedSelectionAbs])) : selectionPaths;
6925
+ const selectionSpecifiedAugmented = Boolean(selectionSpecified || changedSelectionAbs.length > 0);
6926
+ console.info(
6927
+ `Selection \u2192 specified=${selectionSpecifiedAugmented} paths=${selectionPathsAugmented.length}`
6928
+ );
6876
6929
  const { jest } = argsForDiscovery(["run"], jestArgs);
6877
- const selectionLooksLikeTest = selectionPaths.some(
6930
+ const selectionLooksLikeTest = selectionPathsAugmented.some(
6878
6931
  (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText)
6879
6932
  );
6880
- const selectionLooksLikePath = selectionPaths.some(
6933
+ const selectionLooksLikePath = selectionPathsAugmented.some(
6881
6934
  (pathText) => /[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)
6882
6935
  );
6883
- const selectionHasPaths = selectionPaths.length > 0;
6936
+ const selectionHasPaths = selectionPathsAugmented.length > 0;
6884
6937
  const repoRootForDiscovery = workspaceRoot ?? await findRepoRoot();
6885
6938
  const expandProductionSelections = async (tokens, repoRoot) => {
6886
6939
  const results = /* @__PURE__ */ new Set();
@@ -6889,18 +6942,18 @@ var program = async () => {
6889
6942
  if (!token) {
6890
6943
  continue;
6891
6944
  }
6892
- const isAbs = path9.isAbsolute(token);
6945
+ const isAbs = path10.isAbsolute(token);
6893
6946
  const looksLikeRelPath = /[\\/]/.test(token);
6894
6947
  let candidateFromRoot;
6895
6948
  if (token.startsWith("/")) {
6896
- candidateFromRoot = path9.join(repoRoot, token.slice(1));
6949
+ candidateFromRoot = path10.join(repoRoot, token.slice(1));
6897
6950
  } else if (looksLikeRelPath) {
6898
- candidateFromRoot = path9.join(repoRoot, token);
6951
+ candidateFromRoot = path10.join(repoRoot, token);
6899
6952
  } else {
6900
6953
  candidateFromRoot = void 0;
6901
6954
  }
6902
6955
  const tryPushIfProd = (absPath) => {
6903
- const norm = path9.resolve(absPath).replace(/\\/g, "/");
6956
+ const norm = path10.resolve(absPath).replace(/\\/g, "/");
6904
6957
  const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
6905
6958
  if (!isTest && fsSync3.existsSync(norm)) {
6906
6959
  results.add(norm);
@@ -6922,7 +6975,7 @@ var program = async () => {
6922
6975
  }),
6923
6976
  timeoutMs: 4e3
6924
6977
  });
6925
- const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path9.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6978
+ const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path10.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
6926
6979
  (abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
6927
6980
  );
6928
6981
  matches.forEach((abs) => results.add(abs));
@@ -6931,20 +6984,20 @@ var program = async () => {
6931
6984
  }
6932
6985
  return Array.from(results);
6933
6986
  };
6934
- const initialProdSelections = selectionPaths.filter(
6987
+ const initialProdSelections = selectionPathsAugmented.filter(
6935
6988
  (pathText) => (/[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)) && !/(^|\/)tests?\//i.test(pathText) && !/\.(test|spec)\.[tj]sx?$/i.test(pathText)
6936
6989
  );
6937
- const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPaths, repoRootForDiscovery);
6990
+ const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPathsAugmented, repoRootForDiscovery);
6938
6991
  const selectionIncludesProdPaths = expandedProdSelections.length > 0;
6939
6992
  console.info(
6940
6993
  `Selection classify \u2192 looksLikePath=${selectionLooksLikePath} looksLikeTest=${selectionLooksLikeTest} prodPaths=${selectionIncludesProdPaths}`
6941
6994
  );
6942
- const stripPathTokens = (args) => args.filter((token) => !selectionPaths.includes(token));
6995
+ const stripPathTokens = (args) => args.filter((token) => !selectionPathsAugmented.includes(token));
6943
6996
  const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
6944
6997
  const projectConfigs = [];
6945
6998
  try {
6946
- const baseCfg = path9.resolve("jest.config.js");
6947
- const tsCfg = path9.resolve("jest.ts.config.js");
6999
+ const baseCfg = path10.resolve("jest.config.js");
7000
+ const tsCfg = path10.resolve("jest.ts.config.js");
6948
7001
  if (fsSync3.existsSync(baseCfg)) {
6949
7002
  projectConfigs.push(baseCfg);
6950
7003
  }
@@ -6961,7 +7014,7 @@ var program = async () => {
6961
7014
  );
6962
7015
  const prodSelections2 = expandedProdSelections;
6963
7016
  for (const cfg of projectConfigs) {
6964
- const cfgCwd = path9.dirname(cfg);
7017
+ const cfgCwd = path10.dirname(cfg);
6965
7018
  const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6966
7019
  cwd: cfgCwd
6967
7020
  });
@@ -6974,7 +7027,7 @@ var program = async () => {
6974
7027
  });
6975
7028
  } catch (err) {
6976
7029
  if (isDebug()) {
6977
- console.warn(`direct selection failed for project ${path9.basename(cfg)}: ${String(err)}`);
7030
+ console.warn(`direct selection failed for project ${path10.basename(cfg)}: ${String(err)}`);
6978
7031
  }
6979
7032
  }
6980
7033
  perProjectFiles.set(cfg, directPerProject);
@@ -6986,7 +7039,7 @@ var program = async () => {
6986
7039
  )} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
6987
7040
  );
6988
7041
  for (const cfg of projectConfigs) {
6989
- const cfgCwd = path9.dirname(cfg);
7042
+ const cfgCwd = path10.dirname(cfg);
6990
7043
  const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
6991
7044
  cwd: cfgCwd
6992
7045
  });
@@ -6996,18 +7049,18 @@ var program = async () => {
6996
7049
  const perProjectFiltered = /* @__PURE__ */ new Map();
6997
7050
  for (const cfg of projectConfigs) {
6998
7051
  const files = perProjectFiles.get(cfg) ?? [];
6999
- const selectionTestPaths = selectionPaths.filter(
7052
+ const selectionTestPaths = selectionPathsAugmented.filter(
7000
7053
  (pathToken) => /\.(test|spec)\.[tj]sx?$/i.test(pathToken) || /(^|\/)tests?\//i.test(pathToken)
7001
7054
  );
7002
7055
  const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
7003
7056
  const absFiles = candidates.map(
7004
- (candidatePath) => path9.isAbsolute(candidatePath) ? candidatePath : path9.join(repoRootForDiscovery, candidatePath)
7057
+ (candidatePath) => path10.isAbsolute(candidatePath) ? candidatePath : path10.join(repoRootForDiscovery, candidatePath)
7005
7058
  ).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
7006
7059
  const onlyOwned = await filterCandidatesForProject(
7007
7060
  cfg,
7008
7061
  jestDiscoveryArgs,
7009
7062
  absFiles,
7010
- path9.dirname(cfg)
7063
+ path10.dirname(cfg)
7011
7064
  );
7012
7065
  perProjectFiltered.set(cfg, onlyOwned);
7013
7066
  }
@@ -7019,7 +7072,7 @@ var program = async () => {
7019
7072
  if (selectionHasPaths && prodSelections.length > 0) {
7020
7073
  console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
7021
7074
  const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
7022
- const selectionKey = prodSelections.map((absPath) => path9.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((a, b) => a.localeCompare(b)).join("|");
7075
+ const selectionKey = prodSelections.map((absPath) => path10.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
7023
7076
  const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
7024
7077
  const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
7025
7078
  const rgMatches = await cachedRelated2({
@@ -7049,7 +7102,7 @@ var program = async () => {
7049
7102
  cfg,
7050
7103
  jestDiscoveryArgs,
7051
7104
  rgCandidates,
7052
- path9.dirname(cfg)
7105
+ path10.dirname(cfg)
7053
7106
  );
7054
7107
  perProjectFromRg.set(cfg, owned);
7055
7108
  }
@@ -7064,9 +7117,9 @@ var program = async () => {
7064
7117
  } else {
7065
7118
  const repoRootForScan = repoRootForDiscovery;
7066
7119
  const toSeeds = (abs) => {
7067
- const rel = path9.relative(repoRootForScan, abs).replace(/\\/g, "/");
7120
+ const rel = path10.relative(repoRootForScan, abs).replace(/\\/g, "/");
7068
7121
  const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
7069
- const base = path9.basename(withoutExt);
7122
+ const base = path10.basename(withoutExt);
7070
7123
  const segs = withoutExt.split("/");
7071
7124
  const tail2 = segs.slice(-2).join("/");
7072
7125
  return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
@@ -7081,8 +7134,8 @@ var program = async () => {
7081
7134
  }
7082
7135
  };
7083
7136
  const resolveLocalImport = (fromFile, spec) => {
7084
- const baseDir = path9.dirname(fromFile);
7085
- const cand = path9.resolve(baseDir, spec);
7137
+ const baseDir = path10.dirname(fromFile);
7138
+ const cand = path10.resolve(baseDir, spec);
7086
7139
  const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
7087
7140
  for (const ext of exts) {
7088
7141
  const full = ext ? `${cand}${ext}` : cand;
@@ -7091,7 +7144,7 @@ var program = async () => {
7091
7144
  }
7092
7145
  }
7093
7146
  for (const ext of exts) {
7094
- const full = path9.join(cand, `index${ext}`);
7147
+ const full = path10.join(cand, `index${ext}`);
7095
7148
  if (fsSync3.existsSync(full)) {
7096
7149
  return full;
7097
7150
  }
@@ -7145,7 +7198,7 @@ var program = async () => {
7145
7198
  cfg,
7146
7199
  jestDiscoveryArgs,
7147
7200
  keptCandidates,
7148
- path9.dirname(cfg)
7201
+ path10.dirname(cfg)
7149
7202
  );
7150
7203
  perProjectFromScan.set(cfg, owned);
7151
7204
  }
@@ -7172,9 +7225,9 @@ var program = async () => {
7172
7225
  if (effectiveJestFiles.length === 0) {
7173
7226
  const repoRoot = repoRootForRefinement;
7174
7227
  const seeds = prodSelections.map(
7175
- (abs) => path9.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7228
+ (abs) => path10.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
7176
7229
  ).flatMap((rel) => {
7177
- const base = path9.basename(rel);
7230
+ const base = path10.basename(rel);
7178
7231
  const segments = rel.split("/");
7179
7232
  return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
7180
7233
  });
@@ -7187,8 +7240,8 @@ var program = async () => {
7187
7240
  }
7188
7241
  };
7189
7242
  const resolveLocalImport = (fromFile, spec) => {
7190
- const baseDir = path9.dirname(fromFile);
7191
- const candidate = path9.resolve(baseDir, spec);
7243
+ const baseDir = path10.dirname(fromFile);
7244
+ const candidate = path10.resolve(baseDir, spec);
7192
7245
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
7193
7246
  for (const ext of extensions) {
7194
7247
  const fullPath = ext ? `${candidate}${ext}` : candidate;
@@ -7197,7 +7250,7 @@ var program = async () => {
7197
7250
  }
7198
7251
  }
7199
7252
  for (const ext of extensions) {
7200
- const fullPath = path9.join(candidate, `index${ext}`);
7253
+ const fullPath = path10.join(candidate, `index${ext}`);
7201
7254
  if (fsSync3.existsSync(fullPath)) {
7202
7255
  return fullPath;
7203
7256
  }
@@ -7316,8 +7369,8 @@ var program = async () => {
7316
7369
  }
7317
7370
  }
7318
7371
  const jestDecision = decideShouldRunJest([], effectiveJestFiles, {
7319
- selectionSpecified,
7320
- selectionPaths
7372
+ selectionSpecified: selectionSpecifiedAugmented,
7373
+ selectionPaths: selectionPathsAugmented
7321
7374
  });
7322
7375
  const { shouldRunJest } = jestDecision;
7323
7376
  const jestCount = effectiveJestFiles.length;
@@ -7335,45 +7388,63 @@ var program = async () => {
7335
7388
  }
7336
7389
  console.info(`Run plan \u2192 Jest maybe=${shouldRunJest} (projects=${projectConfigs.length})`);
7337
7390
  let jestExitCode = 0;
7391
+ const allBridgeJson = [];
7338
7392
  const executedTestFilesSet = /* @__PURE__ */ new Set();
7339
7393
  if (shouldRunJest) {
7340
7394
  console.info("Starting Jest (no Vitest targets)\u2026");
7341
7395
  await runJestBootstrap();
7342
7396
  const jestRunArgs = selectionIncludesProdPaths ? stripPathTokens(jestArgs) : jestArgs;
7397
+ const sanitizedJestRunArgs = jestRunArgs.filter(
7398
+ (arg) => !/^--coverageDirectory(?:=|$)/.test(String(arg))
7399
+ );
7343
7400
  const projectsToRun = projectConfigs.filter(
7344
7401
  (cfg) => (perProjectFiltered.get(cfg) ?? []).length > 0
7345
7402
  );
7346
- const totalProjectsToRun = projectsToRun.length;
7347
7403
  const stripFooter = (text) => {
7348
7404
  const lines = text.split("\n");
7349
7405
  const idx = lines.findIndex((ln) => /^Test Files\s/.test(stripAnsiSimple(ln)));
7350
7406
  return idx >= 0 ? lines.slice(0, idx).join("\n").trimEnd() : text;
7351
7407
  };
7408
+ const prodSeedsForRun = (() => {
7409
+ const changedAbs = (changedSelectionAbs ?? []).map(
7410
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7411
+ );
7412
+ const selAbs = selectionPathsAugmented.map(
7413
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7414
+ );
7415
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7416
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7417
+ );
7418
+ })();
7419
+ const repoRootForRank = repoRootForDiscovery;
7420
+ const fileRank = await computeDirectnessRank({
7421
+ repoRoot: repoRootForRank,
7422
+ productionSeeds: prodSeedsForRun
7423
+ });
7352
7424
  for (let projIndex = 0; projIndex < projectsToRun.length; projIndex += 1) {
7353
7425
  const cfg = projectsToRun[projIndex];
7354
- const isLastProject = projIndex === totalProjectsToRun - 1;
7355
7426
  const files = perProjectFiltered.get(cfg) ?? [];
7356
7427
  if (files.length === 0) {
7357
- console.info(`Project ${path9.basename(cfg)}: 0 matching tests after filter; skipping.`);
7428
+ console.info(`Project ${path10.basename(cfg)}: 0 matching tests after filter; skipping.`);
7358
7429
  continue;
7359
7430
  }
7360
7431
  files.forEach(
7361
- (absTestPath) => executedTestFilesSet.add(path9.resolve(absTestPath).replace(/\\/g, "/"))
7432
+ (absTestPath) => executedTestFilesSet.add(path10.resolve(absTestPath).replace(/\\/g, "/"))
7362
7433
  );
7363
- const outJson = path9.join(
7434
+ const outJson = path10.join(
7364
7435
  os2.tmpdir(),
7365
7436
  `jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
7366
7437
  );
7367
- const reporterPath = path9.resolve("scripts/jest-vitest-bridge.cjs");
7438
+ const reporterPath = path10.resolve("scripts/jest-vitest-bridge.cjs");
7368
7439
  try {
7369
7440
  if (!fsSync3.existsSync(reporterPath)) {
7370
- fsSync3.mkdirSync(path9.dirname(reporterPath), { recursive: true });
7441
+ fsSync3.mkdirSync(path10.dirname(reporterPath), { recursive: true });
7371
7442
  fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
7372
7443
  }
7373
7444
  } catch (ensureReporterError) {
7374
7445
  console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
7375
7446
  }
7376
- const selectedFilesForCoverage = selectionPaths.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path9.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
7447
+ const selectedFilesForCoverage = selectionPathsAugmented.filter((pathToken) => /[\\/]/.test(pathToken)).filter((pathToken) => !looksLikeTestPath(pathToken)).map((pathToken) => path10.relative(repoRootForDiscovery, pathToken).replace(/\\\\/g, "/")).filter((rel) => rel && !/^\.+\//.test(rel)).map((rel) => rel.startsWith("./") ? rel : `./${rel}`);
7377
7448
  const coverageFromArgs = [];
7378
7449
  for (const relPath of selectedFilesForCoverage) {
7379
7450
  coverageFromArgs.push("--collectCoverageFrom", relPath);
@@ -7387,14 +7458,17 @@ var program = async () => {
7387
7458
  "--config",
7388
7459
  cfg,
7389
7460
  "--runTestsByPath",
7390
- "--reporters",
7391
- reporterPath,
7461
+ `--reporters=${reporterPath}`,
7392
7462
  "--silent",
7393
7463
  "--colors",
7394
7464
  "--json",
7395
7465
  "--outputFile",
7396
7466
  outJson,
7397
- ...jestRunArgs,
7467
+ ...sanitizedJestRunArgs,
7468
+ ...collectCoverage ? [
7469
+ "--coverageDirectory",
7470
+ path10.join("coverage", "jest", path10.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
7471
+ ] : [],
7398
7472
  ...coverageFromArgs,
7399
7473
  "--passWithNoTests",
7400
7474
  ...files
@@ -7420,10 +7494,22 @@ var program = async () => {
7420
7494
  const jsonText = fsSync3.readFileSync(outJson, "utf8");
7421
7495
  const parsed = JSON.parse(jsonText);
7422
7496
  const bridge = coerceJestJsonToBridge(parsed);
7423
- pretty = renderVitestFromJestJSON(bridge, {
7424
- cwd: repoRootForDiscovery,
7425
- ...editorCmd !== void 0 ? { editorCmd } : {}
7426
- });
7497
+ allBridgeJson.push(bridge);
7498
+ try {
7499
+ const reordered = {
7500
+ ...bridge,
7501
+ testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
7502
+ };
7503
+ pretty = renderVitestFromJestJSON(reordered, {
7504
+ cwd: repoRootForDiscovery,
7505
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7506
+ });
7507
+ } catch {
7508
+ pretty = renderVitestFromJestJSON(bridge, {
7509
+ cwd: repoRootForDiscovery,
7510
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7511
+ });
7512
+ }
7427
7513
  if (debug) {
7428
7514
  const preview = pretty.split("\n").slice(0, 3).join("\n");
7429
7515
  console.info(`pretty preview (json):
@@ -7447,9 +7533,7 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7447
7533
  ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7448
7534
  }
7449
7535
  }
7450
- if (!isLastProject) {
7451
- pretty = stripFooter(pretty);
7452
- }
7536
+ pretty = stripFooter(pretty);
7453
7537
  if (pretty.trim().length > 0) {
7454
7538
  process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
7455
7539
  `);
@@ -7461,15 +7545,69 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7461
7545
  } else {
7462
7546
  console.info("Jest run skipped based on selection and thresholds.");
7463
7547
  }
7464
- if (collectCoverage && shouldRunJest && coverageAbortOnFailure && jestExitCode !== 0) {
7465
- process.exit(jestExitCode);
7548
+ if (allBridgeJson.length > 0) {
7549
+ const agg = allBridgeJson.map((bridge) => bridge.aggregated);
7550
+ const sum = (select) => agg.reduce((total, item) => total + (select(item) || 0), 0);
7551
+ const startTime = Math.min(
7552
+ ...allBridgeJson.map((bridge) => Number(bridge.startTime || Date.now()))
7553
+ );
7554
+ const unified = {
7555
+ startTime,
7556
+ testResults: allBridgeJson.flatMap((bridge) => bridge.testResults),
7557
+ aggregated: {
7558
+ numTotalTestSuites: sum((item) => item.numTotalTestSuites),
7559
+ numPassedTestSuites: sum((item) => item.numPassedTestSuites),
7560
+ numFailedTestSuites: sum((item) => item.numFailedTestSuites),
7561
+ numTotalTests: sum((item) => item.numTotalTests),
7562
+ numPassedTests: sum((item) => item.numPassedTests),
7563
+ numFailedTests: sum((item) => item.numFailedTests),
7564
+ numPendingTests: sum((item) => item.numPendingTests),
7565
+ numTodoTests: sum((item) => item.numTodoTests),
7566
+ startTime,
7567
+ success: agg.every((item) => Boolean(item.success)),
7568
+ runTimeMs: sum((item) => Number(item.runTimeMs ?? 0))
7569
+ }
7570
+ };
7571
+ try {
7572
+ const prodSeeds = (() => {
7573
+ const changedAbs = (changedSelectionAbs ?? []).map(
7574
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
7575
+ );
7576
+ const selAbs = selectionPathsAugmented.map(
7577
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
7578
+ );
7579
+ return (changedAbs.length ? changedAbs : selAbs).filter(
7580
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
7581
+ );
7582
+ })();
7583
+ const unifiedRank = await computeDirectnessRank({
7584
+ repoRoot: repoRootForDiscovery,
7585
+ productionSeeds: prodSeeds
7586
+ });
7587
+ const ordered = sortTestResultsWithRank(unifiedRank, unified.testResults).reverse();
7588
+ unified.testResults = ordered;
7589
+ } catch {
7590
+ }
7591
+ const text = renderVitestFromJestJSON(unified, {
7592
+ cwd: repoRootForDiscovery,
7593
+ ...editorCmd !== void 0 ? { editorCmd } : {}
7594
+ });
7595
+ if (text.trim().length > 0) {
7596
+ process.stdout.write(text.endsWith("\n") ? text : `${text}
7597
+ `);
7598
+ }
7599
+ }
7600
+ const finalExitCode = jestExitCode;
7601
+ if (collectCoverage && shouldRunJest && coverageAbortOnFailure && finalExitCode !== 0) {
7602
+ process.exit(finalExitCode);
7603
+ return;
7466
7604
  }
7467
7605
  if (collectCoverage && shouldRunJest) {
7468
7606
  await mergeLcov();
7469
7607
  const repoRoot = workspaceRoot ?? await findRepoRoot();
7470
7608
  const mergedOptsBase = {
7471
- selectionSpecified,
7472
- selectionPaths,
7609
+ selectionSpecified: selectionSpecifiedAugmented,
7610
+ selectionPaths: selectionPathsAugmented,
7473
7611
  includeGlobs,
7474
7612
  excludeGlobs,
7475
7613
  workspaceRoot: repoRoot,
@@ -7484,7 +7622,6 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
7484
7622
  };
7485
7623
  await emitMergedCoverage(coverageUi, mergedOptsBase);
7486
7624
  }
7487
- const finalExitCode = jestExitCode;
7488
7625
  process.exit(finalExitCode);
7489
7626
  };
7490
7627
  export {