@vibgrate/cli 2026.610.2 → 2026.611.2

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.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  baselineCommand,
3
3
  runBaseline
4
- } from "./chunk-GH53OHBI.js";
5
- import "./chunk-5HKSJI57.js";
4
+ } from "./chunk-4CDBCG4I.js";
5
+ import "./chunk-EIZZ6VC3.js";
6
6
  import "./chunk-74ZJFYEM.js";
7
- import "./chunk-W6LGNQSJ.js";
7
+ import "./chunk-C7LU6YIL.js";
8
8
  import "./chunk-JSBRDJBE.js";
9
9
  export {
10
10
  baselineCommand,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-5HKSJI57.js";
3
+ } from "./chunk-EIZZ6VC3.js";
4
4
 
5
5
  // src/commands/baseline.ts
6
6
  import * as path3 from "path";
@@ -48,9 +48,12 @@ import * as path from "path";
48
48
 
49
49
  // src/utils/fs.ts
50
50
  var execFileAsync = promisify(execFile);
51
+ function stripBom(text) {
52
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
53
+ }
51
54
  async function readJsonFile(filePath) {
52
55
  const txt = await fs.readFile(filePath, "utf8");
53
- return JSON.parse(txt);
56
+ return JSON.parse(stripBom(txt));
54
57
  }
