@vibgrate/cli 2026.825.2 → 2026.829.1

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.
Files changed (129) hide show
  1. package/DOCS.md +204 -9
  2. package/README.md +2 -0
  3. package/dist/{agent-DHQYEHVW.js → agent-5GW7C7EB.js} +14 -14
  4. package/dist/{agent-DHQYEHVW.js.map → agent-5GW7C7EB.js.map} +1 -1
  5. package/dist/{approvals-KBM72NU2.js → approvals-ISJFILVK.js} +5 -5
  6. package/dist/{approvals-KBM72NU2.js.map → approvals-ISJFILVK.js.map} +1 -1
  7. package/dist/{attach-O74NL4M5.js → attach-JUSALQ75.js} +3 -3
  8. package/dist/{attach-O74NL4M5.js.map → attach-JUSALQ75.js.map} +1 -1
  9. package/dist/baseline-YL2IZZ4Y.js +9 -0
  10. package/dist/{baseline-4X6MQBDA.js.map → baseline-YL2IZZ4Y.js.map} +1 -1
  11. package/dist/{chunk-JAEHLTVX.js → chunk-5RW5PIBH.js} +3 -3
  12. package/dist/{chunk-JAEHLTVX.js.map → chunk-5RW5PIBH.js.map} +1 -1
  13. package/dist/{chunk-TT2J2TB6.js → chunk-65YN3BG3.js} +1207 -40
  14. package/dist/chunk-65YN3BG3.js.map +1 -0
  15. package/dist/{chunk-VMPKOMHG.js → chunk-B5VCPIKT.js} +7 -6
  16. package/dist/chunk-B5VCPIKT.js.map +1 -0
  17. package/dist/{chunk-FQHWPOTS.js → chunk-BA6YBVNT.js} +3 -3
  18. package/dist/{chunk-FQHWPOTS.js.map → chunk-BA6YBVNT.js.map} +1 -1
  19. package/dist/{chunk-3E7QT4NE.js → chunk-BODXEZEY.js} +9 -9
  20. package/dist/{chunk-3E7QT4NE.js.map → chunk-BODXEZEY.js.map} +1 -1
  21. package/dist/{chunk-BICZR6UJ.js → chunk-D3SV3JWB.js} +3 -3
  22. package/dist/{chunk-BICZR6UJ.js.map → chunk-D3SV3JWB.js.map} +1 -1
  23. package/dist/chunk-EGMBHPXV.js +127 -0
  24. package/dist/chunk-EGMBHPXV.js.map +1 -0
  25. package/dist/{chunk-DIZ5KI5Q.js → chunk-FHSEK64W.js} +7 -7
  26. package/dist/{chunk-DIZ5KI5Q.js.map → chunk-FHSEK64W.js.map} +1 -1
  27. package/dist/{chunk-E3XVIFSH.js → chunk-FKCN653F.js} +3 -3
  28. package/dist/{chunk-E3XVIFSH.js.map → chunk-FKCN653F.js.map} +1 -1
  29. package/dist/{chunk-OIUVWCKM.js → chunk-FPIMNVLI.js} +12 -4
  30. package/dist/chunk-FPIMNVLI.js.map +1 -0
  31. package/dist/{chunk-NS4DL3QR.js → chunk-HHMWE52J.js} +10 -135
  32. package/dist/chunk-HHMWE52J.js.map +1 -0
  33. package/dist/{chunk-5UG4UHKU.js → chunk-HUJZO4K5.js} +281 -106
  34. package/dist/chunk-HUJZO4K5.js.map +1 -0
  35. package/dist/{chunk-HFQIYM6E.js → chunk-JWQ2JCQS.js} +4 -4
  36. package/dist/{chunk-HFQIYM6E.js.map → chunk-JWQ2JCQS.js.map} +1 -1
  37. package/dist/{chunk-27TS2AUJ.js → chunk-K7QR7U3V.js} +12 -11
  38. package/dist/chunk-K7QR7U3V.js.map +1 -0
  39. package/dist/{chunk-IJVWKZRS.js → chunk-KCKG3EEJ.js} +3 -3
  40. package/dist/{chunk-IJVWKZRS.js.map → chunk-KCKG3EEJ.js.map} +1 -1
  41. package/dist/{chunk-GWSJYDK7.js → chunk-MMIIVRQC.js} +7 -7
  42. package/dist/chunk-MMIIVRQC.js.map +1 -0
  43. package/dist/chunk-N6R4XPLX.js +27 -0
  44. package/dist/chunk-N6R4XPLX.js.map +1 -0
  45. package/dist/{chunk-GR7SFCQJ.js → chunk-NQQT75AN.js} +77 -27
  46. package/dist/chunk-NQQT75AN.js.map +1 -0
  47. package/dist/{chunk-6MIJLO5B.js → chunk-NSC7ZK62.js} +3 -3
  48. package/dist/{chunk-6MIJLO5B.js.map → chunk-NSC7ZK62.js.map} +1 -1
  49. package/dist/{chunk-SYMJSRYF.js → chunk-OAYSRM7J.js} +12 -14
  50. package/dist/chunk-OAYSRM7J.js.map +1 -0
  51. package/dist/{chunk-L7GCJNDT.js → chunk-QMROD7MI.js} +15 -8
  52. package/dist/chunk-QMROD7MI.js.map +1 -0
  53. package/dist/{chunk-SHD5TCZ5.js → chunk-R2FX4USU.js} +7 -7
  54. package/dist/chunk-R2FX4USU.js.map +1 -0
  55. package/dist/{chunk-7M4E7UGX.js → chunk-RL7JYG3D.js} +3 -3
  56. package/dist/{chunk-7M4E7UGX.js.map → chunk-RL7JYG3D.js.map} +1 -1
  57. package/dist/{chunk-QLNJ3VWO.js → chunk-RRSUNTZI.js} +3 -3
  58. package/dist/{chunk-QLNJ3VWO.js.map → chunk-RRSUNTZI.js.map} +1 -1
  59. package/dist/{chunk-NZES47ST.js → chunk-TWFPHXPN.js} +3 -3
  60. package/dist/{chunk-NZES47ST.js.map → chunk-TWFPHXPN.js.map} +1 -1
  61. package/dist/chunk-UGRUG2FA.js +6 -0
  62. package/dist/{chunk-G3ACLBMG.js.map → chunk-UGRUG2FA.js.map} +1 -1
  63. package/dist/{chunk-KYM55VZX.js → chunk-VOEEYCR3.js} +6 -6
  64. package/dist/{chunk-KYM55VZX.js.map → chunk-VOEEYCR3.js.map} +1 -1
  65. package/dist/{chunk-SIXWWQ6S.js → chunk-WBDRBP22.js} +4 -4
  66. package/dist/{chunk-SIXWWQ6S.js.map → chunk-WBDRBP22.js.map} +1 -1
  67. package/dist/{chunk-K3SXWOTT.js → chunk-YL332BU2.js} +20 -15
  68. package/dist/chunk-YL332BU2.js.map +1 -0
  69. package/dist/{chunk-2NUS5BOR.js → chunk-ZQY5TWCB.js} +3 -3
  70. package/dist/{chunk-2NUS5BOR.js.map → chunk-ZQY5TWCB.js.map} +1 -1
  71. package/dist/cli.d.ts +16 -1
  72. package/dist/cli.js +2362 -342
  73. package/dist/cli.js.map +1 -1
  74. package/dist/{daemon-RZS5FPIW.js → daemon-NRIIDMX2.js} +10 -10
  75. package/dist/{daemon-RZS5FPIW.js.map → daemon-NRIIDMX2.js.map} +1 -1
  76. package/dist/embed-worker-main.js +3 -3
  77. package/dist/{ensure-map-DL2S774J.js → ensure-map-VAIYCCEJ.js} +17 -16
  78. package/dist/{ensure-map-DL2S774J.js.map → ensure-map-VAIYCCEJ.js.map} +1 -1
  79. package/dist/explain-EXPJTCB5.js +126 -0
  80. package/dist/explain-EXPJTCB5.js.map +1 -0
  81. package/dist/{graph-backend-JXARVT3R.js → graph-backend-V5A2H5ZB.js} +7 -7
  82. package/dist/{graph-backend-JXARVT3R.js.map → graph-backend-V5A2H5ZB.js.map} +1 -1
  83. package/dist/index.d.ts +217 -13
  84. package/dist/index.js +17 -16
  85. package/dist/index.js.map +1 -1
  86. package/dist/{interactive-2XRBRFRB.js → interactive-VQM2CTE2.js} +37 -32
  87. package/dist/interactive-VQM2CTE2.js.map +1 -0
  88. package/dist/load-UAVA732M.js +11 -0
  89. package/dist/{load-7C5VETRD.js.map → load-UAVA732M.js.map} +1 -1
  90. package/dist/parse-worker.js +2 -2
  91. package/dist/{rules-ASYEUNB5.js → rules-TLRGB5L4.js} +12 -4
  92. package/dist/rules-TLRGB5L4.js.map +1 -0
  93. package/dist/{run-outcome-TMB5TJNA.js → run-outcome-EATWIKLR.js} +5 -5
  94. package/dist/{run-outcome-TMB5TJNA.js.map → run-outcome-EATWIKLR.js.map} +1 -1
  95. package/dist/{runtime-session-Y23NVY7R.js → runtime-session-Y2BRBXUW.js} +9 -9
  96. package/dist/{runtime-session-Y23NVY7R.js.map → runtime-session-Y2BRBXUW.js.map} +1 -1
  97. package/dist/{session-KXT2ZTPE.js → session-N2FTYIHQ.js} +9 -9
  98. package/dist/{session-KXT2ZTPE.js.map → session-N2FTYIHQ.js.map} +1 -1
  99. package/dist/{stream-json-NW6AOE47.js → stream-json-GCZAHORX.js} +14 -14
  100. package/dist/{stream-json-NW6AOE47.js.map → stream-json-GCZAHORX.js.map} +1 -1
  101. package/dist/version-XNZ4GLMQ.js +4 -0
  102. package/dist/{version-7ABYXWOZ.js.map → version-XNZ4GLMQ.js.map} +1 -1
  103. package/dist/{vg-mcp-bridge-LXBHKEFR.js → vg-mcp-bridge-VDZWFEO2.js} +22 -11
  104. package/dist/vg-mcp-bridge-VDZWFEO2.js.map +1 -0
  105. package/dist/{vgd-RW7HCEIP.js → vgd-3A2TRNP5.js} +8 -8
  106. package/dist/vgd-3A2TRNP5.js.map +1 -0
  107. package/package.json +2 -2
  108. package/dist/baseline-4X6MQBDA.js +0 -9
  109. package/dist/chunk-27TS2AUJ.js.map +0 -1
  110. package/dist/chunk-5UG4UHKU.js.map +0 -1
  111. package/dist/chunk-G3ACLBMG.js +0 -6
  112. package/dist/chunk-GR7SFCQJ.js.map +0 -1
  113. package/dist/chunk-GWSJYDK7.js.map +0 -1
  114. package/dist/chunk-K3SXWOTT.js.map +0 -1
  115. package/dist/chunk-L7GCJNDT.js.map +0 -1
  116. package/dist/chunk-NS4DL3QR.js.map +0 -1
  117. package/dist/chunk-OIUVWCKM.js.map +0 -1
  118. package/dist/chunk-SHD5TCZ5.js.map +0 -1
  119. package/dist/chunk-SYMJSRYF.js.map +0 -1
  120. package/dist/chunk-TT2J2TB6.js.map +0 -1
  121. package/dist/chunk-VMPKOMHG.js.map +0 -1
  122. package/dist/chunk-YENM3HT7.js +0 -23
  123. package/dist/chunk-YENM3HT7.js.map +0 -1
  124. package/dist/interactive-2XRBRFRB.js.map +0 -1
  125. package/dist/load-7C5VETRD.js +0 -11
  126. package/dist/rules-ASYEUNB5.js.map +0 -1
  127. package/dist/version-7ABYXWOZ.js +0 -4
  128. package/dist/vg-mcp-bridge-LXBHKEFR.js.map +0 -1
  129. package/dist/vgd-RW7HCEIP.js.map +0 -1
