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/cli.cjs CHANGED
@@ -93,7 +93,7 @@ var init_TimeoutError = __esm({
93
93
 
94
94
  // node_modules/es-toolkit/dist/promise/delay.mjs
95
95
  function delay(ms, { signal } = {}) {
96
- return new Promise((resolve8, reject) => {
96
+ return new Promise((resolve9, reject) => {
97
97
  const abortError = () => {
98
98
  reject(new AbortError());
99
99
  };
@@ -106,7 +106,7 @@ function delay(ms, { signal } = {}) {
106
106
  }
107
107
  const timeoutId = setTimeout(() => {
108
108
  signal?.removeEventListener("abort", abortHandler);
109
- resolve8();
109
+ resolve9();
110
110
  }, ms);
111
111
  signal?.addEventListener("abort", abortHandler, { once: true });
112
112
  });
@@ -171,11 +171,11 @@ var init_exec = __esm({
171
171
  child.stderr?.on("data", (chunk) => {
172
172
  stderr += String(chunk);
173
173
  });
174
- const exec = new Promise((resolve8, reject) => {
174
+ const exec = new Promise((resolve9, reject) => {
175
175
  child.on("error", reject);
176
176
  child.on(
177
177
  "close",
178
- (code) => Number(code) === 0 ? resolve8(stdout) : reject(new Error(stderr || `exit ${code}`))
178
+ (code) => Number(code) === 0 ? resolve9(stdout) : reject(new Error(stderr || `exit ${code}`))
179
179
  );
180
180
  });
181
181
  try {
@@ -195,7 +195,7 @@ var init_exec = __esm({
195
195
  throw caughtError;
196
196
  }
197
197
  };
198
- runExitCode = async (cmd, args, opts = {}) => new Promise((resolve8, reject) => {
198
+ runExitCode = async (cmd, args, opts = {}) => new Promise((resolve9, reject) => {
199
199
  const child = (0, import_node_child_process.spawn)(cmd, [...args], {
200
200
  cwd: opts.cwd,
201
201
  env: opts.env,
@@ -204,9 +204,9 @@ var init_exec = __esm({
204
204
  windowsHide: true
205
205
  });
206
206
  child.on("error", reject);
207
- child.on("close", (code) => resolve8(Number(code)));
207
+ child.on("close", (code) => resolve9(Number(code)));
208
208
  });
209
- runWithCapture = async (cmd, args, opts) => new Promise((resolve8, reject) => {
209
+ runWithCapture = async (cmd, args, opts) => new Promise((resolve9, reject) => {
210
210
  const child = (0, import_node_child_process.spawn)(cmd, [...args], {
211
211
  cwd: opts.cwd,
212
212
  env: opts.env,
@@ -222,7 +222,7 @@ var init_exec = __esm({
222
222
  buf += String(chunk);
223
223
  });
224
224
  child.on("error", reject);
225
- child.on("close", (code) => resolve8({ code: Number(code), output: buf }));
225
+ child.on("close", (code) => resolve9({ code: Number(code), output: buf }));
226
226
  });
227
227
  }
228
228
  });
@@ -263,7 +263,8 @@ var init_args = __esm({
263
263
  coverageMode: (value) => ({ type: "coverageMode", value }),
264
264
  coverageMaxFiles: (value) => ({ type: "coverageMaxFiles", value }),
265
265
  coverageMaxHotspots: (value) => ({ type: "coverageMaxHotspots", value }),
266
- coveragePageFit: (value) => ({ type: "coveragePageFit", value })
266
+ coveragePageFit: (value) => ({ type: "coveragePageFit", value }),
267
+ changed: (value) => ({ type: "changed", value })
267
268
  };
268
269
  Some = (value) => ({ _tag: "some", value });
269
270
  None = { _tag: "none" };
@@ -460,6 +461,18 @@ var init_args = __esm({
460
461
  "--coverage.root=",
461
462
  (value) => step([ActionBuilders.coverageRoot((value.split("=")[1] ?? "").trim())])
462
463
  ),
464
+ // --changed flag: selects changed files via git (all|staged|unstaged)
465
+ rule.eq("--changed", () => step([ActionBuilders.changed("all")])),
466
+ rule.startsWith("--changed=", (value) => {
467
+ const raw = (value.split("=")[1] ?? "").trim().toLowerCase();
468
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
469
+ return step([ActionBuilders.changed(mode)]);
470
+ }),
471
+ rule.withLookahead("--changed", (_flag, lookahead) => {
472
+ const raw = String(lookahead).trim().toLowerCase();
473
+ const mode = raw === "staged" ? "staged" : raw === "unstaged" ? "unstaged" : "all";
474
+ return step([ActionBuilders.changed(mode)], true);
475
+ }),
463
476
  rule.withLookahead(
464
477
  "-t",
465
478
  (flag, lookahead) => step(
@@ -562,6 +575,8 @@ var init_args = __esm({
562
575
  return { vitest: [], jest: [], coverage: false, coverageMaxHotspots: action.value };
563
576
  case "coveragePageFit":
564
577
  return { vitest: [], jest: [], coverage: false, coveragePageFit: action.value };
578
+ case "changed":
579
+ return { vitest: [], jest: [], coverage: false, changed: action.value };
565
580
  case "jestArg":
566
581
  return { vitest: [], jest: [action.value], coverage: false };
567
582
  case "vitestArg":
@@ -601,6 +616,7 @@ var init_args = __esm({
601
616
  }
602
617
  return {
603
618
  ...next,
619
+ ...right.changed !== void 0 || left.changed !== void 0 ? { changed: right.changed ?? left.changed } : {},
604
620
  ...right.coverageAbortOnFailure !== void 0 || left.coverageAbortOnFailure !== void 0 ? { coverageAbortOnFailure: right.coverageAbortOnFailure ?? left.coverageAbortOnFailure } : {},
605
621
  ...right.coverageDetail !== void 0 || left.coverageDetail !== void 0 ? { coverageDetail: right.coverageDetail ?? left.coverageDetail } : {},
606
622
  ...right.coverageShowCode !== void 0 || left.coverageShowCode !== void 0 ? { coverageShowCode: right.coverageShowCode ?? left.coverageShowCode } : {},
@@ -686,7 +702,8 @@ var init_args = __esm({
686
702
  ...coverageMaxHotspotsLocal !== void 0 ? { coverageMaxHotspots: coverageMaxHotspotsLocal } : {},
687
703
  coveragePageFit,
688
704
  ...contrib.editorCmd !== void 0 ? { editorCmd: contrib.editorCmd } : {},
689
- ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {}
705
+ ...contrib.workspaceRoot !== void 0 ? { workspaceRoot: contrib.workspaceRoot } : {},
706
+ ...contrib.changed !== void 0 ? { changed: contrib.changed } : {}
690
707
  };
691
708
  return out;
692
709
  };
@@ -1960,7 +1977,7 @@ var require_lib = __commonJS({
1960
1977
  });
1961
1978
 
1962
1979
  // src/lib/program.ts
1963
- var path9 = __toESM(require("node:path"), 1);
1980
+ var path10 = __toESM(require("node:path"), 1);
1964
1981
  var os2 = __toESM(require("node:os"), 1);
1965
1982
  var fsSync3 = __toESM(require("node:fs"), 1);
1966
1983
  var fs5 = __toESM(require("node:fs/promises"), 1);
@@ -2921,7 +2938,7 @@ var compositeBarPct = (summary, hotspots) => {
2921
2938
  };
2922
2939
 
2923
2940
  // src/lib/coverage-print.ts
2924
- var path7 = __toESM(require("node:path"), 1);
2941
+ var path8 = __toESM(require("node:path"), 1);
2925
2942
  var fsSync2 = __toESM(require("node:fs"), 1);
2926
2943
  init_env_utils();
2927
2944
  init_exec();
@@ -3091,6 +3108,71 @@ var renderTable = (columns, rows) => {
3091
3108
  };
3092
3109
  var barCell = (pct) => (padded) => bar(pct, padded.length);
3093
3110
 
3111
+ // src/lib/relevance.ts
3112
+ var path7 = __toESM(require("node:path"), 1);
3113
+ init_args();
3114
+ init_fast_related();
3115
+ var normalizeAbs = (inputPath) => path7.resolve(inputPath).replace(/\\/g, "/");
3116
+ var compareBooleanDesc = (left, right) => {
3117
+ if (left === right) {
3118
+ return 0;
3119
+ }
3120
+ return right ? 1 : -1;
3121
+ };
3122
+ var compareNumberAsc = (left, right) => left - right;
3123
+ var compareStringAsc = (left, right) => left.localeCompare(right);
3124
+ var fileFailed = (file) => Boolean(
3125
+ (file.status ?? "") === "failed" || (file.testResults ?? []).some((assertion) => (assertion.status ?? "") === "failed")
3126
+ );
3127
+ var composeComparators = (...comparators) => (left, right) => {
3128
+ for (const cmp of comparators) {
3129
+ const result = cmp(left, right);
3130
+ if (result !== 0) {
3131
+ return result;
3132
+ }
3133
+ }
3134
+ return 0;
3135
+ };
3136
+ var comparatorForRank = (rankByPath) => {
3137
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
3138
+ return composeComparators(
3139
+ (left, right) => compareBooleanDesc(fileFailed(left), fileFailed(right)),
3140
+ (left, right) => compareNumberAsc(
3141
+ rankOrInf(normalizeAbs(left.testFilePath)),
3142
+ rankOrInf(normalizeAbs(right.testFilePath))
3143
+ ),
3144
+ (left, right) => compareStringAsc(normalizeAbs(left.testFilePath), normalizeAbs(right.testFilePath))
3145
+ );
3146
+ };
3147
+ var computeDirectnessRank = async (opts) => {
3148
+ const selectionKey = opts.productionSeeds.map((abs) => path7.relative(opts.repoRoot, abs).replace(/\\/g, "/")).sort((left, right) => left.localeCompare(right)).join("|");
3149
+ const related = await cachedRelated({
3150
+ repoRoot: opts.repoRoot,
3151
+ selectionKey,
3152
+ compute: () => findRelatedTestsFast({
3153
+ repoRoot: opts.repoRoot,
3154
+ productionPaths: opts.productionSeeds,
3155
+ testGlobs: DEFAULT_TEST_GLOBS,
3156
+ excludeGlobs: opts.excludeGlobs ?? DEFAULT_EXCLUDE,
3157
+ timeoutMs: 1500
3158
+ })
3159
+ });
3160
+ const out = /* @__PURE__ */ new Map();
3161
+ related.forEach((abs, index) => {
3162
+ out.set(normalizeAbs(abs), index);
3163
+ });
3164
+ return out;
3165
+ };
3166
+ var sortTestResultsWithRank = (rankByPath, results) => results.slice().sort(comparatorForRank(rankByPath));
3167
+ var comparatorForPathRank = (rankByPath) => {
3168
+ const rankOrInf = (absPath) => rankByPath.has(absPath) ? rankByPath.get(absPath) : Number.POSITIVE_INFINITY;
3169
+ return composeComparators(
3170
+ (left, right) => compareNumberAsc(rankOrInf(normalizeAbs(left)), rankOrInf(normalizeAbs(right))),
3171
+ (left, right) => compareStringAsc(normalizeAbs(left), normalizeAbs(right))
3172
+ );
3173
+ };
3174
+ var sortPathsWithRank = (rankByPath, paths) => paths.slice().sort(comparatorForPathRank(rankByPath));
3175
+
3094
3176
  // src/lib/coverage-print.ts
3095
3177
  var printDetailedCoverage = async (opts) => {
3096
3178
  const files = opts.map.files().sort((fileA, fileB) => {
@@ -3101,7 +3183,7 @@ var printDetailedCoverage = async (opts) => {
3101
3183
  for (const abs of files) {
3102
3184
  const fc = opts.map.fileCoverageFor(abs);
3103
3185
  const sum = fc.toSummary();
3104
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
3186
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
3105
3187
  const blocks = computeUncoveredBlocks(fc);
3106
3188
  const misses = missedBranches(fc);
3107
3189
  const missFns = missedFunctions(fc);
@@ -3110,9 +3192,9 @@ var printDetailedCoverage = async (opts) => {
3110
3192
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
3111
3193
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
3112
3194
  linesPctText
3113
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
3114
- sum.functions.pct
3115
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
3195
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
3196
+ funcsPctText
3197
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
3116
3198
  console.info(header);
3117
3199
  const max = opts.limitPerFile === "all" ? Number.POSITIVE_INFINITY : opts.limitPerFile ?? 5;
3118
3200
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
@@ -3139,10 +3221,8 @@ var printDetailedCoverage = async (opts) => {
3139
3221
  abs,
3140
3222
  block.start,
3141
3223
  opts.editorCmd
3142
- )}\x07${path7.basename(abs)}:${block.start}\x1B]8;;\x07`;
3143
- const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(
3144
- `L${block.end}`
3145
- )} ${link}`;
3224
+ )}\x07${path8.basename(abs)}:${block.start}\x1B]8;;\x07`;
3225
+ const label = ` ${ansi.yellow(`L${block.start}`)}\u2013${ansi.yellow(`L${block.end}`)} ${link}`;
3146
3226
  console.info(label);
3147
3227
  if (opts.showCode && src.length) {
3148
3228
  const lines = src.split(/\r?\n/);
@@ -3162,7 +3242,7 @@ var printDetailedCoverage = async (opts) => {
3162
3242
  abs,
3163
3243
  fn.line,
3164
3244
  opts.editorCmd
3165
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`;
3245
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`;
3166
3246
  console.info(` - ${fn.name} @ ${link}`);
3167
3247
  }
3168
3248
  }
@@ -3173,12 +3253,8 @@ var printDetailedCoverage = async (opts) => {
3173
3253
  abs,
3174
3254
  br.line,
3175
3255
  opts.editorCmd
3176
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`;
3177
- console.info(
3178
- ` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(
3179
- ", "
3180
- )}]`
3181
- );
3256
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`;
3257
+ console.info(` - branch#${br.id} @ ${link} missed paths: [${br.zeroPaths.join(", ")}]`);
3182
3258
  }
3183
3259
  }
3184
3260
  console.info("");
@@ -3198,7 +3274,7 @@ var printCompactCoverage = async (opts) => {
3198
3274
  for (const abs of files.slice(0, fileCap)) {
3199
3275
  const fc = opts.map.fileCoverageFor(abs);
3200
3276
  const sum = fc.toSummary();
3201
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
3277
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
3202
3278
  const compareRangesByLengthDescThenStart = (firstRange, secondRange) => {
3203
3279
  const secondLength = secondRange.end - secondRange.start;
3204
3280
  const firstLength = firstRange.end - firstRange.start;
@@ -3212,9 +3288,9 @@ var printCompactCoverage = async (opts) => {
3212
3288
  const branchesPctText = `${sum.branches.pct.toFixed(1)}%`;
3213
3289
  const header = `${ansi.bold(rel)} lines ${tintPct(sum.lines.pct)(
3214
3290
  linesPctText
3215
- )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(
3216
- sum.functions.pct
3217
- )(funcsPctText)} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
3291
+ )} ${barCell(compositeBarPct(sum, blocks))("".padEnd(14))} funcs ${tintPct(sum.functions.pct)(
3292
+ funcsPctText
3293
+ )} branches ${tintPct(sum.branches.pct)(branchesPctText)}`;
3218
3294
  console.info(header);
3219
3295
  const hotspots = blocks.slice(0, maxHotspotsDerived);
3220
3296
  if (hotspots.length) {
@@ -3225,10 +3301,8 @@ var printCompactCoverage = async (opts) => {
3225
3301
  abs,
3226
3302
  hotspot.start,
3227
3303
  opts.editorCmd
3228
- )}\x07${path7.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
3229
- console.info(
3230
- ` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`
3231
- );
3304
+ )}\x07${path8.basename(abs)}:${hotspot.start}\x1B]8;;\x07`;
3305
+ console.info(` - L${hotspot.start}\u2013L${hotspot.end} (${len} lines) ${link}`);
3232
3306
  }
3233
3307
  }
3234
3308
  const functionsList = missFns.slice(0, maxFunctionsDerived);
@@ -3240,7 +3314,7 @@ var printCompactCoverage = async (opts) => {
3240
3314
  abs,
3241
3315
  fn.line,
3242
3316
  opts.editorCmd
3243
- )}\x07${path7.basename(abs)}:${fn.line}\x1B]8;;\x07`
3317
+ )}\x07${path8.basename(abs)}:${fn.line}\x1B]8;;\x07`
3244
3318
  );
3245
3319
  }
3246
3320
  }
@@ -3255,7 +3329,7 @@ var printCompactCoverage = async (opts) => {
3255
3329
  abs,
3256
3330
  br.line,
3257
3331
  opts.editorCmd
3258
- )}\x07${path7.basename(abs)}:${br.line}\x1B]8;;\x07`
3332
+ )}\x07${path8.basename(abs)}:${br.line}\x1B]8;;\x07`
3259
3333
  );
3260
3334
  }
3261
3335
  }
@@ -3264,9 +3338,7 @@ var printCompactCoverage = async (opts) => {
3264
3338
  const restBrs = Math.max(0, misses.length - branchesList.length);
3265
3339
  if (restHs + restFns + restBrs > 0) {
3266
3340
  console.info(
3267
- ansi.dim(
3268
- ` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`
3269
- )
3341
+ ansi.dim(` \u2026 truncated: +${restHs} hotspots, +${restFns} funcs, +${restBrs} branches`)
3270
3342
  );
3271
3343
  }
3272
3344
  console.info("");
@@ -3303,7 +3375,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3303
3375
  return { stem: base.slice(0, -ending.length), ext: ending };
3304
3376
  }
3305
3377
  }
3306
- const ext2 = path7.extname(base);
3378
+ const ext2 = path8.extname(base);
3307
3379
  return { stem: base.slice(0, -ext2.length), ext: ext2 };
3308
3380
  };
3309
3381
  const sliceBalanced = (input, width) => {
@@ -3386,12 +3458,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3386
3458
  const tailParts = tailSrc.map((segment) => segment);
3387
3459
  let hidAny = false;
3388
3460
  const build = () => {
3389
- const label2 = joinParts(
3390
- headParts,
3391
- tailParts,
3392
- hideMiddle2 || hidAny,
3393
- baseLabel
3394
- );
3461
+ const label2 = joinParts(headParts, tailParts, hideMiddle2 || hidAny, baseLabel);
3395
3462
  return { label: label2, width: visibleWidth(label2) };
3396
3463
  };
3397
3464
  let { label, width } = build();
@@ -3484,13 +3551,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3484
3551
  return { headRaw: headRaw2, tailRaw: tailRaw2, hideMiddle: hideMiddle2 };
3485
3552
  };
3486
3553
  let { headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tailCount);
3487
- let candidate = tryTrimDirsToFit(
3488
- headRaw,
3489
- tailRaw,
3490
- hideMiddle,
3491
- baseFull,
3492
- maxWidth
3493
- );
3554
+ let candidate = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
3494
3555
  if (!candidate) {
3495
3556
  return baseFull;
3496
3557
  }
@@ -3499,13 +3560,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3499
3560
  if (headCount + tailCount < total) {
3500
3561
  const tryTail = Math.min(tailCount + 1, total - headCount);
3501
3562
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(headCount, tryTail));
3502
- const candTail = tryTrimDirsToFit(
3503
- headRaw,
3504
- tailRaw,
3505
- hideMiddle,
3506
- baseFull,
3507
- maxWidth
3508
- );
3563
+ const candTail = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
3509
3564
  if (candTail) {
3510
3565
  tailCount = tryTail;
3511
3566
  candidate = candTail;
@@ -3515,13 +3570,7 @@ var shortenPathPreservingFilename = (relPath, maxWidth, opts) => {
3515
3570
  if (!advanced && headCount + tailCount < total) {
3516
3571
  const tryHead = Math.min(headCount + 1, total - tailCount);
3517
3572
  ({ headRaw, tailRaw, hideMiddle } = buildRaw(tryHead, tailCount));
3518
- const candHead = tryTrimDirsToFit(
3519
- headRaw,
3520
- tailRaw,
3521
- hideMiddle,
3522
- baseFull,
3523
- maxWidth
3524
- );
3573
+ const candHead = tryTrimDirsToFit(headRaw, tailRaw, hideMiddle, baseFull, maxWidth);
3525
3574
  if (candHead) {
3526
3575
  headCount = tryHead;
3527
3576
  candidate = candHead;
@@ -3583,7 +3632,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
3583
3632
  const queue = [];
3584
3633
  const seen = /* @__PURE__ */ new Set();
3585
3634
  for (const testAbs of executedTestsAbs) {
3586
- const testPathNormalized = path7.resolve(testAbs).replace(/\\/g, "/");
3635
+ const testPathNormalized = path8.resolve(testAbs).replace(/\\/g, "/");
3587
3636
  dist.set(testPathNormalized, 0);
3588
3637
  queue.push([testPathNormalized, 0]);
3589
3638
  }
@@ -3602,12 +3651,7 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
3602
3651
  const specs = await extractImportSpecs2(currentFile, specsCache);
3603
3652
  const nextDistance = currentDistance + 1;
3604
3653
  for (const spec of specs) {
3605
- const resolved = resolveImportWithRoot(
3606
- currentFile,
3607
- spec,
3608
- rootDir,
3609
- resolutionCache
3610
- );
3654
+ const resolved = resolveImportWithRoot(currentFile, spec, rootDir, resolutionCache);
3611
3655
  const usable = resolved && !resolved.includes("/node_modules/");
3612
3656
  if (usable) {
3613
3657
  const existing = dist.get(resolved);
@@ -3622,13 +3666,10 @@ var buildDistanceMapFromTests = async (executedTestsAbs, rootDir) => {
3622
3666
  return dist;
3623
3667
  };
3624
3668
  var renderPerFileCompositeTable = async (opts) => {
3625
- const rel = path7.relative(opts.root, opts.absPath).replace(/\\/g, "/");
3669
+ const rel = path8.relative(opts.root, opts.absPath).replace(/\\/g, "/");
3626
3670
  const sum = opts.file.toSummary();
3627
3671
  const rowsAvail = typeof process.stdout.rows === "number" && process.stdout.rows > 10 ? process.stdout.rows : 40;
3628
- const tableBudget = Math.max(
3629
- 14,
3630
- Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8)
3631
- );
3672
+ const tableBudget = Math.max(14, Math.min(opts.maxRows ?? rowsAvail - 1, rowsAvail + 8));
3632
3673
  const rowBudget = Math.max(6, tableBudget - 6);
3633
3674
  const blocks = computeUncoveredBlocks(opts.file).slice().sort((firstRange, secondRange) => {
3634
3675
  const firstLength = firstRange.end - firstRange.start;
@@ -3672,9 +3713,7 @@ var renderPerFileCompositeTable = async (opts) => {
3672
3713
  rows.push([
3673
3714
  cell(
3674
3715
  rel,
3675
- (padded) => ansi.dim(
3676
- shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length)
3677
- )
3716
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
3678
3717
  ),
3679
3718
  cell("Totals", ansi.dim),
3680
3719
  cell("\u2014", ansi.dim),
@@ -3693,11 +3732,7 @@ var renderPerFileCompositeTable = async (opts) => {
3693
3732
  rows.push([
3694
3733
  cell(
3695
3734
  rel,
3696
- (padded) => ansi.dim(
3697
- shortenPathPreservingFilename(rel, padded.length).padEnd(
3698
- padded.length
3699
- )
3700
- )
3735
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
3701
3736
  ),
3702
3737
  cell("Hotspots", ansi.dim),
3703
3738
  cell("", ansi.dim),
@@ -3711,14 +3746,8 @@ var renderPerFileCompositeTable = async (opts) => {
3711
3746
  rows.push([
3712
3747
  cell(rel, (padded) => {
3713
3748
  const width = padded.length;
3714
- const display = shortenPathPreservingFilename(rel, width).padEnd(
3715
- width
3716
- );
3717
- return linkifyPadded(
3718
- opts.absPath,
3719
- hotspotRange.start,
3720
- opts.editorCmd
3721
- )(display);
3749
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
3750
+ return linkifyPadded(opts.absPath, hotspotRange.start, opts.editorCmd)(display);
3722
3751
  }),
3723
3752
  cell("Hotspot"),
3724
3753
  cell(`L${hotspotRange.start}\u2013L${hotspotRange.end}`),
@@ -3735,11 +3764,7 @@ var renderPerFileCompositeTable = async (opts) => {
3735
3764
  rows.push([
3736
3765
  cell(
3737
3766
  rel,
3738
- (padded) => ansi.dim(
3739
- shortenPathPreservingFilename(rel, padded.length).padEnd(
3740
- padded.length
3741
- )
3742
- )
3767
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
3743
3768
  ),
3744
3769
  cell("Functions", ansi.dim),
3745
3770
  cell("", ansi.dim),
@@ -3753,14 +3778,8 @@ var renderPerFileCompositeTable = async (opts) => {
3753
3778
  rows.push([
3754
3779
  cell(rel, (padded) => {
3755
3780
  const width = padded.length;
3756
- const display = shortenPathPreservingFilename(rel, width).padEnd(
3757
- width
3758
- );
3759
- return linkifyPadded(
3760
- opts.absPath,
3761
- missedFunction.line,
3762
- opts.editorCmd
3763
- )(display);
3781
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
3782
+ return linkifyPadded(opts.absPath, missedFunction.line, opts.editorCmd)(display);
3764
3783
  }),
3765
3784
  cell("Func"),
3766
3785
  cell(`L${missedFunction.line}`),
@@ -3777,11 +3796,7 @@ var renderPerFileCompositeTable = async (opts) => {
3777
3796
  rows.push([
3778
3797
  cell(
3779
3798
  rel,
3780
- (padded) => ansi.dim(
3781
- shortenPathPreservingFilename(rel, padded.length).padEnd(
3782
- padded.length
3783
- )
3784
- )
3799
+ (padded) => ansi.dim(shortenPathPreservingFilename(rel, padded.length).padEnd(padded.length))
3785
3800
  ),
3786
3801
  cell("Branches", ansi.dim),
3787
3802
  cell("", ansi.dim),
@@ -3795,14 +3810,8 @@ var renderPerFileCompositeTable = async (opts) => {
3795
3810
  rows.push([
3796
3811
  cell(rel, (padded) => {
3797
3812
  const width = padded.length;
3798
- const display = shortenPathPreservingFilename(rel, width).padEnd(
3799
- width
3800
- );
3801
- return linkifyPadded(
3802
- opts.absPath,
3803
- missedBranch.line,
3804
- opts.editorCmd
3805
- )(display);
3813
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
3814
+ return linkifyPadded(opts.absPath, missedBranch.line, opts.editorCmd)(display);
3806
3815
  }),
3807
3816
  cell("Branch"),
3808
3817
  cell(`L${missedBranch.line}`),
@@ -3810,9 +3819,7 @@ var renderPerFileCompositeTable = async (opts) => {
3810
3819
  cell(""),
3811
3820
  cell(""),
3812
3821
  cell(""),
3813
- cell(
3814
- `#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`
3815
- )
3822
+ cell(`#${missedBranch.id} missed [${missedBranch.zeroPaths.join(", ")}]`)
3816
3823
  ]);
3817
3824
  }
3818
3825
  }
@@ -3832,9 +3839,7 @@ var renderPerFileCompositeTable = async (opts) => {
3832
3839
  rows.push([
3833
3840
  cell(rel, (padded) => {
3834
3841
  const width = padded.length;
3835
- const display = shortenPathPreservingFilename(rel, width).padEnd(
3836
- width
3837
- );
3842
+ const display = shortenPathPreservingFilename(rel, width).padEnd(width);
3838
3843
  return linkifyPadded(opts.absPath, ln, opts.editorCmd)(display);
3839
3844
  }),
3840
3845
  cell("Line"),
@@ -3847,16 +3852,7 @@ var renderPerFileCompositeTable = async (opts) => {
3847
3852
  ]);
3848
3853
  }
3849
3854
  while (rows.length < target) {
3850
- rows.push([
3851
- cell(""),
3852
- cell(""),
3853
- cell(""),
3854
- cell(""),
3855
- cell(""),
3856
- cell(""),
3857
- cell(""),
3858
- cell("")
3859
- ]);
3855
+ rows.push([cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell(""), cell("")]);
3860
3856
  }
3861
3857
  }
3862
3858
  }
@@ -3864,20 +3860,17 @@ var renderPerFileCompositeTable = async (opts) => {
3864
3860
  console.info(table);
3865
3861
  const sep = ansi.gray(
3866
3862
  "\u2500".repeat(
3867
- Math.max(
3868
- 20,
3869
- typeof process.stdout.columns === "number" ? process.stdout.columns : 100
3870
- )
3863
+ Math.max(20, typeof process.stdout.columns === "number" ? process.stdout.columns : 100)
3871
3864
  )
3872
3865
  );
3873
3866
  console.info(sep);
3874
3867
  };
3875
3868
  var printPerFileCompositeTables = async (opts) => {
3876
3869
  const selectionAbs = (opts.selectionPaths ?? []).map(
3877
- (selPath) => path7.resolve(selPath).replace(/\\/g, "/")
3870
+ (selPath) => path8.resolve(selPath).replace(/\\/g, "/")
3878
3871
  );
3879
3872
  const changedAbs = (opts.changedFiles ?? []).map(
3880
- (chgPath) => path7.resolve(chgPath).replace(/\\/g, "/")
3873
+ (chgPath) => path8.resolve(chgPath).replace(/\\/g, "/")
3881
3874
  );
3882
3875
  const tokenizeForSimilarity = (filePathForTokens) => new Set(
3883
3876
  filePathForTokens.toLowerCase().replace(/[^a-z0-9/_\-.]/g, " ").split(/[/_.-]+/).filter(Boolean)
@@ -3893,15 +3886,15 @@ var printPerFileCompositeTables = async (opts) => {
3893
3886
  return intersectionCount / unionSize;
3894
3887
  };
3895
3888
  const isSameDirOrChild = (firstAbs, secondAbs) => {
3896
- const dirA = path7.dirname(firstAbs).replace(/\\/g, "/");
3897
- const dirB = path7.dirname(secondAbs).replace(/\\/g, "/");
3889
+ const dirA = path8.dirname(firstAbs).replace(/\\/g, "/");
3890
+ const dirB = path8.dirname(secondAbs).replace(/\\/g, "/");
3898
3891
  return dirA === dirB || dirB.startsWith(`${dirA}/`) || dirA.startsWith(`${dirB}/`);
3899
3892
  };
3900
3893
  const selectionTokens = selectionAbs.map(tokenizeForSimilarity);
3901
3894
  const changedTokens = changedAbs.map(tokenizeForSimilarity);
3902
- const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path7.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
3895
+ const executedTestsAbs = (opts.executedTests ?? []).map((testPath) => path8.resolve(testPath).replace(/\\/g, "/")).filter((absPath) => absPath.length > 0);
3903
3896
  const testTokens = executedTestsAbs.map(tokenizeForSimilarity);
3904
- const allMapFilesAbs = opts.map.files().map((absPath) => path7.resolve(absPath).replace(/\\/g, "/"));
3897
+ const allMapFilesAbs = opts.map.files().map((absPath) => path8.resolve(absPath).replace(/\\/g, "/"));
3905
3898
  const uncoveredCandidates = allMapFilesAbs.filter((absPath) => {
3906
3899
  const sum = opts.map.fileCoverageFor(absPath).toSummary();
3907
3900
  return !(sum.lines.pct >= 100 && sum.functions.pct >= 100 && sum.branches.pct >= 100);
@@ -3918,33 +3911,24 @@ var printPerFileCompositeTables = async (opts) => {
3918
3911
  const selectionSetAbs = new Set(selectionAbs);
3919
3912
  const scored = await Promise.all(
3920
3913
  candidates.map(async (abs) => {
3921
- const rel = path7.relative(opts.root, abs).replace(/\\/g, "/");
3914
+ const rel = path8.relative(opts.root, abs).replace(/\\/g, "/");
3922
3915
  const sum = opts.map.fileCoverageFor(abs).toSummary();
3923
3916
  const pct = Number.isFinite(sum.lines.pct) ? sum.lines.pct : 0;
3924
- const absNorm = path7.resolve(abs).replace(/\\/g, "/");
3917
+ const absNorm = path8.resolve(abs).replace(/\\/g, "/");
3925
3918
  const selfTokens = tokenizeForSimilarity(absNorm);
3926
3919
  const selSim = Math.max(
3927
3920
  0,
3928
- ...selectionTokens.map(
3929
- (selectionTokenSet) => jaccard(selfTokens, selectionTokenSet)
3930
- )
3921
+ ...selectionTokens.map((selectionTokenSet) => jaccard(selfTokens, selectionTokenSet))
3931
3922
  );
3932
3923
  const chgSim = Math.max(
3933
3924
  0,
3934
- ...changedTokens.map(
3935
- (changedTokenSet) => jaccard(selfTokens, changedTokenSet)
3936
- )
3937
- );
3938
- const tstSim = Math.max(
3939
- 0,
3940
- ...testTokens.map((tset) => jaccard(selfTokens, tset))
3925
+ ...changedTokens.map((changedTokenSet) => jaccard(selfTokens, changedTokenSet))
3941
3926
  );
3927
+ const tstSim = Math.max(0, ...testTokens.map((tset) => jaccard(selfTokens, tset)));
3942
3928
  const nearSelection = selectionAbs.some(
3943
3929
  (selectionPath) => isSameDirOrChild(absNorm, selectionPath)
3944
3930
  );
3945
- const nearChanged = changedAbs.some(
3946
- (changedPath) => isSameDirOrChild(absNorm, changedPath)
3947
- );
3931
+ const nearChanged = changedAbs.some((changedPath) => isSameDirOrChild(absNorm, changedPath));
3948
3932
  const related = selSim > 0 || chgSim > 0 || nearSelection || nearChanged;
3949
3933
  const distance = selectionSetAbs.has(absNorm) ? 0 : distFromTests.get(absNorm) ?? INF;
3950
3934
  let group = 6;
@@ -3968,9 +3952,12 @@ var printPerFileCompositeTables = async (opts) => {
3968
3952
  return { abs: absNorm, rel, linesPct: pct, group, score, distance };
3969
3953
  })
3970
3954
  );
3971
- let files = scored.sort(
3972
- (left, right) => left.group - right.group || left.distance - right.distance || right.score - left.score || right.linesPct - left.linesPct || left.rel.localeCompare(right.rel)
3973
- ).map((scoredItem) => scoredItem.abs);
3955
+ const prodSeeds = selectionAbs.length > 0 ? selectionAbs : changedAbs;
3956
+ const rank = await computeDirectnessRank({ repoRoot: opts.root, productionSeeds: prodSeeds });
3957
+ let files = sortPathsWithRank(
3958
+ rank,
3959
+ scored.map((s) => s.abs)
3960
+ );
3974
3961
  if (selectionAbs.length > 0) {
3975
3962
  const selectionSet = new Set(selectionAbs);
3976
3963
  const selectedHead = files.filter((filePath) => selectionSet.has(filePath));
@@ -3993,7 +3980,7 @@ var printPerFileCompositeTables = async (opts) => {
3993
3980
  };
3994
3981
 
3995
3982
  // src/lib/jest-bridge.ts
3996
- var path8 = __toESM(require("node:path"), 1);
3983
+ var path9 = __toESM(require("node:path"), 1);
3997
3984
  var fs4 = __toESM(require("node:fs"), 1);
3998
3985
  var util = __toESM(require("node:util"), 1);
3999
3986
  var import_json5 = __toESM(require_lib(), 1);
@@ -4068,7 +4055,7 @@ var extractBridgePath = (raw, cwd) => {
4068
4055
  return null;
4069
4056
  }
4070
4057
  const jsonPath = matches[matches.length - 1][1].trim().replace(/^["'`]|["'`]$/g, "");
4071
- return path8.isAbsolute(jsonPath) ? jsonPath : path8.resolve(cwd, jsonPath);
4058
+ return path9.isAbsolute(jsonPath) ? jsonPath : path9.resolve(cwd, jsonPath);
4072
4059
  };
4073
4060
  var drawRule = (label) => {
4074
4061
  const width = Math.max(
@@ -4667,7 +4654,7 @@ var buildStackSection = (mergedForStack, ctx, fallbackLoc) => {
4667
4654
  const loc = deepestProjectLoc(mergedForStack, ctx.projectHint);
4668
4655
  if (loc) {
4669
4656
  const href = preferredEditorHref(loc.file, loc.line, ctx.editorCmd);
4670
- out.push(` ${ansi.dim("at")} ${osc8(`${path8.basename(loc.file)}:${loc.line}`, href)}`);
4657
+ out.push(` ${ansi.dim("at")} ${osc8(`${path9.basename(loc.file)}:${loc.line}`, href)}`);
4671
4658
  }
4672
4659
  out.push("");
4673
4660
  return out;
@@ -4946,7 +4933,7 @@ function renderVitestFromJestJSON(data, opts) {
4946
4933
  const deepestLoc = deepestProjectLoc(mergedForStack, projectHint);
4947
4934
  const locLink = deepestLoc && (() => {
4948
4935
  const href = preferredEditorHref(deepestLoc.file, deepestLoc.line, opts?.editorCmd);
4949
- const base = `${path8.basename(deepestLoc.file)}:${deepestLoc.line}`;
4936
+ const base = `${path9.basename(deepestLoc.file)}:${deepestLoc.line}`;
4950
4937
  return osc8(base, href);
4951
4938
  })();
4952
4939
  const headerLine = `${ansi.white(header)}${locLink ? ` ${ansi.dim(`(${locLink})`)}` : ""}`;
@@ -4991,13 +4978,8 @@ ${footer}`;
4991
4978
  }
4992
4979
 
4993
4980
  // src/lib/program.ts
4994
- var import_meta = {};
4995
4981
  var jestBin = "./node_modules/.bin/jest";
4996
4982
  var babelNodeBin = "./node_modules/.bin/babel-node";
4997
- var moduleSpecifierForRequire = (
4998
- // @ts-ignore
4999
- typeof __filename !== "undefined" ? __filename : import_meta.url
5000
- );
5001
4983
  var registerSignalHandlersOnce = () => {
5002
4984
  let handled = false;
5003
4985
  const on = (sig) => {
@@ -5015,7 +4997,6 @@ Received ${sig}, exiting...
5015
4997
  };
5016
4998
  var isDebug = () => Boolean(process.env.TEST_CLI_DEBUG);
5017
4999
  var mergeLcov = async () => {
5018
- const jestLcovPath = "coverage/jest/lcov.info";
5019
5000
  const vitestLcovPath = "coverage/vitest/lcov.info";
5020
5001
  const mergedOutPath = "coverage/lcov.info";
5021
5002
  const readOrEmpty = async (filePath) => {
@@ -5026,7 +5007,7 @@ var mergeLcov = async () => {
5026
5007
  }
5027
5008
  };
5028
5009
  let vitestContent = "";
5029
- let jestContent = "";
5010
+ const jestParts = [];
5030
5011
  try {
5031
5012
  vitestContent = await readOrEmpty(vitestLcovPath);
5032
5013
  } catch (readVitestError) {
@@ -5034,20 +5015,46 @@ var mergeLcov = async () => {
5034
5015
  console.info(`read vitest lcov failed: ${String(readVitestError)}`);
5035
5016
  }
5036
5017
  }
5018
+ const collectLcovs = (dir) => {
5019
+ const out = [];
5020
+ try {
5021
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
5022
+ for (const entry of entries) {
5023
+ const full = path10.join(dir, entry.name);
5024
+ if (entry.isDirectory()) {
5025
+ out.push(...collectLcovs(full));
5026
+ } else if (entry.isFile() && entry.name === "lcov.info") {
5027
+ out.push(full);
5028
+ }
5029
+ }
5030
+ } catch {
5031
+ }
5032
+ return out;
5033
+ };
5037
5034
  try {
5038
- jestContent = await readOrEmpty(jestLcovPath);
5035
+ const jestRoot = path10.join("coverage", "jest");
5036
+ const candidates = [path10.join(jestRoot, "lcov.info"), ...collectLcovs(jestRoot)].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => arr.indexOf(absolutePath) === index);
5037
+ for (const filePath of candidates) {
5038
+ try {
5039
+ const content = await readOrEmpty(filePath);
5040
+ if (content.trim()) {
5041
+ jestParts.push(content.trim());
5042
+ }
5043
+ } catch {
5044
+ }
5045
+ }
5039
5046
  } catch (readJestError) {
5040
5047
  if (isDebug()) {
5041
- console.info(`read jest lcov failed: ${String(readJestError)}`);
5048
+ console.info(`scan jest lcov failed: ${String(readJestError)}`);
5042
5049
  }
5043
5050
  }
5044
- if (!vitestContent && !jestContent) {
5051
+ if (!vitestContent && jestParts.length === 0) {
5045
5052
  if (isDebug()) {
5046
5053
  console.info("No coverage outputs found to merge.");
5047
5054
  }
5048
5055
  return;
5049
5056
  }
5050
- const merged = [vitestContent.trim(), jestContent.trim()].filter(Boolean).join("\n");
5057
+ const merged = [vitestContent.trim(), ...jestParts].filter(Boolean).join("\n");
5051
5058
  if (merged.length > 0) {
5052
5059
  await (await import("node:fs/promises")).mkdir("coverage", { recursive: true });
5053
5060
  await (await import("node:fs/promises")).writeFile(mergedOutPath, `${merged}
@@ -5060,23 +5067,40 @@ var mergeLcov = async () => {
5060
5067
  }
5061
5068
  };
5062
5069
  var emitMergedCoverage = async (ui, opts) => {
5063
- const jestJson = path9.join("coverage", "jest", "coverage-final.json");
5064
- const jSize = fsSync3.existsSync(jestJson) ? fsSync3.statSync(jestJson).size : -1;
5065
- const jestSizeLabel = jSize >= 0 ? `${jSize} bytes` : "missing";
5066
- if (isDebug()) {
5067
- console.info(`Coverage JSON probe \u2192 jest: ${jestSizeLabel}`);
5068
- }
5069
- const jestData = await readCoverageJson(jestJson);
5070
- const jestFilesCount = Object.keys(jestData).length;
5071
- if (isDebug()) {
5072
- console.info(`Decoded coverage entries \u2192 jest: ${jestFilesCount}`);
5073
- }
5074
5070
  const map = (0, import_istanbul_lib_coverage2.createCoverageMap)({});
5075
- if (jestFilesCount > 0) {
5071
+ const listJsons = (dir) => {
5072
+ const out = [];
5076
5073
  try {
5077
- map.merge(jestData);
5074
+ const entries = fsSync3.readdirSync(dir, { withFileTypes: true });
5075
+ for (const entry of entries) {
5076
+ const full = path10.join(dir, entry.name);
5077
+ if (entry.isDirectory()) {
5078
+ out.push(...listJsons(full));
5079
+ } else if (entry.isFile() && entry.name === "coverage-final.json") {
5080
+ out.push(full);
5081
+ }
5082
+ }
5083
+ } catch {
5084
+ }
5085
+ return out;
5086
+ };
5087
+ const coverageRoot = path10.join("coverage", "jest");
5088
+ const jsonCandidates = [
5089
+ path10.join(coverageRoot, "coverage-final.json"),
5090
+ ...listJsons(coverageRoot)
5091
+ ].map((candidatePath) => path10.resolve(candidatePath)).filter((absolutePath, index, arr) => {
5092
+ const isFirst = arr.indexOf(absolutePath) === index;
5093
+ const exists = fsSync3.existsSync(absolutePath);
5094
+ return isFirst && exists;
5095
+ });
5096
+ for (const jsonPath of jsonCandidates) {
5097
+ try {
5098
+ const data = await readCoverageJson(jsonPath);
5099
+ if (Object.keys(data).length) {
5100
+ map.merge(data);
5101
+ }
5078
5102
  } catch (mergeJestError) {
5079
- console.warn(`Failed merging jest coverage JSON: ${String(mergeJestError)}`);
5103
+ console.warn(`Failed merging jest coverage JSON @ ${jsonPath}: ${String(mergeJestError)}`);
5080
5104
  }
5081
5105
  }
5082
5106
  if (map.files().length === 0) {
@@ -5126,7 +5150,7 @@ var emitMergedCoverage = async (ui, opts) => {
5126
5150
  executedTests: opts.executedTests ?? []
5127
5151
  });
5128
5152
  const context = LibReport.createContext({
5129
- dir: path9.resolve("coverage", "merged"),
5153
+ dir: path10.resolve("coverage", "merged"),
5130
5154
  coverageMap: filteredMap,
5131
5155
  defaultSummarizer: "nested"
5132
5156
  });
@@ -5194,8 +5218,8 @@ var emitMergedCoverage = async (ui, opts) => {
5194
5218
  for (const reporter of reporters) {
5195
5219
  reporter.execute(context);
5196
5220
  }
5197
- const textPath = path9.resolve("coverage", "merged", "coverage.txt");
5198
- const summaryPath = path9.resolve("coverage", "merged", "coverage-summary.txt");
5221
+ const textPath = path10.resolve("coverage", "merged", "coverage.txt");
5222
+ const summaryPath = path10.resolve("coverage", "merged", "coverage-summary.txt");
5199
5223
  const filesToPrint = [];
5200
5224
  if (fsSync3.existsSync(textPath)) {
5201
5225
  filesToPrint.push(textPath);
@@ -5277,17 +5301,43 @@ var program = async () => {
5277
5301
  coverageMode,
5278
5302
  coverageMaxFiles: coverageMaxFilesArg,
5279
5303
  coverageMaxHotspots: coverageMaxHotspotsArg,
5280
- coveragePageFit
5304
+ coveragePageFit,
5305
+ changed
5281
5306
  } = deriveArgs(argv);
5282
- console.info(`Selection \u2192 specified=${selectionSpecified} paths=${selectionPaths.length}`);
5307
+ const getChangedFiles = async (mode, cwd) => {
5308
+ const collect = async (cmd, args) => {
5309
+ try {
5310
+ const out = await runText(cmd, args, {
5311
+ cwd,
5312
+ env: safeEnv(process.env, {}),
5313
+ timeoutMs: 4e3
5314
+ });
5315
+ return out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
5316
+ } catch {
5317
+ return [];
5318
+ }
5319
+ };
5320
+ const staged = mode === "staged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB", "--cached"]) : [];
5321
+ const unstagedTracked = mode === "unstaged" || mode === "all" ? await collect("git", ["diff", "--name-only", "--diff-filter=ACMRTUXB"]) : [];
5322
+ const untracked = mode === "unstaged" || mode === "all" ? await collect("git", ["ls-files", "--others", "--exclude-standard"]) : [];
5323
+ const rels = Array.from(/* @__PURE__ */ new Set([...staged, ...unstagedTracked, ...untracked]));
5324
+ return rels.map((rel) => path10.resolve(cwd, rel).replace(/\\/g, "/")).filter((abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/"));
5325
+ };
5326
+ const repoRootForChanged = workspaceRoot ?? await findRepoRoot();
5327
+ const changedSelectionAbs = changed ? await getChangedFiles(changed, repoRootForChanged) : [];
5328
+ const selectionPathsAugmented = changedSelectionAbs.length ? Array.from(/* @__PURE__ */ new Set([...selectionPaths, ...changedSelectionAbs])) : selectionPaths;
5329
+ const selectionSpecifiedAugmented = Boolean(selectionSpecified || changedSelectionAbs.length > 0);
5330
+ console.info(
5331
+ `Selection \u2192 specified=${selectionSpecifiedAugmented} paths=${selectionPathsAugmented.length}`
5332
+ );
5283
5333
  const { jest } = argsForDiscovery(["run"], jestArgs);
5284
- const selectionLooksLikeTest = selectionPaths.some(
5334
+ const selectionLooksLikeTest = selectionPathsAugmented.some(
5285
5335
  (pathText) => /\.(test|spec)\.[tj]sx?$/i.test(pathText) || /(^|\/)tests?\//i.test(pathText)
5286
5336
  );
5287
- const selectionLooksLikePath = selectionPaths.some(
5337
+ const selectionLooksLikePath = selectionPathsAugmented.some(
5288
5338
  (pathText) => /[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)
5289
5339
  );
5290
- const selectionHasPaths = selectionPaths.length > 0;
5340
+ const selectionHasPaths = selectionPathsAugmented.length > 0;
5291
5341
  const repoRootForDiscovery = workspaceRoot ?? await findRepoRoot();
5292
5342
  const expandProductionSelections = async (tokens, repoRoot) => {
5293
5343
  const results = /* @__PURE__ */ new Set();
@@ -5296,18 +5346,18 @@ var program = async () => {
5296
5346
  if (!token) {
5297
5347
  continue;
5298
5348
  }
5299
- const isAbs = path9.isAbsolute(token);
5349
+ const isAbs = path10.isAbsolute(token);
5300
5350
  const looksLikeRelPath = /[\\/]/.test(token);
5301
5351
  let candidateFromRoot;
5302
5352
  if (token.startsWith("/")) {
5303
- candidateFromRoot = path9.join(repoRoot, token.slice(1));
5353
+ candidateFromRoot = path10.join(repoRoot, token.slice(1));
5304
5354
  } else if (looksLikeRelPath) {
5305
- candidateFromRoot = path9.join(repoRoot, token);
5355
+ candidateFromRoot = path10.join(repoRoot, token);
5306
5356
  } else {
5307
5357
  candidateFromRoot = void 0;
5308
5358
  }
5309
5359
  const tryPushIfProd = (absPath) => {
5310
- const norm = path9.resolve(absPath).replace(/\\/g, "/");
5360
+ const norm = path10.resolve(absPath).replace(/\\/g, "/");
5311
5361
  const isTest = /(^|\/)tests?\//i.test(norm) || /\.(test|spec)\.[tj]sx?$/i.test(norm);
5312
5362
  if (!isTest && fsSync3.existsSync(norm)) {
5313
5363
  results.add(norm);
@@ -5329,7 +5379,7 @@ var program = async () => {
5329
5379
  }),
5330
5380
  timeoutMs: 4e3
5331
5381
  });
5332
- const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path9.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
5382
+ const matches = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((rel) => path10.resolve(repoRoot, rel).replace(/\\/g, "/")).filter(
5333
5383
  (abs) => !abs.includes("/node_modules/") && !abs.includes("/coverage/") && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
5334
5384
  );
5335
5385
  matches.forEach((abs) => results.add(abs));
@@ -5338,20 +5388,20 @@ var program = async () => {
5338
5388
  }
5339
5389
  return Array.from(results);
5340
5390
  };
5341
- const initialProdSelections = selectionPaths.filter(
5391
+ const initialProdSelections = selectionPathsAugmented.filter(
5342
5392
  (pathText) => (/[\\/]/.test(pathText) || /\.(m?[tj]sx?)$/i.test(pathText)) && !/(^|\/)tests?\//i.test(pathText) && !/\.(test|spec)\.[tj]sx?$/i.test(pathText)
5343
5393
  );
5344
- const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPaths, repoRootForDiscovery);
5394
+ const expandedProdSelections = initialProdSelections.length ? initialProdSelections : await expandProductionSelections(selectionPathsAugmented, repoRootForDiscovery);
5345
5395
  const selectionIncludesProdPaths = expandedProdSelections.length > 0;
5346
5396
  console.info(
5347
5397
  `Selection classify \u2192 looksLikePath=${selectionLooksLikePath} looksLikeTest=${selectionLooksLikeTest} prodPaths=${selectionIncludesProdPaths}`
5348
5398
  );
5349
- const stripPathTokens = (args) => args.filter((token) => !selectionPaths.includes(token));
5399
+ const stripPathTokens = (args) => args.filter((token) => !selectionPathsAugmented.includes(token));
5350
5400
  const jestDiscoveryArgs = selectionIncludesProdPaths ? stripPathTokens(jest) : jest;
5351
5401
  const projectConfigs = [];
5352
5402
  try {
5353
- const baseCfg = path9.resolve("jest.config.js");
5354
- const tsCfg = path9.resolve("jest.ts.config.js");
5403
+ const baseCfg = path10.resolve("jest.config.js");
5404
+ const tsCfg = path10.resolve("jest.ts.config.js");
5355
5405
  if (fsSync3.existsSync(baseCfg)) {
5356
5406
  projectConfigs.push(baseCfg);
5357
5407
  }
@@ -5368,7 +5418,7 @@ var program = async () => {
5368
5418
  );
5369
5419
  const prodSelections2 = expandedProdSelections;
5370
5420
  for (const cfg of projectConfigs) {
5371
- const cfgCwd = path9.dirname(cfg);
5421
+ const cfgCwd = path10.dirname(cfg);
5372
5422
  const allTests = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
5373
5423
  cwd: cfgCwd
5374
5424
  });
@@ -5381,7 +5431,7 @@ var program = async () => {
5381
5431
  });
5382
5432
  } catch (err) {
5383
5433
  if (isDebug()) {
5384
- console.warn(`direct selection failed for project ${path9.basename(cfg)}: ${String(err)}`);
5434
+ console.warn(`direct selection failed for project ${path10.basename(cfg)}: ${String(err)}`);
5385
5435
  }
5386
5436
  }
5387
5437
  perProjectFiles.set(cfg, directPerProject);
@@ -5393,7 +5443,7 @@ var program = async () => {
5393
5443
  )} | related=${selectionIncludesProdPaths} | cwd=${repoRootForDiscovery}`
5394
5444
  );
5395
5445
  for (const cfg of projectConfigs) {
5396
- const cfgCwd = path9.dirname(cfg);
5446
+ const cfgCwd = path10.dirname(cfg);
5397
5447
  const files = await discoverJestResilient([...jestDiscoveryArgs, "--config", cfg], {
5398
5448
  cwd: cfgCwd
5399
5449
  });
@@ -5403,18 +5453,18 @@ var program = async () => {
5403
5453
  const perProjectFiltered = /* @__PURE__ */ new Map();
5404
5454
  for (const cfg of projectConfigs) {
5405
5455
  const files = perProjectFiles.get(cfg) ?? [];
5406
- const selectionTestPaths = selectionPaths.filter(
5456
+ const selectionTestPaths = selectionPathsAugmented.filter(
5407
5457
  (pathToken) => /\.(test|spec)\.[tj]sx?$/i.test(pathToken) || /(^|\/)tests?\//i.test(pathToken)
5408
5458
  );
5409
5459
  const candidates = selectionHasPaths && selectionLooksLikeTest ? selectionTestPaths : files;
5410
5460
  const absFiles = candidates.map(
5411
- (candidatePath) => path9.isAbsolute(candidatePath) ? candidatePath : path9.join(repoRootForDiscovery, candidatePath)
5461
+ (candidatePath) => path10.isAbsolute(candidatePath) ? candidatePath : path10.join(repoRootForDiscovery, candidatePath)
5412
5462
  ).map((absolutePath) => absolutePath.replace(/\\/g, "/"));
5413
5463
  const onlyOwned = await filterCandidatesForProject(
5414
5464
  cfg,
5415
5465
  jestDiscoveryArgs,
5416
5466
  absFiles,
5417
- path9.dirname(cfg)
5467
+ path10.dirname(cfg)
5418
5468
  );
5419
5469
  perProjectFiltered.set(cfg, onlyOwned);
5420
5470
  }
@@ -5426,7 +5476,7 @@ var program = async () => {
5426
5476
  if (selectionHasPaths && prodSelections.length > 0) {
5427
5477
  console.info(`rg related \u2192 prodSelections=${prodSelections.length} (starting)`);
5428
5478
  const repoRootForRefinement = workspaceRoot ?? await findRepoRoot();
5429
- const selectionKey = prodSelections.map((absPath) => path9.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((a, b) => a.localeCompare(b)).join("|");
5479
+ const selectionKey = prodSelections.map((absPath) => path10.relative(repoRootForRefinement, absPath).replace(/\\/g, "/")).sort((firstPath, secondPath) => firstPath.localeCompare(secondPath)).join("|");
5430
5480
  const { cachedRelated: cachedRelated2, findRelatedTestsFast: findRelatedTestsFast2, DEFAULT_TEST_GLOBS: DEFAULT_TEST_GLOBS2 } = await Promise.resolve().then(() => (init_fast_related(), fast_related_exports));
5431
5481
  const { DEFAULT_EXCLUDE: DEFAULT_EXCLUDE2 } = await Promise.resolve().then(() => (init_args(), args_exports));
5432
5482
  const rgMatches = await cachedRelated2({
@@ -5456,7 +5506,7 @@ var program = async () => {
5456
5506
  cfg,
5457
5507
  jestDiscoveryArgs,
5458
5508
  rgCandidates,
5459
- path9.dirname(cfg)
5509
+ path10.dirname(cfg)
5460
5510
  );
5461
5511
  perProjectFromRg.set(cfg, owned);
5462
5512
  }
@@ -5471,9 +5521,9 @@ var program = async () => {
5471
5521
  } else {
5472
5522
  const repoRootForScan = repoRootForDiscovery;
5473
5523
  const toSeeds = (abs) => {
5474
- const rel = path9.relative(repoRootForScan, abs).replace(/\\/g, "/");
5524
+ const rel = path10.relative(repoRootForScan, abs).replace(/\\/g, "/");
5475
5525
  const withoutExt = rel.replace(/\.(m?[tj]sx?)$/i, "");
5476
- const base = path9.basename(withoutExt);
5526
+ const base = path10.basename(withoutExt);
5477
5527
  const segs = withoutExt.split("/");
5478
5528
  const tail2 = segs.slice(-2).join("/");
5479
5529
  return Array.from(new Set([withoutExt, base, tail2].filter(Boolean)));
@@ -5488,8 +5538,8 @@ var program = async () => {
5488
5538
  }
5489
5539
  };
5490
5540
  const resolveLocalImport = (fromFile, spec) => {
5491
- const baseDir = path9.dirname(fromFile);
5492
- const cand = path9.resolve(baseDir, spec);
5541
+ const baseDir = path10.dirname(fromFile);
5542
+ const cand = path10.resolve(baseDir, spec);
5493
5543
  const exts = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
5494
5544
  for (const ext of exts) {
5495
5545
  const full = ext ? `${cand}${ext}` : cand;
@@ -5498,7 +5548,7 @@ var program = async () => {
5498
5548
  }
5499
5549
  }
5500
5550
  for (const ext of exts) {
5501
- const full = path9.join(cand, `index${ext}`);
5551
+ const full = path10.join(cand, `index${ext}`);
5502
5552
  if (fsSync3.existsSync(full)) {
5503
5553
  return full;
5504
5554
  }
@@ -5552,7 +5602,7 @@ var program = async () => {
5552
5602
  cfg,
5553
5603
  jestDiscoveryArgs,
5554
5604
  keptCandidates,
5555
- path9.dirname(cfg)
5605
+ path10.dirname(cfg)
5556
5606
  );
5557
5607
  perProjectFromScan.set(cfg, owned);
5558
5608
  }
@@ -5579,9 +5629,9 @@ var program = async () => {
5579
5629
  if (effectiveJestFiles.length === 0) {
5580
5630
  const repoRoot = repoRootForRefinement;
5581
5631
  const seeds = prodSelections.map(
5582
- (abs) => path9.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
5632
+ (abs) => path10.relative(repoRoot, abs).replace(/\\/g, "/").replace(/\.(m?[tj]sx?)$/i, "")
5583
5633
  ).flatMap((rel) => {
5584
- const base = path9.basename(rel);
5634
+ const base = path10.basename(rel);
5585
5635
  const segments = rel.split("/");
5586
5636
  return Array.from(new Set([rel, base, segments.slice(-2).join("/")].filter(Boolean)));
5587
5637
  });
@@ -5594,8 +5644,8 @@ var program = async () => {
5594
5644
  }
5595
5645
  };
5596
5646
  const resolveLocalImport = (fromFile, spec) => {
5597
- const baseDir = path9.dirname(fromFile);
5598
- const candidate = path9.resolve(baseDir, spec);
5647
+ const baseDir = path10.dirname(fromFile);
5648
+ const candidate = path10.resolve(baseDir, spec);
5599
5649
  const extensions = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
5600
5650
  for (const ext of extensions) {
5601
5651
  const fullPath = ext ? `${candidate}${ext}` : candidate;
@@ -5604,7 +5654,7 @@ var program = async () => {
5604
5654
  }
5605
5655
  }
5606
5656
  for (const ext of extensions) {
5607
- const fullPath = path9.join(candidate, `index${ext}`);
5657
+ const fullPath = path10.join(candidate, `index${ext}`);
5608
5658
  if (fsSync3.existsSync(fullPath)) {
5609
5659
  return fullPath;
5610
5660
  }
@@ -5723,8 +5773,8 @@ var program = async () => {
5723
5773
  }
5724
5774
  }
5725
5775
  const jestDecision = decideShouldRunJest([], effectiveJestFiles, {
5726
- selectionSpecified,
5727
- selectionPaths
5776
+ selectionSpecified: selectionSpecifiedAugmented,
5777
+ selectionPaths: selectionPathsAugmented
5728
5778
  });
5729
5779
  const { shouldRunJest } = jestDecision;
5730
5780
  const jestCount = effectiveJestFiles.length;
@@ -5742,45 +5792,63 @@ var program = async () => {
5742
5792
  }
5743
5793
  console.info(`Run plan \u2192 Jest maybe=${shouldRunJest} (projects=${projectConfigs.length})`);
5744
5794
  let jestExitCode = 0;
5795
+ const allBridgeJson = [];
5745
5796
  const executedTestFilesSet = /* @__PURE__ */ new Set();
5746
5797
  if (shouldRunJest) {
5747
5798
  console.info("Starting Jest (no Vitest targets)\u2026");
5748
5799
  await runJestBootstrap();
5749
5800
  const jestRunArgs = selectionIncludesProdPaths ? stripPathTokens(jestArgs) : jestArgs;
5801
+ const sanitizedJestRunArgs = jestRunArgs.filter(
5802
+ (arg) => !/^--coverageDirectory(?:=|$)/.test(String(arg))
5803
+ );
5750
5804
  const projectsToRun = projectConfigs.filter(
5751
5805
  (cfg) => (perProjectFiltered.get(cfg) ?? []).length > 0
5752
5806
  );
5753
- const totalProjectsToRun = projectsToRun.length;
5754
5807
  const stripFooter = (text) => {
5755
5808
  const lines = text.split("\n");
5756
5809
  const idx = lines.findIndex((ln) => /^Test Files\s/.test(stripAnsiSimple(ln)));
5757
5810
  return idx >= 0 ? lines.slice(0, idx).join("\n").trimEnd() : text;
5758
5811
  };
5812
+ const prodSeedsForRun = (() => {
5813
+ const changedAbs = (changedSelectionAbs ?? []).map(
5814
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
5815
+ );
5816
+ const selAbs = selectionPathsAugmented.map(
5817
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
5818
+ );
5819
+ return (changedAbs.length ? changedAbs : selAbs).filter(
5820
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
5821
+ );
5822
+ })();
5823
+ const repoRootForRank = repoRootForDiscovery;
5824
+ const fileRank = await computeDirectnessRank({
5825
+ repoRoot: repoRootForRank,
5826
+ productionSeeds: prodSeedsForRun
5827
+ });
5759
5828
  for (let projIndex = 0; projIndex < projectsToRun.length; projIndex += 1) {
5760
5829
  const cfg = projectsToRun[projIndex];
5761
- const isLastProject = projIndex === totalProjectsToRun - 1;
5762
5830
  const files = perProjectFiltered.get(cfg) ?? [];
5763
5831
  if (files.length === 0) {
5764
- console.info(`Project ${path9.basename(cfg)}: 0 matching tests after filter; skipping.`);
5832
+ console.info(`Project ${path10.basename(cfg)}: 0 matching tests after filter; skipping.`);
5765
5833
  continue;
5766
5834
  }
5767
5835
  files.forEach(
5768
- (absTestPath) => executedTestFilesSet.add(path9.resolve(absTestPath).replace(/\\/g, "/"))
5836
+ (absTestPath) => executedTestFilesSet.add(path10.resolve(absTestPath).replace(/\\/g, "/"))
5769
5837
  );
5770
- const outJson = path9.join(
5838
+ const outJson = path10.join(
5771
5839
  os2.tmpdir(),
5772
5840
  `jest-bridge-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
5773
5841
  );
5774
- const reporterPath = path9.resolve("scripts/jest-vitest-bridge.cjs");
5842
+ const reporterPath = path10.resolve("scripts/jest-vitest-bridge.cjs");
5775
5843
  try {
5776
5844
  if (!fsSync3.existsSync(reporterPath)) {
5777
- fsSync3.mkdirSync(path9.dirname(reporterPath), { recursive: true });
5845
+ fsSync3.mkdirSync(path10.dirname(reporterPath), { recursive: true });
5778
5846
  fsSync3.writeFileSync(reporterPath, JEST_BRIDGE_REPORTER_SOURCE, "utf8");
5779
5847
  }
5780
5848
  } catch (ensureReporterError) {
5781
5849
  console.warn(`Unable to ensure jest bridge reporter: ${String(ensureReporterError)}`);
5782
5850
  }
5783
- 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}`);
5851
+ 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}`);
5784
5852
  const coverageFromArgs = [];
5785
5853
  for (const relPath of selectedFilesForCoverage) {
5786
5854
  coverageFromArgs.push("--collectCoverageFrom", relPath);
@@ -5794,14 +5862,17 @@ var program = async () => {
5794
5862
  "--config",
5795
5863
  cfg,
5796
5864
  "--runTestsByPath",
5797
- "--reporters",
5798
- reporterPath,
5865
+ `--reporters=${reporterPath}`,
5799
5866
  "--silent",
5800
5867
  "--colors",
5801
5868
  "--json",
5802
5869
  "--outputFile",
5803
5870
  outJson,
5804
- ...jestRunArgs,
5871
+ ...sanitizedJestRunArgs,
5872
+ ...collectCoverage ? [
5873
+ "--coverageDirectory",
5874
+ path10.join("coverage", "jest", path10.basename(cfg).replace(/[^a-zA-Z0-9_.-]+/g, "_"))
5875
+ ] : [],
5805
5876
  ...coverageFromArgs,
5806
5877
  "--passWithNoTests",
5807
5878
  ...files
@@ -5827,10 +5898,22 @@ var program = async () => {
5827
5898
  const jsonText = fsSync3.readFileSync(outJson, "utf8");
5828
5899
  const parsed = JSON.parse(jsonText);
5829
5900
  const bridge = coerceJestJsonToBridge(parsed);
5830
- pretty = renderVitestFromJestJSON(bridge, {
5831
- cwd: repoRootForDiscovery,
5832
- ...editorCmd !== void 0 ? { editorCmd } : {}
5833
- });
5901
+ allBridgeJson.push(bridge);
5902
+ try {
5903
+ const reordered = {
5904
+ ...bridge,
5905
+ testResults: sortTestResultsWithRank(fileRank, bridge.testResults).reverse()
5906
+ };
5907
+ pretty = renderVitestFromJestJSON(reordered, {
5908
+ cwd: repoRootForDiscovery,
5909
+ ...editorCmd !== void 0 ? { editorCmd } : {}
5910
+ });
5911
+ } catch {
5912
+ pretty = renderVitestFromJestJSON(bridge, {
5913
+ cwd: repoRootForDiscovery,
5914
+ ...editorCmd !== void 0 ? { editorCmd } : {}
5915
+ });
5916
+ }
5834
5917
  if (debug) {
5835
5918
  const preview = pretty.split("\n").slice(0, 3).join("\n");
5836
5919
  console.info(`pretty preview (json):
@@ -5854,9 +5937,7 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
5854
5937
  ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
5855
5938
  }
5856
5939
  }
5857
- if (!isLastProject) {
5858
- pretty = stripFooter(pretty);
5859
- }
5940
+ pretty = stripFooter(pretty);
5860
5941
  if (pretty.trim().length > 0) {
5861
5942
  process.stdout.write(pretty.endsWith("\n") ? pretty : `${pretty}
5862
5943
  `);
@@ -5868,15 +5949,69 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
5868
5949
  } else {
5869
5950
  console.info("Jest run skipped based on selection and thresholds.");
5870
5951
  }
5871
- if (collectCoverage && shouldRunJest && coverageAbortOnFailure && jestExitCode !== 0) {
5872
- process.exit(jestExitCode);
5952
+ if (allBridgeJson.length > 0) {
5953
+ const agg = allBridgeJson.map((bridge) => bridge.aggregated);
5954
+ const sum = (select) => agg.reduce((total, item) => total + (select(item) || 0), 0);
5955
+ const startTime = Math.min(
5956
+ ...allBridgeJson.map((bridge) => Number(bridge.startTime || Date.now()))
5957
+ );
5958
+ const unified = {
5959
+ startTime,
5960
+ testResults: allBridgeJson.flatMap((bridge) => bridge.testResults),
5961
+ aggregated: {
5962
+ numTotalTestSuites: sum((item) => item.numTotalTestSuites),
5963
+ numPassedTestSuites: sum((item) => item.numPassedTestSuites),
5964
+ numFailedTestSuites: sum((item) => item.numFailedTestSuites),
5965
+ numTotalTests: sum((item) => item.numTotalTests),
5966
+ numPassedTests: sum((item) => item.numPassedTests),
5967
+ numFailedTests: sum((item) => item.numFailedTests),
5968
+ numPendingTests: sum((item) => item.numPendingTests),
5969
+ numTodoTests: sum((item) => item.numTodoTests),
5970
+ startTime,
5971
+ success: agg.every((item) => Boolean(item.success)),
5972
+ runTimeMs: sum((item) => Number(item.runTimeMs ?? 0))
5973
+ }
5974
+ };
5975
+ try {
5976
+ const prodSeeds = (() => {
5977
+ const changedAbs = (changedSelectionAbs ?? []).map(
5978
+ (absPath) => path10.resolve(absPath).replace(/\\/g, "/")
5979
+ );
5980
+ const selAbs = selectionPathsAugmented.map(
5981
+ (pathToken) => path10.resolve(pathToken).replace(/\\/g, "/")
5982
+ );
5983
+ return (changedAbs.length ? changedAbs : selAbs).filter(
5984
+ (abs) => /[\\/]/.test(abs) && !/(^|\/)tests?\//i.test(abs) && !/\.(test|spec)\.[tj]sx?$/i.test(abs)
5985
+ );
5986
+ })();
5987
+ const unifiedRank = await computeDirectnessRank({
5988
+ repoRoot: repoRootForDiscovery,
5989
+ productionSeeds: prodSeeds
5990
+ });
5991
+ const ordered = sortTestResultsWithRank(unifiedRank, unified.testResults).reverse();
5992
+ unified.testResults = ordered;
5993
+ } catch {
5994
+ }
5995
+ const text = renderVitestFromJestJSON(unified, {
5996
+ cwd: repoRootForDiscovery,
5997
+ ...editorCmd !== void 0 ? { editorCmd } : {}
5998
+ });
5999
+ if (text.trim().length > 0) {
6000
+ process.stdout.write(text.endsWith("\n") ? text : `${text}
6001
+ `);
6002
+ }
6003
+ }
6004
+ const finalExitCode = jestExitCode;
6005
+ if (collectCoverage && shouldRunJest && coverageAbortOnFailure && finalExitCode !== 0) {
6006
+ process.exit(finalExitCode);
6007
+ return;
5873
6008
  }
5874
6009
  if (collectCoverage && shouldRunJest) {
5875
6010
  await mergeLcov();
5876
6011
  const repoRoot = workspaceRoot ?? await findRepoRoot();
5877
6012
  const mergedOptsBase = {
5878
- selectionSpecified,
5879
- selectionPaths,
6013
+ selectionSpecified: selectionSpecifiedAugmented,
6014
+ selectionPaths: selectionPathsAugmented,
5880
6015
  includeGlobs,
5881
6016
  excludeGlobs,
5882
6017
  workspaceRoot: repoRoot,
@@ -5891,7 +6026,6 @@ ${preview}${pretty.includes("\n") ? "\n\u2026" : ""}`);
5891
6026
  };
5892
6027
  await emitMergedCoverage(coverageUi, mergedOptsBase);
5893
6028
  }
5894
- const finalExitCode = jestExitCode;
5895
6029
  process.exit(finalExitCode);
5896
6030
  };
5897
6031