55
58
  async function pathExists(p) {
56
59
  try {
@@ -1,4 +1,4 @@
1
- // ../vibgrate-core/dist/chunk-IDJRKJOC.js
1
+ // ../vibgrate-core/dist/chunk-R3UFC4G6.js
2
2
  import { execFile } from "child_process";
3
3
  import * as fs from "fs/promises";
4
4
  import * as os from "os";
@@ -124,6 +124,10 @@ function escapeRegex(s) {
124
124
  var execFileAsync = promisify(execFile);
125
125
  var SKIP_DIRS = /* @__PURE__ */ new Set([
126
126
  "node_modules",
127
+ // Vendored third-party dependency trees (Go vendor/, PHP composer,
128
+ // Rails vendor/) — their manifests are not the repo's own projects and
129
+ // their runtimes/dependencies must not produce drift findings.
130
+ "vendor",
127
131
  ".git",
128
132
  ".vibgrate",
129
133
  ".wrangler",
@@ -562,7 +566,7 @@ var FileCache = class _FileCache {
562
566
  if (cached) return cached;
563
567
  const promise = this.readTextFile(abs).then((txt) => {
564
568
  this.textCache.delete(abs);
565
- return JSON.parse(txt);
569
+ return JSON.parse(stripBom(txt));
566
570
  });
567
571
  this.jsonCache.set(abs, promise);
568
572
  return promise;
@@ -757,9 +761,12 @@ async function findSolutionFiles(rootDir) {
757
761
  async function findCsprojFiles(rootDir) {
758
762
  return findFiles(rootDir, (name) => name.endsWith(".csproj"));
759
763
  }
764
+ function stripBom(text) {
765
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
766
+ }
760
767
  async function readJsonFile(filePath) {
761
768
  const txt = await fs.readFile(filePath, "utf8");
762
- return JSON.parse(txt);
769
+ return JSON.parse(stripBom(txt));
763
770
  }
764
771
  async function readTextFile(filePath) {
765
772
  return fs.readFile(filePath, "utf8");
@@ -796,6 +803,7 @@ export {
796
803
  findPackageJsonFiles,
797
804
  findSolutionFiles,
798
805
  findCsprojFiles,
806
+ stripBom,
799
807
  readJsonFile,
800
808
  readTextFile,
801
809
  pathExists,
@@ -14,7 +14,7 @@ import {
14
14
  readTextFile,
15
15
  writeJsonFile,
16
16
  writeTextFile
17
- } from "./chunk-W6LGNQSJ.js";
17
+ } from "./chunk-C7LU6YIL.js";
18
18
  import {
19
19
  __commonJS,
20
20
  __toESM
@@ -1802,6 +1802,8 @@ import * as path31 from "path";
1802
1802
  import * as path33 from "path";
1803
1803
  import chalk3 from "chalk";
1804
1804
  import chalk2 from "chalk";
1805
+ import { writeFileSync, mkdirSync } from "fs";
1806
+ import { dirname as dirname182, basename as basename192 } from "path";
1805
1807
  import * as fs7 from "fs/promises";
1806
1808
  import * as path32 from "path";
1807
1809
  var CONFIG_FILES = [
@@ -4938,7 +4940,7 @@ async function scanPythonProjects(rootDir, pypiCache, cache, projectScanTimeout)
4938
4940
  return results;
4939
4941
  }
4940
4942
  async function findPythonManifests(rootDir) {
4941
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
4943
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
4942
4944
  return findFiles2(rootDir, (name) => PYTHON_MANIFEST_FILES.has(name) || /^requirements.*\.txt$/.test(name));
4943
4945
  }
4944
4946
  async function scanOnePythonProject(dir, manifestFiles, rootDir, pypiCache, cache) {
@@ -5335,7 +5337,7 @@ async function scanJavaProjects(rootDir, mavenCache, cache, projectScanTimeout)
5335
5337
  return results;
5336
5338
  }
5337
5339
  async function findJavaManifests(rootDir) {
5338
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
5340
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
5339
5341
  return findFiles2(rootDir, (name) => JAVA_MANIFEST_FILES.has(name));
5340
5342
  }
5341
5343
  async function scanOneJavaProject(dir, manifestFiles, rootDir, mavenCache, cache) {
@@ -5700,7 +5702,7 @@ async function scanRubyProjects(rootDir, rubygemsCache, cache, projectScanTimeou
5700
5702
  return results;
5701
5703
  }
5702
5704
  async function findRubyManifests(rootDir) {
5703
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
5705
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
5704
5706
  return findFiles2(rootDir, (name) => RUBY_MANIFEST_FILES.has(name) || isGemspec(name));
5705
5707
  }
5706
5708
  async function scanOneRubyProject(dir, manifestFiles, rootDir, rubygemsCache, cache) {
@@ -5924,7 +5926,7 @@ async function scanSwiftProjects(rootDir, swiftCache, cache, projectScanTimeout)
5924
5926
  return results;
5925
5927
  }
5926
5928
  async function findSwiftManifests(rootDir) {
5927
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
5929
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
5928
5930
  return findFiles2(rootDir, (name) => SWIFT_MANIFEST_FILES.has(name));
5929
5931
  }
5930
5932
  async function scanOneSwiftProject(dir, manifestFile, rootDir, swiftCache, cache) {
@@ -6161,7 +6163,7 @@ async function scanGoProjects(rootDir, goCache, cache, projectScanTimeout) {
6161
6163
  return results;
6162
6164
  }
6163
6165
  async function findGoManifests(rootDir) {
6164
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
6166
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
6165
6167
  return findFiles2(rootDir, (name) => GO_MANIFEST_FILES.has(name));
6166
6168
  }
6167
6169
  async function scanOneGoProject(dir, manifestFile, rootDir, goCache, cache) {
@@ -6395,7 +6397,7 @@ async function scanRustProjects(rootDir, cargoCache, cache, projectScanTimeout)
6395
6397
  return results;
6396
6398
  }
6397
6399
  async function findRustManifests(rootDir) {
6398
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
6400
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
6399
6401
  return findFiles2(rootDir, (name) => RUST_MANIFEST_FILES.has(name));
6400
6402
  }
6401
6403
  async function scanOneRustProject(dir, manifestFile, rootDir, cargoCache, cache) {
@@ -6619,7 +6621,7 @@ async function scanPhpProjects(rootDir, composerCache, cache, projectScanTimeout
6619
6621
  return results;
6620
6622
  }
6621
6623
  async function findPhpManifests(rootDir) {
6622
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
6624
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
6623
6625
  return findFiles2(rootDir, (name) => PHP_MANIFEST_FILES.has(name));
6624
6626
  }
6625
6627
  async function scanOnePhpProject(dir, manifestFile, rootDir, composerCache, cache) {
@@ -6795,7 +6797,7 @@ function parsePubspecYaml(content) {
6795
6797
  for (const line of content.split(/\r?\n/)) {
6796
6798
  const trimmed = line.trim();
6797
6799
  if (trimmed.startsWith("sdk:")) {
6798
- const match = trimmed.match(/sdk:\s*['"]?>=?([^<'"]+)/);
6800
+ const match = trimmed.match(/sdk:\s*['"]?>?=?\s*(\d[^<'"]*)/);
6799
6801
  if (match) dartVersion = match[1].trim();
6800
6802
  continue;
6801
6803
  }
@@ -6805,7 +6807,7 @@ function parsePubspecYaml(content) {
6805
6807
  } else if (trimmed === "dev_dependencies:") {
6806
6808
  currentSection = "dev_dependencies";
6807
6809
  continue;
6808
- } else if (/^\w+:/.test(trimmed) && !trimmed.startsWith(" ")) {
6810
+ } else if (/^\w+:/.test(line)) {
6809
6811
  currentSection = null;
6810
6812
  continue;
6811
6813
  }
@@ -6833,7 +6835,7 @@ async function parsePubspecLock(filePath, cache) {
6833
6835
  let currentPackage = null;
6834
6836
  for (const line of content.split(/\r?\n/)) {
6835
6837
  const trimmed = line.trim();
6836
- const pkgMatch = trimmed.match(/^(\w+):$/);
6838
+ const pkgMatch = line.match(/^ {2}(\w+):$/);
6837
6839
  if (pkgMatch) {
6838
6840
  currentPackage = pkgMatch[1];
6839
6841
  continue;
@@ -6884,7 +6886,7 @@ async function scanDartProjects(rootDir, pubCache, cache, projectScanTimeout) {
6884
6886
  return results;
6885
6887
  }
6886
6888
  async function findDartManifests(rootDir) {
6887
- const { findFiles: findFiles2 } = await import("./fs-IJIQMW3G-NJTQJLB6.js");
6889
+ const { findFiles: findFiles2 } = await import("./fs-PXXYZATK-EW5LCUA7.js");
6888
6890
  return findFiles2(rootDir, (name) => DART_MANIFEST_FILES.has(name));
6889
6891
  }
6890
6892
  async function scanOneDartProject(dir, manifestFile, rootDir, pubCache, cache) {
@@ -7254,6 +7256,7 @@ async function scanElixirProjects(rootDir, manifest, cache, projectScanTimeout,
7254
7256
  try {
7255
7257
  const scan = await scanElixir(dir, cache, manifest, offline);
7256
7258
  if (scan) {
7259
+ scan.path = path13.relative(rootDir, dir) || ".";
7257
7260
  results.push(scan);
7258
7261
  }
7259
7262
  } catch (error) {
@@ -7511,6 +7514,7 @@ async function scanDockerProjects(rootDir, manifest, cache, projectScanTimeout,
7511
7514
  try {
7512
7515
  const scan = await scanDocker(dir, cache, manifest, offline);
7513
7516
  if (scan) {
7517
+ scan.path = path14.relative(rootDir, dir) || ".";
7514
7518
  results.push(scan);
7515
7519
  }
7516
7520
  } catch (error) {
@@ -7747,6 +7751,7 @@ async function scanHelmProjects(rootDir, manifest, cache, projectScanTimeout, of
7747
7751
  try {
7748
7752
  const scan = await scanHelm(dir, cache, manifest, offline);
7749
7753
  if (scan) {
7754
+ scan.path = path15.relative(rootDir, dir) || ".";
7750
7755
  results.push(scan);
7751
7756
  }
7752
7757
  } catch (error) {
@@ -8123,6 +8128,7 @@ async function scanTerraformProjects(rootDir, manifest, cache, projectScanTimeou
8123
8128
  try {
8124
8129
  const scan = await scanTerraform(dir, cache, manifest, offline);
8125
8130
  if (scan) {
8131
+ scan.path = path16.relative(rootDir, dir) || ".";
8126
8132
  results.push(scan);
8127
8133
  }
8128
8134
  } catch (error) {
@@ -8184,6 +8190,9 @@ function parseLineDependencies(content, regex, capture = 1) {
8184
8190
  function getProjectName(projectPath, rootDir) {
8185
8191
  return path17.basename(projectPath) || path17.basename(rootDir);
8186
8192
  }
8193
+ function makefileHasCSignals(content) {
8194
+ return /^\s*(CC|CFLAGS|LDFLAGS)\s*[:?+]?=/m.test(content) || /\b(gcc|clang)\b/.test(content) || /\.(c|o)\b/.test(content);
8195
+ }
8187
8196
  function addProject(projects, seen, type, projectPath, rootDir, dependencies = []) {
8188
8197
  const normalizedPath = projectPath || ".";
8189
8198
  const key = `${type}:${normalizedPath}`;
@@ -8254,6 +8263,10 @@ async function scanPolyglotProjects(rootDir, cache) {
8254
8263
  for (const file of candidateFiles) {
8255
8264
  const mapping = MANIFEST_TO_LANGUAGE.find((m) => m.name === file.name || m.name.startsWith("*.") && file.name.endsWith(m.name.slice(1)));
8256
8265
  if (!mapping) continue;
8266
+ if (file.name === "Makefile") {
8267
+ const text = cache ? await cache.readTextFile(file.absPath) : await readTextFile(file.absPath);
8268
+ if (!makefileHasCSignals(text)) continue;
8269
+ }
8257
8270
  const projectPath = path17.dirname(file.relPath) || ".";
8258
8271
  const dependencies = await parseDepsByManifest(file.absPath, cache);
8259
8272
  addProject(projects, seen, mapping.type, projectPath, rootDir, dependencies);
@@ -13406,6 +13419,54 @@ async function computeTreeMetadataHash(rootDir, options) {
13406
13419
  }
13407
13420
  return digest.digest("hex");
13408
13421
  }
13422
+ var ProgressTrace = class _ProgressTrace {
13423
+ constructor(outPath) {
13424
+ this.outPath = outPath;
13425
+ }
13426
+ events = [];
13427
+ start = Date.now();
13428
+ lastThrottled = /* @__PURE__ */ new Map();
13429
+ flushed = false;
13430
+ /** Returns a trace when VIBGRATE_TRACE_EVENTS is set, else null. */
13431
+ static fromEnv() {
13432
+ const path34 = process.env.VIBGRATE_TRACE_EVENTS;
13433
+ return path34 ? new _ProgressTrace(path34) : null;
13434
+ }
13435
+ record(op, data = {}) {
13436
+ if (this.flushed) return;
13437
+ this.events.push({ t: Date.now() - this.start, op, ...data });
13438
+ }
13439
+ /**
13440
+ * Record at most one event per key per interval. Used for high-frequency
13441
+ * updates (sub-step progress, live stats) so traces stay compact.
13442
+ */
13443
+ recordThrottled(key, op, data = {}, intervalMs = 120) {
13444
+ if (this.flushed) return;
13445
+ const now = Date.now();
13446
+ const last = this.lastThrottled.get(key) ?? 0;
13447
+ if (now - last < intervalMs) return;
13448
+ this.lastThrottled.set(key, now);
13449
+ this.record(op, data);
13450
+ }
13451
+ /** Write the trace document. Safe to call once; later calls are no-ops. */
13452
+ flush(meta) {
13453
+ if (this.flushed) return;
13454
+ this.flushed = true;
13455
+ const doc = {
13456
+ traceVersion: 1,
13457
+ cliVersion: meta.cliVersion,
13458
+ workspace: basename192(meta.rootDir) || meta.rootDir,
13459
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
13460
+ durationMs: Date.now() - this.start,
13461
+ events: this.events
13462
+ };
13463
+ try {
13464
+ mkdirSync(dirname182(this.outPath), { recursive: true });
13465
+ writeFileSync(this.outPath, JSON.stringify(doc));
13466
+ } catch {
13467
+ }
13468
+ }
13469
+ };
13409
13470
  var ROBOT = [
13410
13471
  chalk2.cyan(" \u256D\u2500\u2500\u2500\u256E") + chalk2.greenBright("\u279C"),
13411
13472
  chalk2.cyan(" \u256D\u2524") + chalk2.greenBright("\u25C9 \u25C9") + chalk2.cyan("\u251C\u256E"),
@@ -13449,8 +13510,11 @@ var ScanProgress = class {
13449
13510
  /** Last emitted step snapshot for append-only output modes */
13450
13511
  lastLoggedStates = /* @__PURE__ */ new Map();
13451
13512
  version;
13513
+ /** Optional semantic event recorder (VIBGRATE_TRACE_EVENTS) for the web simulator */
13514
+ trace;
13452
13515
  constructor(rootDir, version = "unknown") {
13453
13516
  this.version = version;
13517
+ this.trace = ProgressTrace.fromEnv();
13454
13518
  this.isTTY = process.stderr.isTTY ?? false;
13455
13519
  this.useLiveUpdates = this.isTTY && process.env.VIBGRATE_PROGRESS_MODE !== "plain";
13456
13520
  this.rootDir = rootDir;
@@ -13475,6 +13539,7 @@ var ScanProgress = class {
13475
13539
  /** Set the estimated total duration from scan history */
13476
13540
  setEstimatedTotal(estimatedMs) {
13477
13541
  this.estimatedTotalMs = estimatedMs;
13542
+ this.trace?.record("setEstimatedTotal", { estimatedMs });
13478
13543
  }
13479
13544
  /** Set per-step estimated durations from scan history */
13480
13545
  setStepEstimates(estimates) {
@@ -13487,6 +13552,7 @@ var ScanProgress = class {
13487
13552
  /** Register all steps up front, optionally with weights */
13488
13553
  setSteps(steps) {
13489
13554
  this.steps = steps.map((s) => ({ ...s, status: "pending", weight: s.weight ?? 1 }));
13555
+ this.trace?.record("setSteps", { steps: steps.map((s) => ({ id: s.id, label: s.label, weight: s.weight ?? 1 })) });
13490
13556
  if (this.isTTY) {
13491
13557
  const header = [
13492
13558
  "",
@@ -13512,6 +13578,7 @@ var ScanProgress = class {
13512
13578
  } else {
13513
13579
  this.steps.push(newStep);
13514
13580
  }
13581
+ this.trace?.record("insertStepBefore", { beforeId, step: { id: step.id, label: step.label, weight: step.weight ?? 1 } });
13515
13582
  }
13516
13583
  /** Mark a step as active (currently running), optionally with expected total */
13517
13584
  startStep(id, subTotal) {
@@ -13524,6 +13591,7 @@ var ScanProgress = class {
13524
13591
  step.subTotal = subTotal;
13525
13592
  }
13526
13593
  this.stepStartTimes.set(id, Date.now());
13594
+ this.trace?.record("startStep", { id, subTotal });
13527
13595
  this.render();
13528
13596
  }
13529
13597
  /** Mark a step as completed */
@@ -13538,6 +13606,7 @@ var ScanProgress = class {
13538
13606
  if (started) {
13539
13607
  this.stepTimings.push({ id, durationMs: Date.now() - started });
13540
13608
  }
13609
+ this.trace?.record("completeStep", { id, detail, count });
13541
13610
  this.render();
13542
13611
  }
13543
13612
  /** Mark a step as skipped */
@@ -13547,6 +13616,7 @@ var ScanProgress = class {
13547
13616
  step.status = "skipped";
13548
13617
  step.detail = "disabled";
13549
13618
  }
13619
+ this.trace?.record("skipStep", { id });
13550
13620
  this.render();
13551
13621
  }
13552
13622
  /** Update sub-step progress for the active step (files processed, etc.) */
@@ -13557,32 +13627,51 @@ var ScanProgress = class {
13557
13627
  if (total !== void 0) step.subTotal = total;
13558
13628
  if (label !== void 0) step.subLabel = label;
13559
13629
  }
13630
+ this.trace?.recordThrottled(`sub:${id}`, "updateStepProgress", { id, current, total, label });
13560
13631
  this.render();
13561
13632
  }
13562
13633
  /** Update live stats */
13563
13634
  updateStats(partial) {
13564
13635
  Object.assign(this.stats, partial);
13636
+ this.traceStats(true);
13565
13637
  this.render();
13566
13638
  }
13567
13639
  /** Increment stats */
13568
13640
  addProjects(n) {
13569
13641
  this.stats.projects += n;
13642
+ this.traceStats();
13570
13643
  this.render();
13571
13644
  }
13572
13645
  addDependencies(n) {
13573
13646
  this.stats.dependencies += n;
13647
+ this.traceStats();
13574
13648
  this.render();
13575
13649
  }
13576
13650
  addFrameworks(n) {
13577
13651
  this.stats.frameworks += n;
13652
+ this.traceStats();
13578
13653
  this.render();
13579
13654
  }
13580
13655
  addFindings(warnings, errors, notes) {
13581
13656
  this.stats.findings.warnings += warnings;
13582
13657
  this.stats.findings.errors += errors;
13583
13658
  this.stats.findings.notes += notes;
13659
+ this.traceStats();
13584
13660
  this.render();
13585
13661
  }
13662
+ /** Record a (throttled) snapshot of live stats into the trace */
13663
+ traceStats(force = false) {
13664
+ if (!this.trace) return;
13665
+ const snapshot = {
13666
+ stats: {
13667
+ ...this.stats,
13668
+ findings: { ...this.stats.findings },
13669
+ treeSummary: this.stats.treeSummary ? { ...this.stats.treeSummary } : void 0
13670
+ }
13671
+ };
13672
+ if (force) this.trace.record("stats", snapshot);
13673
+ else this.trace.recordThrottled("stats", "stats", snapshot);
13674
+ }
13586
13675
  /** Stop the progress display and clear it */
13587
13676
  finish() {
13588
13677
  if (this.timer) {
@@ -13607,6 +13696,8 @@ var ScanProgress = class {
13607
13696
 
13608
13697
  `)
13609
13698
  );
13699
+ this.trace?.record("finish", { doneCount, elapsedMs: Date.now() - this.startTime, summary: `${doneCount} scanners completed in ${elapsed}` });
13700
+ this.trace?.flush({ cliVersion: this.version, rootDir: this.rootDir });
13610
13701
  if (this.isTTY && process.platform === "win32") {
13611
13702
  process.stdout.write("\x1B[0G\x1B[K");
13612
13703
  }
@@ -14260,14 +14351,17 @@ async function runScan(rootDir, opts) {
14260
14351
  progress.addProjects(polyglotProjects.length);
14261
14352
  progress.completeStep("polyglot", `${polyglotProjects.length} project${polyglotProjects.length !== 1 ? "s" : ""}`, polyglotProjects.length);
14262
14353
  }
14354
+ const OVERLAY_PROJECT_TYPES = /* @__PURE__ */ new Set(["docker", "helm", "terraform"]);
14355
+ const dedupeKey = (p) => OVERLAY_PROJECT_TYPES.has(p.type) ? `${p.type}:${p.path}` : p.path;
14263
14356
  const rawProjects = [...nodeProjects, ...dotnetProjects, ...pythonProjects, ...javaProjects, ...rubyProjects, ...swiftProjects, ...goProjects, ...rustProjects, ...phpProjects, ...dartProjects, ...elixirProjects, ...dockerProjects, ...helmProjects, ...terraformProjects, ...polyglotProjects];
14264
14357
  const deduplicatedMap = /* @__PURE__ */ new Map();
14265
14358
  for (const project of rawProjects) {
14266
- const existing = deduplicatedMap.get(project.path);
14359
+ const existing = deduplicatedMap.get(dedupeKey(project));
14267
14360
  if (!existing) {
14268
- deduplicatedMap.set(project.path, project);
14361
+ deduplicatedMap.set(dedupeKey(project), project);
14269
14362
  } else {
14270
- const keepNew = project.dependencies.length > existing.dependencies.length;
14363
+ const resolvedCount = (p) => p.dependencies.filter((d) => d.resolvedVersion || d.latestStable).length;
14364
+ const keepNew = resolvedCount(project) > resolvedCount(existing) || resolvedCount(project) === resolvedCount(existing) && project.dependencies.length > existing.dependencies.length;
14271
14365
  const winner = keepNew ? project : existing;
14272
14366
  const loser = keepNew ? existing : project;
14273
14367
  if (loser.projectReferences?.length) {
@@ -14280,7 +14374,7 @@ async function runScan(rootDir, opts) {
14280
14374
  }
14281
14375
  }
14282
14376
  }
14283
- deduplicatedMap.set(project.path, winner);
14377
+ deduplicatedMap.set(dedupeKey(project), winner);
14284
14378
  }
14285
14379
  }
14286
14380
  const allProjects = [...deduplicatedMap.values()];
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  pathExists,
7
7
  readJsonFile,
8
8
  writeTextFile
9
- } from "./chunk-GH53OHBI.js";
9
+ } from "./chunk-4CDBCG4I.js";
10
10
  import {
11
11
  computeRepoFingerprint,
12
12
  detectVcs,
@@ -16,14 +16,14 @@ import {
16
16
  resolveRepositoryName,
17
17
  runScan,
18
18
  writeDefaultConfig
19
- } from "./chunk-5HKSJI57.js";
19
+ } from "./chunk-EIZZ6VC3.js";
20
20
  import {
21
21
  require_semver
22
22
  } from "./chunk-74ZJFYEM.js";
23
23
  import {
24
24
  parseExcludePatterns,
25
25
  pathExists as pathExists2
26
- } from "./chunk-W6LGNQSJ.js";
26
+ } from "./chunk-C7LU6YIL.js";
27
27
  import {
28
28
  __toESM
29
29
  } from "./chunk-JSBRDJBE.js";
@@ -49,7 +49,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
49
49
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
50
50
  }
51
51
  if (opts.baseline) {
52
- const { runBaseline } = await import("./baseline-FYQ2HRTZ.js");
52
+ const { runBaseline } = await import("./baseline-QRGGTCJD.js");
53
53
  await runBaseline(rootDir);
54
54
  }
55
55
  console.log("");
@@ -12,9 +12,10 @@ import {
12
12
  quickTreeCount,
13
13
  readJsonFile,
14
14
  readTextFile,
15
+ stripBom,
15
16
  writeJsonFile,
16
17
  writeTextFile
17
- } from "./chunk-W6LGNQSJ.js";
18
+ } from "./chunk-C7LU6YIL.js";
18
19
  import "./chunk-JSBRDJBE.js";
19
20
  export {
20
21
  FileCache,
@@ -30,6 +31,7 @@ export {
30
31
  quickTreeCount,
31
32
  readJsonFile,
32
33
  readTextFile,
34
+ stripBom,
33
35
  writeJsonFile,
34
36
  writeTextFile
35
37
  };