@@ -6,12 +6,12 @@ import chalk3 from 'chalk';
6
6
  import * as os3 from 'os';
7
7
  import * as semver26 from 'semver';
8
8
  import { minVersion, validRange, SemVer, gt } from 'semver';
9
- import { parse } from 'yaml';
9
+ import { parse as parse$1 } from 'yaml';
10
10
  import { execFile, spawn } from 'child_process';
11
11
  import * as fs from 'fs/promises';
12
12
  import { readFile, mkdir, writeFile, mkdtemp, rm } from 'fs/promises';
13
13
  import { XMLParser } from 'fast-xml-parser';
14
- import { parse as parse$1 } from 'smol-toml';
14
+ import { parse } from 'smol-toml';
15
15
  import * as crypto from 'crypto';
16
16
  import * as fs7 from 'fs';
17
17
  import { mkdirSync, writeFileSync } from 'fs';
@@ -105,7 +105,7 @@ async function loadNpmLockIndex(dir, io) {
105
105
  const lockPath = path31.join(dir, "package-lock.json");
106
106
  if (await io.exists(pnpmPath).catch(() => false)) {
107
107
  try {
108
- const map = parsePnpmLock(parse(await io.readText(pnpmPath)));
108
+ const map = parsePnpmLock(parse$1(await io.readText(pnpmPath)));
109
109
  if (map.size) return { source: "pnpm", size: map.size, resolve: (name) => map.get(name) ?? null };
110
110
  } catch {
111
111
  }
@@ -2044,7 +2044,7 @@ async function scanOneDotnetProjectFile(csprojPath, rootDir, nugetCache, cache,
2044
2044
  function parseToml(text) {
2045
2045
  if (!text) return null;
2046
2046
  try {
2047
- const doc = parse$1(text);
2047
+ const doc = parse(text);
2048
2048
  return doc && typeof doc === "object" ? doc : null;
2049
2049
  } catch {
2050
2050
  return null;
@@ -12533,6 +12533,7 @@ function cloneClassification(c) {
12533
12533
  signals: [...c.signals]
12534
12534
  };
12535
12535
  if (c.role) next.role = c.role;
12536
+ if (c.source) next.source = c.source;
12536
12537
  return next;
12537
12538
  }
12538
12539
  function applyFolderInheritance(input) {
@@ -12622,7 +12623,8 @@ function applyFolderInheritance(input) {
12622
12623
  filePath: file,
12623
12624
  layer: folder.layer,
12624
12625
  confidence: folder.confidence,
12625
- signals: [folderInheritSignal(folder.layer)]
12626
+ signals: [folderInheritSignal(folder.layer)],
12627
+ source: "folder-inherit"
12626
12628
  });
12627
12629
  } else {
12628
12630
  stillUnclassified.push(file);
@@ -12707,24 +12709,156 @@ function mergeUnclassifiedFiles(groups) {
12707
12709
  return capUnclassifiedFiles([...files]);
12708
12710
  }
12709
12711
 
12710
- // src/core-open/scanners/architecture/workspace-edges.ts
12712
+ // src/core-open/scanners/architecture/coverage.ts
12713
+ var UNCLASSIFIED_FOLDERS_CAP = 40;
12714
+ var UNCLASSIFIED_FOLDER_SAMPLE_CAP = 5;
12715
+ var COVERAGE_SOURCES = Object.freeze([
12716
+ "path",
12717
+ "suffix",
12718
+ "pascal",
12719
+ "folder-inherit",
12720
+ "graph",
12721
+ "ast",
12722
+ "declared"
12723
+ ]);
12724
+ function emptyBySource() {
12725
+ const out = {};
12726
+ for (const s of COVERAGE_SOURCES) out[s] = 0;
12727
+ return out;
12728
+ }
12729
+ function coverageRatio(classified, unclassified) {
12730
+ const total = classified + unclassified;
12731
+ if (total <= 0) return 0;
12732
+ return Math.round(classified / total * 1e4) / 1e4;
12733
+ }
12711
12734
  function normalizeRelPath2(p) {
12735
+ return (p || ".").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "");
12736
+ }
12737
+ function parentDirOf(filePath) {
12738
+ const n = normalizeRelPath2(filePath);
12739
+ const i = n.lastIndexOf("/");
12740
+ return i < 0 ? "." : n.slice(0, i);
12741
+ }
12742
+ function compareRelPath2(a, b) {
12743
+ return a.localeCompare(b);
12744
+ }
12745
+ function coverageSourceOf(c) {
12746
+ if (c.source) return c.source;
12747
+ const signals = c.signals ?? [];
12748
+ if (signals.some((s) => s === "graph-neighborhood" || s === "graph-override")) return "graph";
12749
+ if (signals.some((s) => s.startsWith("ast-role:"))) return "ast";
12750
+ if (signals.some((s) => s.startsWith("folder-inherit:"))) return "folder-inherit";
12751
+ return "path";
12752
+ }
12753
+ function buildCoverage(classifications, unclassified) {
12754
+ const bySource = emptyBySource();
12755
+ for (const c of classifications) bySource[coverageSourceOf(c)] += 1;
12756
+ const classified = classifications.length;
12757
+ return { classified, unclassified, ratio: coverageRatio(classified, unclassified), bySource };
12758
+ }
12759
+ function creditCoverage(result, source) {
12760
+ const coverage = result.coverage;
12761
+ if (!coverage) return;
12762
+ coverage.bySource[source] += 1;
12763
+ coverage.classified += 1;
12764
+ if (coverage.unclassified > 0) coverage.unclassified -= 1;
12765
+ coverage.ratio = coverageRatio(coverage.classified, coverage.unclassified);
12766
+ }
12767
+ function reattributeCoverage(result, from, to) {
12768
+ const coverage = result.coverage;
12769
+ if (!coverage || from === to) return;
12770
+ if (coverage.bySource[from] > 0) coverage.bySource[from] -= 1;
12771
+ coverage.bySource[to] += 1;
12772
+ }
12773
+ function buildUnclassifiedFolders(unclassifiedSource) {
12774
+ if (unclassifiedSource.length === 0) return { folders: [], capped: false };
12775
+ const byDir = /* @__PURE__ */ new Map();
12776
+ for (const raw of unclassifiedSource) {
12777
+ const file = normalizeRelPath2(raw);
12778
+ const dir = parentDirOf(file);
12779
+ const list = byDir.get(dir);
12780
+ if (list) list.push(file);
12781
+ else byDir.set(dir, [file]);
12782
+ }
12783
+ const all = [...byDir.entries()].map(([path37, files]) => ({
12784
+ path: path37,
12785
+ count: files.length,
12786
+ sampleFiles: [...files].sort(compareRelPath2).slice(0, UNCLASSIFIED_FOLDER_SAMPLE_CAP)
12787
+ })).sort((a, b) => b.count - a.count || compareRelPath2(a.path, b.path));
12788
+ return {
12789
+ folders: all.slice(0, UNCLASSIFIED_FOLDERS_CAP),
12790
+ capped: all.length > UNCLASSIFIED_FOLDERS_CAP
12791
+ };
12792
+ }
12793
+ function debitUnclassifiedFolder(result, filePath) {
12794
+ const rows = result.unclassifiedFolders;
12795
+ if (!rows || rows.length === 0) return;
12796
+ const file = normalizeRelPath2(filePath);
12797
+ const dir = parentDirOf(file);
12798
+ const row = rows.find((r) => r.path === dir);
12799
+ if (!row) return;
12800
+ row.count -= 1;
12801
+ row.sampleFiles = row.sampleFiles.filter((f) => normalizeRelPath2(f) !== file);
12802
+ const remaining = rows.filter((r) => r.count > 0);
12803
+ if (remaining.length === 0) {
12804
+ delete result.unclassifiedFolders;
12805
+ delete result.unclassifiedFoldersCapped;
12806
+ return;
12807
+ }
12808
+ remaining.sort((a, b) => b.count - a.count || compareRelPath2(a.path, b.path));
12809
+ result.unclassifiedFolders = remaining;
12810
+ }
12811
+ function mergeCoverage(parts) {
12812
+ const present = parts.filter((p) => !!p);
12813
+ if (present.length === 0) return void 0;
12814
+ const bySource = emptyBySource();
12815
+ let classified = 0;
12816
+ let unclassified = 0;
12817
+ for (const p of present) {
12818
+ classified += p.classified;
12819
+ unclassified += p.unclassified;
12820
+ for (const s of COVERAGE_SOURCES) bySource[s] += p.bySource[s] ?? 0;
12821
+ }
12822
+ return { classified, unclassified, ratio: coverageRatio(classified, unclassified), bySource };
12823
+ }
12824
+ function mergeUnclassifiedFolders(parts) {
12825
+ const byDir = /* @__PURE__ */ new Map();
12826
+ for (const part of parts) {
12827
+ for (const row of part ?? []) {
12828
+ const existing = byDir.get(row.path);
12829
+ if (!existing) {
12830
+ byDir.set(row.path, { path: row.path, count: row.count, sampleFiles: [...row.sampleFiles] });
12831
+ continue;
12832
+ }
12833
+ existing.count += row.count;
12834
+ existing.sampleFiles = [.../* @__PURE__ */ new Set([...existing.sampleFiles, ...row.sampleFiles])].sort(compareRelPath2).slice(0, UNCLASSIFIED_FOLDER_SAMPLE_CAP);
12835
+ }
12836
+ }
12837
+ const all = [...byDir.values()].sort((a, b) => b.count - a.count || compareRelPath2(a.path, b.path));
12838
+ return {
12839
+ folders: all.slice(0, UNCLASSIFIED_FOLDERS_CAP),
12840
+ capped: all.length > UNCLASSIFIED_FOLDERS_CAP
12841
+ };
12842
+ }
12843
+
12844
+ // src/core-open/scanners/architecture/workspace-edges.ts
12845
+ function normalizeRelPath3(p) {
12712
12846
  return (p || ".").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "") || ".";
12713
12847
  }
12714
12848
  function parentDir2(projectPath) {
12715
- const n = normalizeRelPath2(projectPath);
12849
+ const n = normalizeRelPath3(projectPath);
12716
12850
  if (n === ".") return ".";
12717
12851
  const i = n.lastIndexOf("/");
12718
12852
  return i < 0 ? "." : n.slice(0, i);
12719
12853
  }
12720
12854
  function isContractish(result, path37) {
12721
12855
  if (result.projectKind === "contract" || result.artifactKind === "contract") return true;
12722
- const n = normalizeRelPath2(path37);
12856
+ const n = normalizeRelPath3(path37);
12723
12857
  return /(^|\/)(proto|contracts|api-specs?)(\/|$)/.test(n);
12724
12858
  }
12725
12859
  function isIacish(result, path37) {
12726
12860
  if (result.projectKind === "iac" || result.artifactKind === "infrastructure-definition") return true;
12727
- return /(^|\/)(infra|infrastructure|terraform|iac)(\/|$)/.test(normalizeRelPath2(path37));
12861
+ return /(^|\/)(infra|infrastructure|terraform|iac)(\/|$)/.test(normalizeRelPath3(path37));
12728
12862
  }
12729
12863
  function isAppish(result) {
12730
12864
  const kind = result.projectKind;
@@ -12749,21 +12883,21 @@ function detectWorkspaceEdges(projects, results) {
12749
12883
  const to = projects[j];
12750
12884
  const toResult = results[j];
12751
12885
  const sameParent = parentDir2(to.path) === fromParent;
12752
- const contractAtRoot = isContractish(toResult, to.path) && /^(proto|contracts|api-specs?)(\/|$)/.test(normalizeRelPath2(to.path));
12753
- const iacAtRoot = isIacish(fromResult, from.path) && /^(infra|infrastructure|terraform|iac)(\/|$)/.test(normalizeRelPath2(from.path));
12886
+ const contractAtRoot = isContractish(toResult, to.path) && /^(proto|contracts|api-specs?)(\/|$)/.test(normalizeRelPath3(to.path));
12887
+ const iacAtRoot = isIacish(fromResult, from.path) && /^(infra|infrastructure|terraform|iac)(\/|$)/.test(normalizeRelPath3(from.path));
12754
12888
  if (isAppish(fromResult) && isContractish(toResult, to.path) && (sameParent || contractAtRoot)) {
12755
12889
  push({
12756
12890
  kind: "implements-contract",
12757
- fromProject: normalizeRelPath2(from.path),
12758
- toProject: normalizeRelPath2(to.path),
12891
+ fromProject: normalizeRelPath3(from.path),
12892
+ toProject: normalizeRelPath3(to.path),
12759
12893
  confidence: sameParent ? 0.72 : 0.64
12760
12894
  });
12761
12895
  }
12762
12896
  if (isIacish(fromResult, from.path) && isAppish(toResult) && (sameParent || iacAtRoot)) {
12763
12897
  push({
12764
12898
  kind: "provisions",
12765
- fromProject: normalizeRelPath2(from.path),
12766
- toProject: normalizeRelPath2(to.path),
12899
+ fromProject: normalizeRelPath3(from.path),
12900
+ toProject: normalizeRelPath3(to.path),
12767
12901
  confidence: sameParent ? 0.68 : 0.6
12768
12902
  });
12769
12903
  }
@@ -12774,7 +12908,7 @@ function detectWorkspaceEdges(projects, results) {
12774
12908
  function detectLocalContractEdges(projectPath, files, projectKind) {
12775
12909
  if (projectKind === "contract" || projectKind === "iac") return [];
12776
12910
  const edges = [];
12777
- const prefix = normalizeRelPath2(projectPath);
12911
+ const prefix = normalizeRelPath3(projectPath);
12778
12912
  for (const f of files) {
12779
12913
  const base = f.replace(/\\/g, "/").split("/").pop() ?? f;
12780
12914
  const ext = (() => {
@@ -13129,9 +13263,9 @@ function isDomainRepositoryPort(baseName, loweredPath) {
13129
13263
  if (!/^I[A-Z]\w*(Repository|Repo)$/.test(baseName)) return false;
13130
13264
  return /\/(domain|interfaces)\//.test(loweredPath);
13131
13265
  }
13132
- function considerRule(bestMatch, layer, confidence, signal) {
13266
+ function considerRule(bestMatch, layer, confidence, signal, source) {
13133
13267
  if (!bestMatch || confidence > bestMatch.confidence) {
13134
- return { layer, confidence, signal };
13268
+ return { layer, confidence, signal, source };
13135
13269
  }
13136
13270
  return bestMatch;
13137
13271
  }
@@ -13148,7 +13282,7 @@ function classifyOne(filePath, archetype, opts) {
13148
13282
  if (rule.pattern.test(lowered)) {
13149
13283
  const boost = rule.archetypes ? 0.05 : 0;
13150
13284
  const adjustedConfidence = Math.min(rule.confidence + boost, 1);
13151
- bestMatch = considerRule(bestMatch, rule.layer, adjustedConfidence, rule.signal);
13285
+ bestMatch = considerRule(bestMatch, rule.layer, adjustedConfidence, rule.signal, "path");
13152
13286
  }
13153
13287
  }
13154
13288
  for (const rule of opts?.extraPathRules ?? []) {
@@ -13156,19 +13290,19 @@ function classifyOne(filePath, archetype, opts) {
13156
13290
  continue;
13157
13291
  }
13158
13292
  if (rule.pattern.test(lowered)) {
13159
- bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal);
13293
+ bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal, "path");
13160
13294
  }
13161
13295
  }
13162
13296
  const baseName = path31.basename(filePath, path31.extname(filePath));
13163
13297
  const cleanBase = baseName.replace(/\.(test|spec)$/, "");
13164
13298
  for (const rule of SUFFIX_RULES) {
13165
13299
  if (cleanBase.endsWith(rule.suffix)) {
13166
- bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal);
13300
+ bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal, "suffix");
13167
13301
  }
13168
13302
  }
13169
13303
  for (const rule of opts?.extraSuffixRules ?? []) {
13170
13304
  if (cleanBase.endsWith(rule.suffix)) {
13171
- bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal);
13305
+ bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal, "suffix");
13172
13306
  }
13173
13307
  }
13174
13308
  const fileExt = path31.extname(filePath).toLowerCase();
@@ -13176,28 +13310,33 @@ function classifyOne(filePath, archetype, opts) {
13176
13310
  if (spa && SPA_SKIP_PASCAL.has(rule.suffix)) continue;
13177
13311
  if (rule.extensions && !rule.extensions.includes(fileExt)) continue;
13178
13312
  if (cleanBase.endsWith(rule.suffix)) {
13179
- bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal);
13313
+ bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal, "pascal");
13180
13314
  }
13181
13315
  }
13182
13316
  for (const rule of opts?.extraPascalSuffixRules ?? []) {
13183
13317
  if (spa && SPA_SKIP_PASCAL.has(rule.suffix)) continue;
13184
13318
  if (cleanBase.endsWith(rule.suffix)) {
13185
- bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal);
13319
+ bestMatch = considerRule(bestMatch, rule.layer, rule.confidence, rule.signal, "pascal");
13186
13320
  }
13187
13321
  }
13188
13322
  if (isDomainRepositoryPort(cleanBase, lowered)) {
13189
- const port = { layer: "domain", confidence: 0.93, signal: "domain repository port" };
13323
+ const port = {
13324
+ layer: "domain",
13325
+ confidence: 0.93,
13326
+ signal: "domain repository port",
13327
+ source: "pascal"
13328
+ };
13190
13329
  if (!bestMatch || port.confidence >= bestMatch.confidence || bestMatch.layer === "data-access") {
13191
13330
  bestMatch = port;
13192
13331
  }
13193
13332
  }
13194
13333
  if (bestMatch && (bestMatch.layer === "routing" || bestMatch.layer === "services") && /\/(model|entity|entities)\//.test(lowered) && /(Controller|Request|Response|Dto|DTO)$/.test(cleanBase)) {
13195
- bestMatch = { layer: "domain", confidence: 0.88, signal: "entity folder beats type suffix" };
13334
+ bestMatch = { layer: "domain", confidence: 0.88, signal: "entity folder beats type suffix", source: "path" };
13196
13335
  }
13197
13336
  if (!bestMatch) {
13198
13337
  const ext = path31.extname(filePath).toLowerCase();
13199
13338
  if (UI_SOURCE_EXTENSIONS.has(ext)) {
13200
- bestMatch = { layer: "presentation", confidence: 0.62, signal: "ui source extension" };
13339
+ bestMatch = { layer: "presentation", confidence: 0.62, signal: "ui source extension", source: "suffix" };
13201
13340
  }
13202
13341
  }
13203
13342
  return bestMatch;
@@ -13226,7 +13365,8 @@ function classifyFile(filePath, archetype, opts) {
13226
13365
  filePath,
13227
13366
  layer: "testing",
13228
13367
  confidence: 0.9,
13229
- signals: ["test project prefix"]
13368
+ signals: ["test project prefix"],
13369
+ source: "path"
13230
13370
  };
13231
13371
  }
13232
13372
  const local = classifyOne(filePath, archetype, opts);
@@ -13243,7 +13383,8 @@ function classifyFile(filePath, archetype, opts) {
13243
13383
  filePath,
13244
13384
  layer: best.layer,
13245
13385
  confidence: best.confidence,
13246
- signals: usedHint ? [best.signal, "project-path-hint"] : [best.signal]
13386
+ signals: usedHint ? [best.signal, "project-path-hint"] : [best.signal],
13387
+ source: best.source
13247
13388
  };
13248
13389
  }
13249
13390
 
@@ -13285,14 +13426,14 @@ var CLEAN_PROFILES = /* @__PURE__ */ new Set(["clean", "hexagonal", "onion"]);
13285
13426
  var GRAPH_OVERRIDE_SIGNAL = "graph-override";
13286
13427
  var GRAPH_NEIGHBORHOOD_SIGNAL = "graph-neighborhood";
13287
13428
  var GRAPH_CONFIRM_SIGNAL = "graph-confirm";
13288
- function normalizeRelPath3(p) {
13429
+ function normalizeRelPath4(p) {
13289
13430
  return (p || ".").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "") || ".";
13290
13431
  }
13291
- function compareRelPath2(a, b) {
13292
- return normalizeRelPath3(a).localeCompare(normalizeRelPath3(b));
13432
+ function compareRelPath3(a, b) {
13433
+ return normalizeRelPath4(a).localeCompare(normalizeRelPath4(b));
13293
13434
  }
13294
13435
  function parentDir3(filePath) {
13295
- const n = normalizeRelPath3(filePath);
13436
+ const n = normalizeRelPath4(filePath);
13296
13437
  if (n === ".") return ".";
13297
13438
  const i = n.lastIndexOf("/");
13298
13439
  return i <= 0 ? "." : n.slice(0, i);
@@ -13307,8 +13448,8 @@ function ancestorDirs2(filePath) {
13307
13448
  return dirs;
13308
13449
  }
13309
13450
  function pathOwnsFile(projectPath, file) {
13310
- const prefix = normalizeRelPath3(projectPath);
13311
- const f = normalizeRelPath3(file);
13451
+ const prefix = normalizeRelPath4(projectPath);
13452
+ const f = normalizeRelPath4(file);
13312
13453
  if (prefix === ".") return true;
13313
13454
  return f === prefix || f.startsWith(`${prefix}/`);
13314
13455
  }
@@ -13316,7 +13457,7 @@ function owningProjectPath(file, projectPaths) {
13316
13457
  let best = ".";
13317
13458
  let bestLen = -1;
13318
13459
  for (const raw of projectPaths) {
13319
- const p = normalizeRelPath3(raw);
13460
+ const p = normalizeRelPath4(raw);
13320
13461
  if (!pathOwnsFile(p, file)) continue;
13321
13462
  const len = p === "." ? 0 : p.length;
13322
13463
  if (len > bestLen) {
@@ -13334,32 +13475,39 @@ function isLayerSkipped(result) {
13334
13475
  }
13335
13476
  function nearestFolder(filePath, folders) {
13336
13477
  for (const ancestor of ancestorDirs2(filePath)) {
13337
- const folder = folders.find((f) => normalizeRelPath3(f.path) === ancestor);
13478
+ const folder = folders.find((f) => normalizeRelPath4(f.path) === ancestor);
13338
13479
  if (folder) return folder;
13339
13480
  }
13340
13481
  return void 0;
13341
13482
  }
13342
13483
  function fileInLayerSample(result, filePath) {
13343
- const n = normalizeRelPath3(filePath);
13484
+ const n = normalizeRelPath4(filePath);
13344
13485
  for (const layer of result.layers) {
13345
- if (layer.files.some((f) => normalizeRelPath3(f) === n)) return layer.layer;
13486
+ if (layer.files.some((f) => normalizeRelPath4(f) === n)) return layer.layer;
13346
13487
  }
13347
13488
  return void 0;
13348
13489
  }
13490
+ function decidingSource(result, filePath) {
13491
+ const hit = (result.classified ?? []).find((c) => normalizeRelPath4(c.filePath) === filePath);
13492
+ if (hit?.source) return hit.source;
13493
+ const reclassified = classifyFile(filePath, result.archetype, { projectKind: result.projectKind });
13494
+ return reclassified?.source ?? "path";
13495
+ }
13349
13496
  function buildFileState(filePath, result) {
13350
- const n = normalizeRelPath3(filePath);
13497
+ const n = normalizeRelPath4(filePath);
13351
13498
  const sampleLayer = fileInLayerSample(result, n);
13352
13499
  const folder = nearestFolder(n, result.folders ?? []);
13353
13500
  if (sampleLayer) {
13354
13501
  const confidence = folder && folder.layer === sampleLayer ? folder.confidence : folder && folder.layer !== sampleLayer ? Math.max(DEFAULT_PATH_CONFIDENCE, 0.85) : DEFAULT_PATH_CONFIDENCE;
13355
- return { filePath: n, layer: sampleLayer, confidence, signals: [] };
13502
+ return { filePath: n, layer: sampleLayer, confidence, signals: [], source: decidingSource(result, n) };
13356
13503
  }
13357
13504
  if (folder && folder.confidence >= FOLDER_INHERIT_MIN_CONFIDENCE) {
13358
13505
  return {
13359
13506
  filePath: n,
13360
13507
  layer: folder.layer,
13361
13508
  confidence: folder.confidence,
13362
- signals: [folderInheritSignal(folder.layer)]
13509
+ signals: [folderInheritSignal(folder.layer)],
13510
+ source: "folder-inherit"
13363
13511
  };
13364
13512
  }
13365
13513
  return { filePath: n, layer: void 0, confidence: 0, signals: [] };
@@ -13367,9 +13515,9 @@ function buildFileState(filePath, result) {
13367
13515
  function collectKnownFiles(result) {
13368
13516
  const files = /* @__PURE__ */ new Set();
13369
13517
  for (const layer of result.layers) {
13370
- for (const f of layer.files) files.add(normalizeRelPath3(f));
13518
+ for (const f of layer.files) files.add(normalizeRelPath4(f));
13371
13519
  }
13372
- for (const f of result.unclassifiedFiles ?? []) files.add(normalizeRelPath3(f));
13520
+ for (const f of result.unclassifiedFiles ?? []) files.add(normalizeRelPath4(f));
13373
13521
  return [...files];
13374
13522
  }
13375
13523
  function pushEdge(map, key, edge) {
@@ -13382,7 +13530,7 @@ function indexGraph(graph) {
13382
13530
  const fileNodes = /* @__PURE__ */ new Map();
13383
13531
  for (const node of graph.nodes) {
13384
13532
  if (!node.file) continue;
13385
- const file = normalizeRelPath3(node.file);
13533
+ const file = normalizeRelPath4(node.file);
13386
13534
  nodeFile.set(node.id, file);
13387
13535
  const list = fileNodes.get(file);
13388
13536
  if (list) list.push(node.id);
@@ -13469,18 +13617,18 @@ function ensureLayer(result, layer) {
13469
13617
  return summary;
13470
13618
  }
13471
13619
  function removeFileFromLayer(summary, filePath) {
13472
- const n = normalizeRelPath3(filePath);
13473
- const next = summary.files.filter((f) => normalizeRelPath3(f) !== n);
13620
+ const n = normalizeRelPath4(filePath);
13621
+ const next = summary.files.filter((f) => normalizeRelPath4(f) !== n);
13474
13622
  if (next.length === summary.files.length) return;
13475
13623
  summary.files = next;
13476
13624
  if (summary.fileCount > 0) summary.fileCount -= 1;
13477
13625
  }
13478
13626
  function addFileToLayer(summary, filePath) {
13479
- const n = normalizeRelPath3(filePath);
13480
- if (summary.files.some((f) => normalizeRelPath3(f) === n)) return;
13627
+ const n = normalizeRelPath4(filePath);
13628
+ if (summary.files.some((f) => normalizeRelPath4(f) === n)) return;
13481
13629
  if (summary.files.length < LAYER_FILES_CAP) {
13482
13630
  summary.files.push(n);
13483
- summary.files.sort(compareRelPath2);
13631
+ summary.files.sort(compareRelPath3);
13484
13632
  }
13485
13633
  summary.fileCount += 1;
13486
13634
  }
@@ -13556,13 +13704,16 @@ function selectBoundaryProfile(structure, result) {
13556
13704
  return void 0;
13557
13705
  }
13558
13706
  function sliceOf(filePath) {
13559
- const m = normalizeRelPath3(filePath).match(/(?:^|\/)(?:features|modules|slices)\/([^/]+)/i);
13707
+ const m = normalizeRelPath4(filePath).match(/(?:^|\/)(?:features|modules|slices)\/([^/]+)/i);
13560
13708
  return m?.[1]?.toLowerCase();
13561
13709
  }
13562
13710
  function isSliceInternal(filePath) {
13563
- const base = normalizeRelPath3(filePath).split("/").pop() ?? "";
13711
+ const base = normalizeRelPath4(filePath).split("/").pop() ?? "";
13564
13712
  return !/^(index|public|api|mod)\.[^./]+$/i.test(base);
13565
13713
  }
13714
+ function evaluateLayerBoundary(profile, from, to, fromFile, toFile, projectKind) {
13715
+ return isIllegalEdge(profile, from, to, fromFile, toFile, projectKind);
13716
+ }
13566
13717
  function isIllegalEdge(profile, from, to, fromFile, toFile, projectKind) {
13567
13718
  if (profile === "vertical-slice") {
13568
13719
  const fromSlice = sliceOf(fromFile);
@@ -13637,14 +13788,14 @@ function fileInScope(filePath, projectPath) {
13637
13788
  }
13638
13789
  function applyConfirmation(result, graph, projectPath, index) {
13639
13790
  if (isLayerSkipped(result)) return { conflicts: [], states: /* @__PURE__ */ new Map() };
13640
- const unclassified = new Set((result.unclassifiedFiles ?? []).map(normalizeRelPath3));
13791
+ const unclassified = new Set((result.unclassifiedFiles ?? []).map(normalizeRelPath4));
13641
13792
  const known = new Set(collectKnownFiles(result));
13642
13793
  for (const file of index.fileNodes.keys()) {
13643
13794
  if (fileInScope(file, projectPath)) known.add(file);
13644
13795
  }
13645
13796
  const states = /* @__PURE__ */ new Map();
13646
13797
  const stateOf = (file) => {
13647
- const n = normalizeRelPath3(file);
13798
+ const n = normalizeRelPath4(file);
13648
13799
  const existing = states.get(n);
13649
13800
  if (existing) return existing;
13650
13801
  const built = buildFileState(n, result);
@@ -13653,7 +13804,7 @@ function applyConfirmation(result, graph, projectPath, index) {
13653
13804
  };
13654
13805
  for (const file of known) stateOf(file);
13655
13806
  const conflicts = [];
13656
- const scopedFiles = [...known].filter((f) => fileInScope(f, projectPath)).sort(compareRelPath2);
13807
+ const scopedFiles = [...known].filter((f) => fileInScope(f, projectPath)).sort(compareRelPath3);
13657
13808
  for (const file of scopedFiles) {
13658
13809
  const state = stateOf(file);
13659
13810
  const votes = neighbourhoodVotes(file, index, stateOf);
@@ -13667,9 +13818,12 @@ function applyConfirmation(result, graph, projectPath, index) {
13667
13818
  state.signals = sortedSignals2([GRAPH_NEIGHBORHOOD_SIGNAL, ...extra]);
13668
13819
  const target = ensureLayer(result, majority.layer);
13669
13820
  addFileToLayer(target, file);
13821
+ state.source = "graph";
13670
13822
  result.totalClassified += 1;
13671
13823
  unclassified.delete(file);
13672
13824
  if (result.unclassified > 0) result.unclassified -= 1;
13825
+ creditCoverage(result, "graph");
13826
+ debitUnclassifiedFolder(result, file);
13673
13827
  addFolderSignal(result, file, GRAPH_NEIGHBORHOOD_SIGNAL);
13674
13828
  continue;
13675
13829
  }
@@ -13688,6 +13842,8 @@ function applyConfirmation(result, graph, projectPath, index) {
13688
13842
  state.layer = majority.layer;
13689
13843
  state.confidence = GRAPH_ASSIGN_CONFIDENCE;
13690
13844
  state.signals = sortedSignals2([...state.signals, GRAPH_OVERRIDE_SIGNAL, ...extra]);
13845
+ reattributeCoverage(result, state.source ?? "path", "graph");
13846
+ state.source = "graph";
13691
13847
  addFileToLayer(ensureLayer(result, majority.layer), file);
13692
13848
  addFolderSignal(result, file, GRAPH_OVERRIDE_SIGNAL);
13693
13849
  continue;
@@ -13805,13 +13961,13 @@ function refineArchitectureResult(result, graph, opts = {}) {
13805
13961
  }
13806
13962
  function refineArchitectureWithGraph(artifact, graph) {
13807
13963
  const projects = artifact.projects.map((p) => ({
13808
- path: normalizeRelPath3(p.path),
13964
+ path: normalizeRelPath4(p.path),
13809
13965
  name: p.name
13810
13966
  }));
13811
13967
  for (const project of artifact.projects) {
13812
13968
  if (!project.architecture) continue;
13813
13969
  refineArchitectureResult(project.architecture, graph, {
13814
- projectPath: normalizeRelPath3(project.path),
13970
+ projectPath: normalizeRelPath4(project.path),
13815
13971
  projects
13816
13972
  });
13817
13973
  project.architectureMermaid = mermaidFromArchitecture(project.architecture);
@@ -13859,17 +14015,17 @@ var FILE_ROLES = /* @__PURE__ */ new Set([
13859
14015
  "handler",
13860
14016
  "router"
13861
14017
  ]);
13862
- function normalizeRelPath4(p) {
14018
+ function normalizeRelPath5(p) {
13863
14019
  return (p || ".").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "") || ".";
13864
14020
  }
13865
- function compareRelPath3(a, b) {
13866
- return normalizeRelPath4(a).localeCompare(normalizeRelPath4(b));
14021
+ function compareRelPath4(a, b) {
14022
+ return normalizeRelPath5(a).localeCompare(normalizeRelPath5(b));
13867
14023
  }
13868
14024
  function sortedSignals3(signals) {
13869
14025
  return [...new Set(signals)].sort((a, b) => a.localeCompare(b));
13870
14026
  }
13871
14027
  function parentDir4(filePath) {
13872
- const n = normalizeRelPath4(filePath);
14028
+ const n = normalizeRelPath5(filePath);
13873
14029
  if (n === ".") return ".";
13874
14030
  const i = n.lastIndexOf("/");
13875
14031
  return i <= 0 ? "." : n.slice(0, i);
@@ -13884,8 +14040,8 @@ function ancestorDirs3(filePath) {
13884
14040
  return dirs;
13885
14041
  }
13886
14042
  function pathOwnsFile2(projectPath, file) {
13887
- const prefix = normalizeRelPath4(projectPath);
13888
- const f = normalizeRelPath4(file);
14043
+ const prefix = normalizeRelPath5(projectPath);
14044
+ const f = normalizeRelPath5(file);
13889
14045
  if (prefix === ".") return true;
13890
14046
  return f === prefix || f.startsWith(`${prefix}/`);
13891
14047
  }
@@ -13899,32 +14055,32 @@ function pickBestHits(hits) {
13899
14055
  const byFile = /* @__PURE__ */ new Map();
13900
14056
  for (const hit of hits) {
13901
14057
  if (!FILE_ROLES.has(hit.role)) continue;
13902
- const key = normalizeRelPath4(hit.filePath);
14058
+ const key = normalizeRelPath5(hit.filePath);
13903
14059
  const normalized = { ...hit, filePath: key };
13904
14060
  const existing = byFile.get(key);
13905
14061
  if (!existing || compareHits(normalized, existing) < 0) {
13906
14062
  byFile.set(key, normalized);
13907
14063
  }
13908
14064
  }
13909
- return [...byFile.values()].sort((a, b) => compareRelPath3(a.filePath, b.filePath));
14065
+ return [...byFile.values()].sort((a, b) => compareRelPath4(a.filePath, b.filePath));
13910
14066
  }
13911
14067
  function nearestFolder2(filePath, folders) {
13912
14068
  for (const ancestor of ancestorDirs3(filePath)) {
13913
- const folder = folders.find((f) => normalizeRelPath4(f.path) === ancestor);
14069
+ const folder = folders.find((f) => normalizeRelPath5(f.path) === ancestor);
13914
14070
  if (folder) return folder;
13915
14071
  }
13916
14072
  return void 0;
13917
14073
  }
13918
14074
  function fileInLayerSample2(result, filePath) {
13919
- const n = normalizeRelPath4(filePath);
14075
+ const n = normalizeRelPath5(filePath);
13920
14076
  for (const layer of result.layers) {
13921
- if (layer.files.some((f) => normalizeRelPath4(f) === n)) return layer.layer;
14077
+ if (layer.files.some((f) => normalizeRelPath5(f) === n)) return layer.layer;
13922
14078
  }
13923
14079
  return void 0;
13924
14080
  }
13925
14081
  function existingRole(result, filePath) {
13926
- const n = normalizeRelPath4(filePath);
13927
- return result.classified?.find((c) => normalizeRelPath4(c.filePath) === n)?.role;
14082
+ const n = normalizeRelPath5(filePath);
14083
+ return result.classified?.find((c) => normalizeRelPath5(c.filePath) === n)?.role;
13928
14084
  }
13929
14085
  function pathPriorConfidence(folder, pathLayer, fallback) {
13930
14086
  if (folder && folder.layer === pathLayer) return folder.confidence;
@@ -13932,7 +14088,7 @@ function pathPriorConfidence(folder, pathLayer, fallback) {
13932
14088
  return fallback;
13933
14089
  }
13934
14090
  function buildFileState2(filePath, result) {
13935
- const n = normalizeRelPath4(filePath);
14091
+ const n = normalizeRelPath5(filePath);
13936
14092
  const sampleLayer = fileInLayerSample2(result, n);
13937
14093
  const folder = nearestFolder2(n, result.folders ?? []);
13938
14094
  const role = existingRole(result, n);
@@ -13984,11 +14140,11 @@ function ensureLayer2(result, layer) {
13984
14140
  return summary;
13985
14141
  }
13986
14142
  function addFileToLayer2(summary, filePath) {
13987
- const n = normalizeRelPath4(filePath);
13988
- if (summary.files.some((f) => normalizeRelPath4(f) === n)) return;
14143
+ const n = normalizeRelPath5(filePath);
14144
+ if (summary.files.some((f) => normalizeRelPath5(f) === n)) return;
13989
14145
  if (summary.files.length < LAYER_FILES_CAP2) {
13990
14146
  summary.files.push(n);
13991
- summary.files.sort(compareRelPath3);
14147
+ summary.files.sort(compareRelPath4);
13992
14148
  }
13993
14149
  summary.fileCount += 1;
13994
14150
  }
@@ -14002,7 +14158,7 @@ function raiseSupportLevel(result) {
14002
14158
  }
14003
14159
  function persistClassified(result, classified) {
14004
14160
  const sorted = [...classified].sort(
14005
- (a, b) => compareRelPath3(a.filePath, b.filePath) || a.role.localeCompare(b.role)
14161
+ (a, b) => compareRelPath4(a.filePath, b.filePath) || a.role.localeCompare(b.role)
14006
14162
  );
14007
14163
  if (sorted.length <= CLASSIFIED_ROLE_CAP) {
14008
14164
  if (sorted.length > 0) result.classified = sorted;
@@ -14010,9 +14166,9 @@ function persistClassified(result, classified) {
14010
14166
  return;
14011
14167
  }
14012
14168
  const ranked = [...sorted].sort(
14013
- (a, b) => b.confidence - a.confidence || compareRelPath3(a.filePath, b.filePath) || (a.role ?? "").localeCompare(b.role ?? "")
14169
+ (a, b) => b.confidence - a.confidence || compareRelPath4(a.filePath, b.filePath) || (a.role ?? "").localeCompare(b.role ?? "")
14014
14170
  );
14015
- result.classified = ranked.slice(0, CLASSIFIED_ROLE_CAP).sort((a, b) => compareRelPath3(a.filePath, b.filePath));
14171
+ result.classified = ranked.slice(0, CLASSIFIED_ROLE_CAP).sort((a, b) => compareRelPath4(a.filePath, b.filePath));
14016
14172
  }
14017
14173
  function applyAstRoles(result, hits, opts = {}) {
14018
14174
  if (isLayerSkipped2(result)) return;
@@ -14024,10 +14180,10 @@ function applyAstRoles(result, hits, opts = {}) {
14024
14180
  return true;
14025
14181
  });
14026
14182
  if (best.length === 0) return;
14027
- const unclassified = new Set((result.unclassifiedFiles ?? []).map(normalizeRelPath4));
14183
+ const unclassified = new Set((result.unclassifiedFiles ?? []).map(normalizeRelPath5));
14028
14184
  const classified = /* @__PURE__ */ new Map();
14029
14185
  for (const existing of result.classified ?? []) {
14030
- classified.set(normalizeRelPath4(existing.filePath), {
14186
+ classified.set(normalizeRelPath5(existing.filePath), {
14031
14187
  ...existing,
14032
14188
  signals: [...existing.signals]
14033
14189
  });
@@ -14046,14 +14202,16 @@ function applyAstRoles(result, hits, opts = {}) {
14046
14202
  layer: hit.layer,
14047
14203
  confidence: hit.confidence,
14048
14204
  signals: signals2,
14049
- role: hit.role
14205
+ role: hit.role,
14206
+ source: "ast"
14050
14207
  };
14051
14208
  classified.set(hit.filePath, entry);
14052
14209
  addFileToLayer2(ensureLayer2(result, hit.layer), hit.filePath);
14053
14210
  result.totalClassified += 1;
14054
- if (unclassified.delete(hit.filePath) && result.unclassified > 0) {
14055
- result.unclassified -= 1;
14056
- }
14211
+ unclassified.delete(hit.filePath);
14212
+ if (result.unclassified > 0) result.unclassified -= 1;
14213
+ creditCoverage(result, "ast");
14214
+ debitUnclassifiedFolder(result, hit.filePath);
14057
14215
  addFolderSignal2(result, hit.filePath, `${AST_ROLE_SIGNAL_PREFIX}${hit.role}`);
14058
14216
  applied = true;
14059
14217
  continue;
@@ -14082,8 +14240,8 @@ function applyAstRoles(result, hits, opts = {}) {
14082
14240
  const previous = state.layer;
14083
14241
  const fromSummary = result.layers.find((l) => l.layer === previous);
14084
14242
  if (fromSummary) {
14085
- const n = normalizeRelPath4(hit.filePath);
14086
- const next = fromSummary.files.filter((f) => normalizeRelPath4(f) !== n);
14243
+ const n = normalizeRelPath5(hit.filePath);
14244
+ const next = fromSummary.files.filter((f) => normalizeRelPath5(f) !== n);
14087
14245
  if (next.length !== fromSummary.files.length && fromSummary.fileCount > 0) {
14088
14246
  fromSummary.fileCount -= 1;
14089
14247
  }
@@ -14098,12 +14256,14 @@ function applyAstRoles(result, hits, opts = {}) {
14098
14256
  "ast-overrides-suffix",
14099
14257
  hit.signal
14100
14258
  ]);
14259
+ reattributeCoverage(result, already?.source ?? "suffix", "ast");
14101
14260
  classified.set(hit.filePath, {
14102
14261
  filePath: hit.filePath,
14103
14262
  layer: hit.layer,
14104
14263
  confidence: hit.confidence,
14105
14264
  signals: signals2,
14106
- role: hit.role
14265
+ role: hit.role,
14266
+ source: "ast"
14107
14267
  });
14108
14268
  addFileToLayer2(ensureLayer2(result, hit.layer), hit.filePath);
14109
14269
  addFolderSignal2(result, hit.filePath, `${AST_ROLE_SIGNAL_PREFIX}${hit.role}`);
@@ -14138,7 +14298,7 @@ function refineArchitectureWithAstRoles(artifact, hits) {
14138
14298
  if (hits.length === 0) return;
14139
14299
  for (const project of artifact.projects) {
14140
14300
  if (!project.architecture) continue;
14141
- applyAstRoles(project.architecture, hits, { projectPath: normalizeRelPath4(project.path) });
14301
+ applyAstRoles(project.architecture, hits, { projectPath: normalizeRelPath5(project.path) });
14142
14302
  project.architectureMermaid = mermaidFromArchitecture(project.architecture);
14143
14303
  }
14144
14304
  if (artifact.extended?.architecture) {
@@ -14356,20 +14516,20 @@ function residualProject(seed) {
14356
14516
  dependencyAgeBuckets: { current: 0, oneBehind: 0, twoPlusBehind: 0, unknown: 0 }
14357
14517
  };
14358
14518
  }
14359
- function compareRelPath4(a, b) {
14519
+ function compareRelPath5(a, b) {
14360
14520
  return a.replace(/\\/g, "/").localeCompare(b.replace(/\\/g, "/"));
14361
14521
  }
14362
- function normalizeRelPath5(p) {
14522
+ function normalizeRelPath6(p) {
14363
14523
  return (p || ".").replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "") || ".";
14364
14524
  }
14365
14525
  function toRepoRelative(projectPath, filePath) {
14366
14526
  const normalised = filePath.replace(/\\/g, "/");
14367
- const prefix = normalizeRelPath5(projectPath);
14527
+ const prefix = normalizeRelPath6(projectPath);
14368
14528
  if (prefix === ".") return normalised;
14369
14529
  return `${prefix}/${normalised}`;
14370
14530
  }
14371
14531
  function pathOwnsFile3(projectPath, file) {
14372
- const prefix = normalizeRelPath5(projectPath);
14532
+ const prefix = normalizeRelPath6(projectPath);
14373
14533
  const f = file.replace(/\\/g, "/");
14374
14534
  if (prefix === ".") return true;
14375
14535
  return f === prefix || f.startsWith(`${prefix}/`);
@@ -14378,7 +14538,7 @@ function owningProjectPath2(file, projectPaths) {
14378
14538
  let best = ".";
14379
14539
  let bestLen = -1;
14380
14540
  for (const raw of projectPaths) {
14381
- const p = normalizeRelPath5(raw);
14541
+ const p = normalizeRelPath6(raw);
14382
14542
  if (!pathOwnsFile3(p, file)) continue;
14383
14543
  const len = p === "." ? 0 : p.length;
14384
14544
  if (len > bestLen) {
@@ -14755,7 +14915,7 @@ function uniqueResultsForAggregate(projectResults, projects) {
14755
14915
  const byPath = /* @__PURE__ */ new Map();
14756
14916
  const ownerOverlay = /* @__PURE__ */ new Map();
14757
14917
  for (let i = 0; i < projects.length; i++) {
14758
- const key = normalizeRelPath5(projects[i].path);
14918
+ const key = normalizeRelPath6(projects[i].path);
14759
14919
  const overlay = isOverlayProject(projects[i]);
14760
14920
  const existing = byPath.get(key);
14761
14921
  if (!existing) {
@@ -14796,7 +14956,7 @@ function aggregateSolutionArchitecture(projectResults, projects) {
14796
14956
  for (const ls of r.layers) {
14797
14957
  const existing = layerMap.get(ls.layer);
14798
14958
  if (!existing) {
14799
- const files = [...ls.files].sort(compareRelPath4).slice(0, 40);
14959
+ const files = [...ls.files].sort(compareRelPath5).slice(0, 40);
14800
14960
  layerMap.set(ls.layer, { ...ls, packages: [...ls.packages], techStack: [...ls.techStack], services: [...ls.services], files });
14801
14961
  } else {
14802
14962
  existing.fileCount += ls.fileCount;
@@ -14828,7 +14988,7 @@ function aggregateSolutionArchitecture(projectResults, projects) {
14828
14988
  seenFiles.add(f);
14829
14989
  }
14830
14990
  }
14831
- existing.files.sort(compareRelPath4);
14991
+ existing.files.sort(compareRelPath5);
14832
14992
  if (existing.files.length > 40) existing.files.length = 40;
14833
14993
  const { score, riskLevel } = computeLayerDrift(existing.packages);
14834
14994
  existing.driftScore = score;
@@ -14887,6 +15047,15 @@ function applyAggregatedPackFields(target, unique) {
14887
15047
  const unclassifiedFiles = mergeUnclassifiedFiles(unique.map((r) => r.unclassifiedFiles));
14888
15048
  if (unclassifiedFiles) target.unclassifiedFiles = unclassifiedFiles;
14889
15049
  else delete target.unclassifiedFiles;
15050
+ const coverage = mergeCoverage(unique.map((r) => r.coverage));
15051
+ if (coverage) target.coverage = coverage;
15052
+ else delete target.coverage;
15053
+ const groupedFolders = mergeUnclassifiedFolders(unique.map((r) => r.unclassifiedFolders));
15054
+ if (groupedFolders.folders.length > 0) target.unclassifiedFolders = groupedFolders.folders;
15055
+ else delete target.unclassifiedFolders;
15056
+ if (groupedFolders.capped || unique.some((r) => r.unclassifiedFoldersCapped)) {
15057
+ target.unclassifiedFoldersCapped = true;
15058
+ } else delete target.unclassifiedFoldersCapped;
14890
15059
  const payload = capEvidence(allEvidence);
14891
15060
  if (payload.evidence) target.evidence = payload.evidence;
14892
15061
  if (payload.evidenceCapped || unique.some((r) => r.evidenceCapped)) target.evidenceCapped = true;
@@ -14990,10 +15159,10 @@ async function scanArchitectureBundle(rootDir, projects, tooling, services, cach
14990
15159
  workspace2.workspaceShape = "single";
14991
15160
  return { workspace: workspace2, projectResults: [] };
14992
15161
  }
14993
- const sorted = [...projects].sort((a, b) => compareRelPath4(a.path, b.path) || a.name.localeCompare(b.name));
15162
+ const sorted = [...projects].sort((a, b) => compareRelPath5(a.path, b.path) || a.name.localeCompare(b.name));
14994
15163
  const ownerByPath = /* @__PURE__ */ new Map();
14995
15164
  for (const p of sorted) {
14996
- const key = normalizeRelPath5(p.path);
15165
+ const key = normalizeRelPath6(p.path);
14997
15166
  const existing = ownerByPath.get(key);
14998
15167
  if (!existing || isOverlayProject(existing) && !isOverlayProject(p)) {
14999
15168
  ownerByPath.set(key, p);
@@ -15003,11 +15172,11 @@ async function scanArchitectureBundle(rootDir, projects, tooling, services, cach
15003
15172
  const resultByPath = /* @__PURE__ */ new Map();
15004
15173
  await Promise.all(owners.map(async (owner) => {
15005
15174
  const result = await scanProjectArchitecture(rootDir, owner, cache, owners);
15006
- resultByPath.set(normalizeRelPath5(owner.path), result);
15175
+ resultByPath.set(normalizeRelPath6(owner.path), result);
15007
15176
  }));
15008
- const projectResults = projects.map((p) => resultByPath.get(normalizeRelPath5(p.path)));
15177
+ const projectResults = projects.map((p) => resultByPath.get(normalizeRelPath6(p.path)));
15009
15178
  const uniqueResults = [...resultByPath.values()];
15010
- const ownerPaths = owners.map((o) => normalizeRelPath5(o.path));
15179
+ const ownerPaths = owners.map((o) => normalizeRelPath6(o.path));
15011
15180
  if (!ownerPaths.includes(".")) {
15012
15181
  const residual = await classifyTree(rootDir, [residualProject(owners[0])], tooling, services, cache, {
15013
15182
  projectPath: ".",
@@ -15035,8 +15204,8 @@ async function classifyTree(rootDir, projects, tooling, services, cache, scope)
15035
15204
  const detected = detectArchetype(projects);
15036
15205
  let sourceFiles = await walkSourceFiles(rootDir, cache);
15037
15206
  if (scope) {
15038
- const owner = normalizeRelPath5(scope.projectPath);
15039
- const uniquePaths = [...new Set(scope.workspacePaths.map(normalizeRelPath5))];
15207
+ const owner = normalizeRelPath6(scope.projectPath);
15208
+ const uniquePaths = [...new Set(scope.workspacePaths.map(normalizeRelPath6))];
15040
15209
  sourceFiles = sourceFiles.filter((f) => {
15041
15210
  const repoRel = toRepoRelative(scope.projectPath, f);
15042
15211
  return owningProjectPath2(repoRel, uniquePaths) === owner;
@@ -15058,7 +15227,7 @@ async function classifyTree(rootDir, projects, tooling, services, cache, scope)
15058
15227
  const artifactKind = fusion.winners.get("artifactKind")?.value;
15059
15228
  const skipAllLayers = projectKind === "iac" || projectKind === "contract";
15060
15229
  const packRules = collectPackLayerRules(matching);
15061
- sourceFiles.sort(compareRelPath4);
15230
+ sourceFiles.sort(compareRelPath5);
15062
15231
  const seedClassified = [];
15063
15232
  const seedUnclassified = [];
15064
15233
  for (const file of sourceFiles) {
@@ -15168,6 +15337,12 @@ async function classifyTree(rootDir, projects, tooling, services, cache, scope)
15168
15337
  if (!skipAllLayers && inherited.folders.length > 0) result.folders = inherited.folders;
15169
15338
  const unclassifiedSample = capUnclassifiedFiles(inherited.unclassifiedSource);
15170
15339
  if (unclassifiedSample) result.unclassifiedFiles = unclassifiedSample;
15340
+ if (!skipAllLayers && (classifications.length > 0 || unclassified > 0)) {
15341
+ result.coverage = buildCoverage(classifications, unclassified);
15342
+ const grouped = buildUnclassifiedFolders(inherited.unclassifiedSource);
15343
+ if (grouped.folders.length > 0) result.unclassifiedFolders = grouped.folders;
15344
+ if (grouped.capped) result.unclassifiedFoldersCapped = true;
15345
+ }
15171
15346
  const supportLevel = computeSupportLevel(fusion, classifications.length, primary.type);
15172
15347
  result.supportLevel = supportLevel;
15173
15348
  if (fusion.packIds.length > 0) result.packIds = fusion.packIds;
@@ -15953,6 +16128,6 @@ async function computeTreeMetadataHash(rootDir, options) {
15953
16128
  return digest.digest("hex");
15954
16129
  }
15955
16130
 
15956
- export { PROJECT_TYPE_TO_OSV_ECOSYSTEM, aggregateSolutionArchitecture, boundaryProfileRules, brandProgressBar, buildVersionTimelines, compactUiPurpose, computeDriftScore, computeRepoFingerprint, computeUpgradeImpact, detectVcs, driftBar, fetchRiskySymbols, fetchScanPreflight, findPackageAnyEcosystem, findPackageTimeline, findVersionCrossings, getChangelogSignals, gitHistoryAvailable, loadConfig, mermaidFromArchitecture, normalizeConstraint, parseDsn, perDependencyDrift, prepareCompressedUpload, projectTypeToVulnEcosystem, refineArchitectureWithAstRoles, refineArchitectureWithGraph, resolveHead, resolveRepositoryName, runCoreScan, scanArchitectureBundle, severityRank, titleBox, versionSatisfies, workingTreeDirty, writeDefaultConfig };
15957
- //# sourceMappingURL=chunk-5UG4UHKU.js.map
15958
- //# sourceMappingURL=chunk-5UG4UHKU.js.map
16131
+ export { PROJECT_TYPE_TO_OSV_ECOSYSTEM, aggregateSolutionArchitecture, boundaryProfileRules, brandProgressBar, buildVersionTimelines, classifyFile, compactUiPurpose, computeDriftScore, computeRepoFingerprint, computeUpgradeImpact, detectVcs, driftBar, evaluateLayerBoundary, fetchRiskySymbols, fetchScanPreflight, findPackageAnyEcosystem, findPackageTimeline, findVersionCrossings, getChangelogSignals, gitHistoryAvailable, loadConfig, mermaidFromArchitecture, normalizeConstraint, parseDsn, parseToml, perDependencyDrift, prepareCompressedUpload, projectTypeToVulnEcosystem, refineArchitectureWithAstRoles, refineArchitectureWithGraph, resolveHead, resolveRepositoryName, runCoreScan, scanArchitectureBundle, severityRank, titleBox, versionSatisfies, workingTreeDirty, writeDefaultConfig };
16132
+ //# sourceMappingURL=chunk-HUJZO4K5.js.map
16133
+ //# sourceMappingURL=chunk-HUJZO4K5.js.map