@vibgrate/cli 2026.721.2 → 2026.722.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 (67) hide show
  1. package/DOCS.md +185 -0
  2. package/README.md +2 -1
  3. package/dist/baseline-J44M7MWP.js +7 -0
  4. package/dist/{baseline-KK7ZLRZB.js.map → baseline-J44M7MWP.js.map} +1 -1
  5. package/dist/chunk-2EVQKZTO.js +696 -0
  6. package/dist/chunk-2EVQKZTO.js.map +1 -0
  7. package/dist/chunk-6CXTPC74.js +234 -0
  8. package/dist/chunk-6CXTPC74.js.map +1 -0
  9. package/dist/chunk-CRAMOEBE.js +675 -0
  10. package/dist/chunk-CRAMOEBE.js.map +1 -0
  11. package/dist/chunk-CS37OBE3.js +861 -0
  12. package/dist/chunk-CS37OBE3.js.map +1 -0
  13. package/dist/chunk-CT5BS56U.js +23 -0
  14. package/dist/chunk-CT5BS56U.js.map +1 -0
  15. package/dist/{chunk-MUMBLIDO.js → chunk-EXQV26MF.js} +4 -3
  16. package/dist/chunk-EXQV26MF.js.map +1 -0
  17. package/dist/chunk-GGJZA3Q6.js +961 -0
  18. package/dist/chunk-GGJZA3Q6.js.map +1 -0
  19. package/dist/chunk-JBXNQCGE.js +484 -0
  20. package/dist/chunk-JBXNQCGE.js.map +1 -0
  21. package/dist/chunk-JFGYT6BI.js +32 -0
  22. package/dist/chunk-JFGYT6BI.js.map +1 -0
  23. package/dist/{chunk-NHLSAZ6N.js → chunk-JSNHZCIV.js} +42 -71
  24. package/dist/chunk-JSNHZCIV.js.map +1 -0
  25. package/dist/chunk-JYS4OBN2.js +148 -0
  26. package/dist/chunk-JYS4OBN2.js.map +1 -0
  27. package/dist/chunk-K3SLGULW.js +50 -0
  28. package/dist/chunk-K3SLGULW.js.map +1 -0
  29. package/dist/chunk-LEPTUB4H.js +108 -0
  30. package/dist/chunk-LEPTUB4H.js.map +1 -0
  31. package/dist/{chunk-JWBS5GS6.js → chunk-PY3DNX5H.js} +94 -34
  32. package/dist/chunk-PY3DNX5H.js.map +1 -0
  33. package/dist/{chunk-3DPZQS2Q.js → chunk-VXT6LUGP.js} +356 -2532
  34. package/dist/chunk-VXT6LUGP.js.map +1 -0
  35. package/dist/chunk-WNIIKCNF.js +972 -0
  36. package/dist/chunk-WNIIKCNF.js.map +1 -0
  37. package/dist/chunk-XKXKWYJF.js +417 -0
  38. package/dist/chunk-XKXKWYJF.js.map +1 -0
  39. package/dist/cli.js +2479 -1234
  40. package/dist/cli.js.map +1 -1
  41. package/dist/fs-KCABDURV.js +3 -0
  42. package/dist/fs-KCABDURV.js.map +1 -0
  43. package/dist/index.d.ts +5 -3
  44. package/dist/index.js +8 -3
  45. package/dist/index.js.map +1 -1
  46. package/dist/interactive-736PBC7J.js +820 -0
  47. package/dist/interactive-736PBC7J.js.map +1 -0
  48. package/dist/mcp-tools-I6PME3AV.js +3 -0
  49. package/dist/mcp-tools-I6PME3AV.js.map +1 -0
  50. package/dist/parse-worker.d.ts +1 -1
  51. package/dist/parse-worker.js +2 -1
  52. package/dist/parse-worker.js.map +1 -1
  53. package/dist/session-POJUDFPH.js +6 -0
  54. package/dist/session-POJUDFPH.js.map +1 -0
  55. package/dist/session-store-W5RZZHA3.js +3 -0
  56. package/dist/session-store-W5RZZHA3.js.map +1 -0
  57. package/dist/stream-json-Q2JGWPHB.js +65 -0
  58. package/dist/stream-json-Q2JGWPHB.js.map +1 -0
  59. package/dist/{types-BgNa-FZQ.d.ts → types-Jl6RJ175.d.ts} +8 -0
  60. package/dist/ui-HOVOPWMX.js +4 -0
  61. package/dist/ui-HOVOPWMX.js.map +1 -0
  62. package/package.json +3 -2
  63. package/dist/baseline-KK7ZLRZB.js +0 -6
  64. package/dist/chunk-3DPZQS2Q.js.map +0 -1
  65. package/dist/chunk-JWBS5GS6.js.map +0 -1
  66. package/dist/chunk-MUMBLIDO.js.map +0 -1
  67. package/dist/chunk-NHLSAZ6N.js.map +0 -1
@@ -1,8 +1,12 @@
1
- import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-JWBS5GS6.js';
2
- import { writeTextFile, pathExists, readJsonFile, loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-NHLSAZ6N.js';
3
- import * as fs17 from 'fs';
1
+ import { resolveOne, resolveVersion, resolveLib, loadCatalog, driftFor, localPackageDocs, readDoc, findNodes, inventory, localApiSurface } from './chunk-GGJZA3Q6.js';
2
+ import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-JSNHZCIV.js';
3
+ import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-PY3DNX5H.js';
4
+ import { writeTextFile, pathExists, readJsonFile } from './chunk-CRAMOEBE.js';
5
+ import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady, cacheDir, loadCache, stableStringify, serializeGraph, parseGraph, acquireLock, releaseLock } from './chunk-WNIIKCNF.js';
6
+ import { shortId, canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
7
+ import * as fs18 from 'fs';
4
8
  import { spawnSync, execFileSync } from 'child_process';
5
- import * as path17 from 'path';
9
+ import * as path2 from 'path';
6
10
  import ignore from 'ignore';
7
11
  import * as v8 from 'v8';
8
12
  import Graph from 'graphology';
@@ -11,9 +15,8 @@ import betweenness from 'graphology-metrics/centrality/betweenness.js';
11
15
  import eigenvector from 'graphology-metrics/centrality/eigenvector.js';
12
16
  import louvain from 'graphology-communities-louvain';
13
17
  import * as os from 'os';
14
- import { pathToFileURL, fileURLToPath } from 'url';
18
+ import { fileURLToPath } from 'url';
15
19
  import ts from 'typescript';
16
- import { createRequire } from 'module';
17
20
  import { bidirectional } from 'graphology-shortest-path/unweighted.js';
18
21
  import * as crypto from 'crypto';
19
22
  import { createHash } from 'crypto';
@@ -23,9 +26,6 @@ import * as zlib from 'zlib';
23
26
  import { promisify } from 'util';
24
27
  import { encode, decode } from 'gpt-tokenizer/encoding/cl100k_base';
25
28
  import { Worker } from 'worker_threads';
26
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
27
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
28
- import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
29
29
 
30
30
  var NPX_INVOCATION = "npx @vibgrate/cli";
31
31
  function whichOnPath(cmd) {
@@ -41,9 +41,9 @@ function whichOnPath(cmd) {
41
41
  }
42
42
  function isOwnBinary(binPath) {
43
43
  try {
44
- const real = fs17.realpathSync(binPath);
44
+ const real = fs18.realpathSync(binPath);
45
45
  if (/[\\/]@vibgrate[\\/]cli[\\/]/.test(real)) return true;
46
- const head = fs17.readFileSync(real, { encoding: "utf8" }).slice(0, 2048);
46
+ const head = fs18.readFileSync(real, { encoding: "utf8" }).slice(0, 2048);
47
47
  return head.includes("@vibgrate/cli") || head.includes("vibgrate");
48
48
  } catch {
49
49
  return false;
@@ -53,7 +53,7 @@ function isEphemeralNpxBinary(binPath) {
53
53
  const NPX_SEGMENT = /[\\/]_npx[\\/]/;
54
54
  if (NPX_SEGMENT.test(binPath)) return true;
55
55
  try {
56
- return NPX_SEGMENT.test(fs17.realpathSync(binPath));
56
+ return NPX_SEGMENT.test(fs18.realpathSync(binPath));
57
57
  } catch {
58
58
  return false;
59
59
  }
@@ -219,19 +219,19 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
219
219
  "flake.lock"
220
220
  ]);
221
221
  function toPosix(p) {
222
- return p.split(path17.sep).join("/");
222
+ return p.split(path2.sep).join("/");
223
223
  }
224
224
  function buildRootIgnore(root, exclude) {
225
225
  const ig = ignore();
226
- const gitignorePath = path17.join(root, ".gitignore");
227
- if (fs17.existsSync(gitignorePath)) {
228
- ig.add(fs17.readFileSync(gitignorePath, "utf8"));
226
+ const gitignorePath = path2.join(root, ".gitignore");
227
+ if (fs18.existsSync(gitignorePath)) {
228
+ ig.add(fs18.readFileSync(gitignorePath, "utf8"));
229
229
  }
230
230
  if (exclude.length) ig.add(exclude);
231
231
  return ig;
232
232
  }
233
233
  function discover(options) {
234
- const root = path17.resolve(options.root);
234
+ const root = path2.resolve(options.root);
235
235
  const onlyLangs = (options.only ?? []).filter(Boolean);
236
236
  const allowLang = (lang) => onlyLangs.length === 0 || onlyLangs.includes(lang.id);
237
237
  for (const id of onlyLangs) {
@@ -240,27 +240,27 @@ function discover(options) {
240
240
  }
241
241
  }
242
242
  const rootIg = buildRootIgnore(root, options.exclude ?? []);
243
- const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path17.resolve(root, p)) : [root]).filter((p) => fs17.existsSync(p));
243
+ const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path2.resolve(root, p)) : [root]).filter((p) => fs18.existsSync(p));
244
244
  const found = /* @__PURE__ */ new Map();
245
245
  const considerFile = (abs) => {
246
- const rel2 = toPosix(path17.relative(root, abs));
246
+ const rel2 = toPosix(path2.relative(root, abs));
247
247
  if (rel2.startsWith("..")) return;
248
248
  if (rel2 === "" || rootIg.ignores(rel2)) return;
249
- if (SKIP_FILES.has(path17.basename(abs).toLowerCase())) return;
250
- const lang = langForExtension(path17.extname(abs));
249
+ if (SKIP_FILES.has(path2.basename(abs).toLowerCase())) return;
250
+ const lang = langForExtension(path2.extname(abs));
251
251
  if (!lang || !allowLang(lang)) return;
252
252
  found.set(rel2, { rel: rel2, abs, lang });
253
253
  };
254
254
  const walk2 = (dir) => {
255
255
  let entries;
256
256
  try {
257
- entries = fs17.readdirSync(dir, { withFileTypes: true });
257
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
258
258
  } catch {
259
259
  return;
260
260
  }
261
261
  for (const entry of entries) {
262
- const abs = path17.join(dir, entry.name);
263
- const rel2 = toPosix(path17.relative(root, abs));
262
+ const abs = path2.join(dir, entry.name);
263
+ const rel2 = toPosix(path2.relative(root, abs));
264
264
  if (entry.isDirectory()) {
265
265
  if (SKIP_DIRS.has(entry.name)) continue;
266
266
  if (rel2 && rootIg.ignores(`${rel2}/`)) continue;
@@ -271,7 +271,7 @@ function discover(options) {
271
271
  }
272
272
  };
273
273
  for (const scope of scopeAbs) {
274
- const stat = fs17.statSync(scope);
274
+ const stat = fs18.statSync(scope);
275
275
  if (stat.isDirectory()) walk2(scope);
276
276
  else if (stat.isFile()) considerFile(scope);
277
277
  }
@@ -455,10 +455,10 @@ function buildModuleResolver(root, relSet) {
455
455
  resolve(fromRel, source) {
456
456
  if (source.includes("\\")) source = source.split("\\").join(".");
457
457
  if (source.startsWith("./") || source.startsWith("../")) {
458
- return probe(posixJoin(path17.posix.dirname(fromRel), source));
458
+ return probe(posixJoin(path2.posix.dirname(fromRel), source));
459
459
  }
460
460
  if (/\.[A-Za-z0-9]+$/.test(source) && !source.startsWith("<")) {
461
- const hit = probe(posixJoin(path17.posix.dirname(fromRel), source));
461
+ const hit = probe(posixJoin(path2.posix.dirname(fromRel), source));
462
462
  if (hit) return hit;
463
463
  }
464
464
  if (source.startsWith(".")) {
@@ -471,11 +471,11 @@ function buildModuleResolver(root, relSet) {
471
471
  function buildSuffixIndex(relSet) {
472
472
  const byLast = /* @__PURE__ */ new Map();
473
473
  for (const rel2 of relSet) {
474
- const dot2 = rel2.lastIndexOf(".");
475
- if (dot2 < 0) continue;
476
- const ext = rel2.slice(dot2);
474
+ const dot = rel2.lastIndexOf(".");
475
+ if (dot < 0) continue;
476
+ const ext = rel2.slice(dot);
477
477
  if (!EXT_GROUPS[ext]) continue;
478
- let key = rel2.slice(0, dot2);
478
+ let key = rel2.slice(0, dot);
479
479
  const base = key.slice(key.lastIndexOf("/") + 1);
480
480
  if (base === "__init__" || base === "index") {
481
481
  key = key.slice(0, Math.max(0, key.length - base.length - 1));
@@ -494,12 +494,12 @@ function resolveSuffix(index, fromRel, source) {
494
494
  const fromExt = fromDot >= 0 ? fromRel.slice(fromDot) : "";
495
495
  const allowed = EXT_GROUPS[fromExt];
496
496
  if (!allowed) return null;
497
- const q2 = source.replace(/\./g, "/");
498
- const last = q2.slice(q2.lastIndexOf("/") + 1);
497
+ const q = source.replace(/\./g, "/");
498
+ const last = q.slice(q.lastIndexOf("/") + 1);
499
499
  const cands = index.get(last);
500
500
  if (!cands) return null;
501
501
  const matches = cands.filter(
502
- (c) => allowed.includes(c.ext) && (c.key === q2 || c.key.endsWith("/" + q2))
502
+ (c) => allowed.includes(c.ext) && (c.key === q || c.key.endsWith("/" + q))
503
503
  );
504
504
  if (matches.length === 0) return null;
505
505
  if (matches.length === 1) return matches[0].rel;
@@ -529,8 +529,8 @@ function resolvePyRelative(probe, fromRel, source) {
529
529
  let dots = 0;
530
530
  while (source[dots] === ".") dots++;
531
531
  const rest = source.slice(dots);
532
- let dir = path17.posix.dirname(fromRel);
533
- for (let i = 1; i < dots; i++) dir = path17.posix.dirname(dir);
532
+ let dir = path2.posix.dirname(fromRel);
533
+ for (let i = 1; i < dots; i++) dir = path2.posix.dirname(dir);
534
534
  const sub = rest.replace(/\./g, "/");
535
535
  return probe(sub ? posixJoin(dir, sub) : dir);
536
536
  }
@@ -538,7 +538,7 @@ function relativeResolver(relSet) {
538
538
  const probe = makeProbe(relSet);
539
539
  return {
540
540
  resolve(fromRel, source) {
541
- if (source.startsWith(".")) return probe(posixJoin(path17.posix.dirname(fromRel), source));
541
+ if (source.startsWith(".")) return probe(posixJoin(path2.posix.dirname(fromRel), source));
542
542
  return resolveDotted(probe, source);
543
543
  }
544
544
  };
@@ -556,12 +556,12 @@ function makeProbe(relSet) {
556
556
  };
557
557
  }
558
558
  function normalizeRel(p) {
559
- const norm = path17.posix.normalize(p);
559
+ const norm = path2.posix.normalize(p);
560
560
  const stripped = norm.startsWith("./") ? norm.slice(2) : norm;
561
561
  return stripped.startsWith("..") ? "" : stripped;
562
562
  }
563
563
  function posixJoin(a, b) {
564
- return path17.posix.normalize(path17.posix.join(a, b));
564
+ return path2.posix.normalize(path2.posix.join(a, b));
565
565
  }
566
566
  function matchPattern(pattern, source) {
567
567
  const star = pattern.indexOf("*");
@@ -574,8 +574,8 @@ function matchPattern(pattern, source) {
574
574
  }
575
575
  function loadTsconfigPaths(root) {
576
576
  for (const name of ["tsconfig.base.json", "tsconfig.json"]) {
577
- const file = path17.join(root, name);
578
- if (fs17.existsSync(file)) {
577
+ const file = path2.join(root, name);
578
+ if (fs18.existsSync(file)) {
579
579
  const merged = readTsconfigChain(root, file, /* @__PURE__ */ new Set());
580
580
  if (merged && merged.paths.length) return merged;
581
581
  }
@@ -587,21 +587,21 @@ function readTsconfigChain(root, file, seen) {
587
587
  seen.add(file);
588
588
  let cfg;
589
589
  try {
590
- cfg = parseJsonc(fs17.readFileSync(file, "utf8"));
590
+ cfg = parseJsonc(fs18.readFileSync(file, "utf8"));
591
591
  } catch {
592
592
  return null;
593
593
  }
594
- const dir = path17.dirname(file);
594
+ const dir = path2.dirname(file);
595
595
  let base = { baseUrlRel: "", paths: [] };
596
596
  if (cfg.extends && cfg.extends.startsWith(".")) {
597
- const extPath = path17.resolve(dir, cfg.extends.endsWith(".json") ? cfg.extends : `${cfg.extends}.json`);
598
- if (fs17.existsSync(extPath)) {
597
+ const extPath = path2.resolve(dir, cfg.extends.endsWith(".json") ? cfg.extends : `${cfg.extends}.json`);
598
+ if (fs18.existsSync(extPath)) {
599
599
  const inherited = readTsconfigChain(root, extPath, seen);
600
600
  if (inherited) base = inherited;
601
601
  }
602
602
  }
603
603
  const co = cfg.compilerOptions ?? {};
604
- const baseUrlRel = co.baseUrl !== void 0 ? path17.relative(root, path17.resolve(dir, co.baseUrl)).split(path17.sep).join("/") : base.baseUrlRel;
604
+ const baseUrlRel = co.baseUrl !== void 0 ? path2.relative(root, path2.resolve(dir, co.baseUrl)).split(path2.sep).join("/") : base.baseUrlRel;
605
605
  const paths = [...base.paths];
606
606
  if (co.paths) for (const [k, v] of Object.entries(co.paths)) paths.push([k, v]);
607
607
  return { baseUrlRel, paths };
@@ -609,27 +609,27 @@ function readTsconfigChain(root, file, seen) {
609
609
  function loadWorkspacePackages(root) {
610
610
  const map = /* @__PURE__ */ new Map();
611
611
  const globs = [];
612
- const rootPkg = path17.join(root, "package.json");
613
- if (fs17.existsSync(rootPkg)) {
612
+ const rootPkg = path2.join(root, "package.json");
613
+ if (fs18.existsSync(rootPkg)) {
614
614
  try {
615
- const pkg = JSON.parse(fs17.readFileSync(rootPkg, "utf8"));
615
+ const pkg = JSON.parse(fs18.readFileSync(rootPkg, "utf8"));
616
616
  const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
617
617
  if (ws) globs.push(...ws);
618
618
  } catch {
619
619
  }
620
620
  }
621
- const pnpmWs = path17.join(root, "pnpm-workspace.yaml");
622
- if (fs17.existsSync(pnpmWs)) {
623
- for (const line of fs17.readFileSync(pnpmWs, "utf8").split("\n")) {
621
+ const pnpmWs = path2.join(root, "pnpm-workspace.yaml");
622
+ if (fs18.existsSync(pnpmWs)) {
623
+ for (const line of fs18.readFileSync(pnpmWs, "utf8").split("\n")) {
624
624
  const m = /^\s*-\s*['"]?([^'"\n]+)['"]?\s*$/.exec(line);
625
625
  if (m) globs.push(m[1].trim());
626
626
  }
627
627
  }
628
628
  for (const dir of expandWorkspaceGlobs(root, globs)) {
629
- const pj = path17.join(dir, "package.json");
629
+ const pj = path2.join(dir, "package.json");
630
630
  try {
631
- const name = JSON.parse(fs17.readFileSync(pj, "utf8")).name;
632
- if (name) map.set(name, path17.relative(root, dir).split(path17.sep).join("/"));
631
+ const name = JSON.parse(fs18.readFileSync(pj, "utf8")).name;
632
+ if (name) map.set(name, path2.relative(root, dir).split(path2.sep).join("/"));
633
633
  } catch {
634
634
  }
635
635
  }
@@ -639,10 +639,10 @@ function expandWorkspaceGlobs(root, globs) {
639
639
  const out = /* @__PURE__ */ new Set();
640
640
  for (const glob of globs) {
641
641
  const clean = glob.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
642
- const baseDir = path17.join(root, clean);
642
+ const baseDir = path2.join(root, clean);
643
643
  const recurse = glob.endsWith("**");
644
644
  if (glob === clean) {
645
- if (fs17.existsSync(path17.join(baseDir, "package.json"))) out.add(baseDir);
645
+ if (fs18.existsSync(path2.join(baseDir, "package.json"))) out.add(baseDir);
646
646
  continue;
647
647
  }
648
648
  collectPackageDirs(baseDir, recurse ? 4 : 1, out);
@@ -653,14 +653,14 @@ function collectPackageDirs(dir, depth, out) {
653
653
  if (depth < 0) return;
654
654
  let entries;
655
655
  try {
656
- entries = fs17.readdirSync(dir, { withFileTypes: true });
656
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
657
657
  } catch {
658
658
  return;
659
659
  }
660
660
  for (const e of entries) {
661
661
  if (!e.isDirectory() || e.name === "node_modules" || e.name.startsWith(".")) continue;
662
- const child = path17.join(dir, e.name);
663
- if (fs17.existsSync(path17.join(child, "package.json"))) out.add(child);
662
+ const child = path2.join(dir, e.name);
663
+ if (fs18.existsSync(path2.join(child, "package.json"))) out.add(child);
664
664
  else collectPackageDirs(child, depth - 1, out);
665
665
  }
666
666
  }
@@ -1159,14 +1159,14 @@ var DEFAULT_PATHS = [
1159
1159
  ];
1160
1160
  function loadCoverage(root, explicit) {
1161
1161
  const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
1162
- (p) => path17.resolve(root, p)
1162
+ (p) => path2.resolve(root, p)
1163
1163
  );
1164
- const found = candidates.filter((p) => fs17.existsSync(p));
1164
+ const found = candidates.filter((p) => fs18.existsSync(p));
1165
1165
  if (found.length === 0) return null;
1166
1166
  const map = /* @__PURE__ */ new Map();
1167
1167
  for (const file of found) {
1168
1168
  try {
1169
- const text = fs17.readFileSync(file, "utf8");
1169
+ const text = fs18.readFileSync(file, "utf8");
1170
1170
  if (file.endsWith(".json")) mergeIstanbul(map, text, root);
1171
1171
  else mergeLcov(map, text, root);
1172
1172
  } catch {
@@ -1175,8 +1175,8 @@ function loadCoverage(root, explicit) {
1175
1175
  return map.size ? map : null;
1176
1176
  }
1177
1177
  function rel(root, p) {
1178
- const abs = path17.isAbsolute(p) ? p : path17.resolve(root, p);
1179
- return path17.relative(root, abs).split(path17.sep).join("/");
1178
+ const abs = path2.isAbsolute(p) ? p : path2.resolve(root, p);
1179
+ return path2.relative(root, abs).split(path2.sep).join("/");
1180
1180
  }
1181
1181
  function bump(map, file, line, hits) {
1182
1182
  let lh = map.get(file);
@@ -1464,32 +1464,6 @@ function matchConfidence(entry, nameParts, imports, callees) {
1464
1464
  return conf;
1465
1465
  }
1466
1466
 
1467
- // src/engine/serialize.ts
1468
- function serializeGraph(graph) {
1469
- return `${stableStringify(graph, 2)}
1470
- `;
1471
- }
1472
- function stableStringify(value, indent = 2) {
1473
- return JSON.stringify(sortKeys(value), null, indent);
1474
- }
1475
- function sortKeys(value) {
1476
- if (Array.isArray(value)) return value.map(sortKeys);
1477
- if (value && typeof value === "object") {
1478
- const input = value;
1479
- const out = {};
1480
- for (const key of Object.keys(input).sort()) {
1481
- const v = input[key];
1482
- if (v === void 0) continue;
1483
- out[key] = sortKeys(v);
1484
- }
1485
- return out;
1486
- }
1487
- return value;
1488
- }
1489
- function parseGraph(json) {
1490
- return JSON.parse(json);
1491
- }
1492
-
1493
1467
  // src/schema.ts
1494
1468
  var SCHEMA_VERSION = "vg-graph/1.0";
1495
1469
  var DEFAULT_INLINE_THRESHOLD = 24;
@@ -1512,7 +1486,7 @@ async function parseInline(files, options) {
1512
1486
  onProgress?.(0, files.length);
1513
1487
  for (const file of files) {
1514
1488
  try {
1515
- const source = fs17.readFileSync(file.abs, "utf8");
1489
+ const source = fs18.readFileSync(file.abs, "utf8");
1516
1490
  out.push(await parseSource(file.rel, file.lang.id, source));
1517
1491
  } catch (err) {
1518
1492
  out.push(emptyParse(file, `parse failed: ${err.message}`));
@@ -1567,9 +1541,9 @@ function isWorkerOom(err) {
1567
1541
  return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
1568
1542
  }
1569
1543
  function resolveWorkerFile() {
1570
- const here = path17.dirname(fileURLToPath(import.meta.url));
1571
- const candidate = path17.join(here, "parse-worker.js");
1572
- return fs17.existsSync(candidate) ? candidate : null;
1544
+ const here = path2.dirname(fileURLToPath(import.meta.url));
1545
+ const candidate = path2.join(here, "parse-worker.js");
1546
+ return fs18.existsSync(candidate) ? candidate : null;
1573
1547
  }
1574
1548
  function chunk(items, buckets) {
1575
1549
  const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
@@ -1608,7 +1582,7 @@ function resolve4(parses, resolver) {
1608
1582
  addNode(baseNodes, {
1609
1583
  id: fileId,
1610
1584
  kind: "file",
1611
- name: path17.posix.basename(p.rel),
1585
+ name: path2.posix.basename(p.rel),
1612
1586
  qualifiedName: p.rel,
1613
1587
  file: p.rel,
1614
1588
  span: { start: 1, end: 1 },
@@ -1646,6 +1620,38 @@ function resolve4(parses, resolver) {
1646
1620
  push(defsByFile, p.rel, ref);
1647
1621
  }
1648
1622
  }
1623
+ const filesByNamespace = /* @__PURE__ */ new Map();
1624
+ for (const p of parses) for (const ns of p.namespaces ?? []) addToSet(filesByNamespace, ns, p.rel);
1625
+ const relsByLang = /* @__PURE__ */ new Map();
1626
+ for (const p of parses) push(relsByLang, p.lang, p.rel);
1627
+ const nsReachableByRel = /* @__PURE__ */ new Map();
1628
+ const moduleReachableByRel = /* @__PURE__ */ new Map();
1629
+ for (const p of parses) {
1630
+ if (MODULE_SCOPED_LANGS.has(p.lang)) {
1631
+ const mod2 = /* @__PURE__ */ new Set();
1632
+ for (const rel2 of relsByLang.get(p.lang) ?? []) if (rel2 !== p.rel) mod2.add(rel2);
1633
+ moduleReachableByRel.set(p.rel, mod2);
1634
+ }
1635
+ const reach = /* @__PURE__ */ new Set();
1636
+ const namespaces = new Set(p.namespaces ?? []);
1637
+ for (const imp of p.imports) {
1638
+ namespaces.add(imp.source);
1639
+ if (IMPORT_NAMES_A_TYPE.has(p.lang)) {
1640
+ const sep9 = Math.max(imp.source.lastIndexOf("."), imp.source.lastIndexOf("\\"));
1641
+ if (sep9 > 0) namespaces.add(imp.source.slice(0, sep9));
1642
+ }
1643
+ }
1644
+ for (const ns of namespaces) for (const rel2 of filesByNamespace.get(ns) ?? []) if (rel2 !== p.rel) reach.add(rel2);
1645
+ nsReachableByRel.set(p.rel, reach);
1646
+ }
1647
+ const superTypesByType = /* @__PURE__ */ new Map();
1648
+ for (const p of parses) {
1649
+ const localDefs = defsByFile.get(p.rel) ?? [];
1650
+ for (const h of p.heritage) {
1651
+ const encl = enclosingDefRef(localDefs, h.byte);
1652
+ if (encl && (encl.kind === "class" || encl.kind === "interface")) addToSet(superTypesByType, encl.name, h.superName);
1653
+ }
1654
+ }
1649
1655
  const edges = new EdgeSet();
1650
1656
  const unresolved2 = /* @__PURE__ */ new Map();
1651
1657
  const stats = {
@@ -1689,9 +1695,11 @@ function resolve4(parses, resolver) {
1689
1695
  const localDefs = defsByFile.get(p.rel) ?? [];
1690
1696
  const imported = importedFilesByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1691
1697
  const testCaller = isTestFile(p.rel);
1698
+ const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1699
+ const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1692
1700
  for (const call of p.calls) {
1693
1701
  const srcId = enclosingDefId(localDefs, call.byte) ?? fileId;
1694
- const resolved = resolveCall(call, p.rel, p.lang, imported, defsByName, srcId, testCaller);
1702
+ const resolved = resolveCall(call, p.rel, p.lang, imported, defsByName, srcId, testCaller, nsReach, superTypesByType, modReach);
1695
1703
  if (resolved) {
1696
1704
  edges.add("call", srcId, resolved.id, "heuristic", resolved.confidence);
1697
1705
  stats.callsResolved++;
@@ -1706,9 +1714,11 @@ function resolve4(parses, resolver) {
1706
1714
  const localDefs = defsByFile.get(p.rel) ?? [];
1707
1715
  const imported = importedFilesByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1708
1716
  const testCaller = isTestFile(p.rel);
1717
+ const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1718
+ const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1709
1719
  for (const ref of p.typeRefs) {
1710
1720
  const srcId = enclosingDefId(localDefs, ref.byte) ?? fileId;
1711
- const target = resolveType(ref.name, p.rel, p.lang, imported, defsByName, testCaller);
1721
+ const target = resolveType(ref.name, p.rel, p.lang, imported, defsByName, testCaller, nsReach, modReach);
1712
1722
  if (target) edges.add("references", srcId, target.id, "heuristic", 0.7);
1713
1723
  }
1714
1724
  }
@@ -1719,7 +1729,9 @@ function resolve4(parses, resolver) {
1719
1729
  for (const h of p.heritage) {
1720
1730
  const srcId = enclosingDefId(localDefs, h.byte);
1721
1731
  if (!srcId) continue;
1722
- const target = resolveType(h.superName, p.rel, p.lang, imported, defsByName, testCaller);
1732
+ const nsReach = nsReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1733
+ const modReach = moduleReachableByRel.get(p.rel) ?? /* @__PURE__ */ new Set();
1734
+ const target = resolveType(h.superName, p.rel, p.lang, imported, defsByName, testCaller, nsReach, modReach);
1723
1735
  if (target) edges.add(h.kind, srcId, target.id, "heuristic", 0.85);
1724
1736
  else bumpUnresolved(unresolved2, srcId, h.superName, h.kind, p.rel);
1725
1737
  }
@@ -1770,21 +1782,52 @@ function parentQualifiedName(qn) {
1770
1782
  const idx = qn.lastIndexOf(".");
1771
1783
  return idx > 0 ? qn.slice(0, idx) : null;
1772
1784
  }
1773
- function enclosingDefId(defs, byte) {
1785
+ function enclosingDefRef(defs, byte) {
1774
1786
  let best;
1775
1787
  for (const d of defs) {
1776
1788
  if (d.startByte <= byte && d.endByte >= byte) {
1777
1789
  if (!best || d.endByte - d.startByte < best.endByte - best.startByte) best = d;
1778
1790
  }
1779
1791
  }
1780
- return best?.id;
1792
+ return best;
1793
+ }
1794
+ function enclosingDefId(defs, byte) {
1795
+ return enclosingDefRef(defs, byte)?.id;
1796
+ }
1797
+ function addToSet(map, key, value) {
1798
+ const s = map.get(key);
1799
+ if (s) s.add(value);
1800
+ else map.set(key, /* @__PURE__ */ new Set([value]));
1801
+ }
1802
+ function parentTypeName(qualifiedName) {
1803
+ const i = qualifiedName.lastIndexOf(".");
1804
+ return i > 0 ? qualifiedName.slice(0, i) : null;
1805
+ }
1806
+ function disambiguateInterfaceImpl(pool, superTypesByType) {
1807
+ const withParent = pool.map((c) => ({ c, parent: parentTypeName(c.qualifiedName) })).filter((x) => x.parent !== null);
1808
+ if (withParent.length < 2) return null;
1809
+ const parents = new Set(withParent.map((x) => x.parent));
1810
+ const impls = withParent.filter(
1811
+ (x) => [...parents].some((other) => other !== x.parent && superTypesByType.get(x.parent)?.has(other))
1812
+ );
1813
+ if (impls.length === 0) return null;
1814
+ const interfaceParents = /* @__PURE__ */ new Set();
1815
+ for (const impl of impls) for (const s of superTypesByType.get(impl.parent) ?? []) interfaceParents.add(s);
1816
+ const interfaces = withParent.filter((x) => interfaceParents.has(x.parent));
1817
+ const uniqueImplIds = new Set(impls.map((x) => x.c.id));
1818
+ if (uniqueImplIds.size === 1) return impls[0].c;
1819
+ const uniqueIfaceIds = new Set(interfaces.map((x) => x.c.id));
1820
+ if (uniqueIfaceIds.size === 1) return interfaces[0].c;
1821
+ return null;
1781
1822
  }
1782
1823
  var PACKAGE_SCOPED_LANGS = /* @__PURE__ */ new Set(["go", "java", "cs", "kotlin", "scala", "swift"]);
1824
+ var IMPORT_NAMES_A_TYPE = /* @__PURE__ */ new Set(["java", "kotlin", "scala", "php"]);
1825
+ var MODULE_SCOPED_LANGS = /* @__PURE__ */ new Set(["swift"]);
1783
1826
  function dirOf(rel2) {
1784
1827
  const i = rel2.lastIndexOf("/");
1785
1828
  return i >= 0 ? rel2.slice(0, i) : "";
1786
1829
  }
1787
- function resolveCall(call, fromRel, fromLang, importedRels, defsByName, enclosingId, fromIsTestFile = false) {
1830
+ function resolveCall(call, fromRel, fromLang, importedRels, defsByName, enclosingId, fromIsTestFile = false, nsReachableRels = /* @__PURE__ */ new Set(), superTypesByType = /* @__PURE__ */ new Map(), moduleReachableRels = /* @__PURE__ */ new Set()) {
1788
1831
  const candidates = defsByName.get(call.callee);
1789
1832
  if (!candidates || candidates.length === 0) return null;
1790
1833
  const callable = candidates.filter((c) => c.kind === "function" || c.kind === "method");
@@ -1796,21 +1839,24 @@ function resolveCall(call, fromRel, fromLang, importedRels, defsByName, enclosin
1796
1839
  const pick = [...sameFile].sort((a, b) => a.id.localeCompare(b.id))[0];
1797
1840
  return { id: pick.id, confidence: 0.4 };
1798
1841
  }
1799
- const imported = pool.filter((c) => importedRels.has(c.rel));
1800
- if (imported.length === 1) return { id: imported[0].id, confidence: 0.75 };
1801
- if (imported.length > 1) return null;
1802
- if (PACKAGE_SCOPED_LANGS.has(fromLang)) {
1803
- const dir = dirOf(fromRel);
1804
- let samePkg = pool.filter((c) => c.lang === fromLang && dirOf(c.rel) === dir);
1805
- if (call.qualified && enclosingId) samePkg = samePkg.filter((c) => c.id !== enclosingId);
1806
- if (samePkg.length === 1) return { id: samePkg[0].id, confidence: 0.7 };
1842
+ const dir = dirOf(fromRel);
1843
+ const notSelf = (c) => !(call.qualified && enclosingId && c.id === enclosingId);
1844
+ const strict = pool.filter(
1845
+ (c) => notSelf(c) && (importedRels.has(c.rel) || PACKAGE_SCOPED_LANGS.has(fromLang) && c.lang === fromLang && dirOf(c.rel) === dir || nsReachableRels.has(c.rel))
1846
+ );
1847
+ if (strict.length === 1) return { id: strict[0].id, confidence: 0.75 };
1848
+ const broad = pool.filter((c) => notSelf(c) && (moduleReachableRels.has(c.rel) || strict.includes(c)));
1849
+ if (broad.length > 1) {
1850
+ const dedup = disambiguateInterfaceImpl(broad, superTypesByType);
1851
+ if (dedup) return { id: dedup.id, confidence: 0.6 };
1807
1852
  }
1853
+ if (strict.length > 1) return null;
1808
1854
  if (fromLang === "swift" && fromIsTestFile && pool.length === 1) {
1809
1855
  return { id: pool[0].id, confidence: 0.5 };
1810
1856
  }
1811
1857
  return null;
1812
1858
  }
1813
- function resolveType(name, fromRel, fromLang, importedRels, defsByName, fromIsTestFile = false) {
1859
+ function resolveType(name, fromRel, fromLang, importedRels, defsByName, fromIsTestFile = false, nsReachableRels = /* @__PURE__ */ new Set(), moduleReachableRels = /* @__PURE__ */ new Set()) {
1814
1860
  const candidates = (defsByName.get(name) ?? []).filter(
1815
1861
  (c) => c.kind === "class" || c.kind === "interface"
1816
1862
  );
@@ -1825,6 +1871,10 @@ function resolveType(name, fromRel, fromLang, importedRels, defsByName, fromIsTe
1825
1871
  const samePkg = candidates.filter((c) => c.lang === fromLang && dirOf(c.rel) === dir);
1826
1872
  if (samePkg.length === 1) return { id: samePkg[0].id };
1827
1873
  }
1874
+ if (nsReachableRels.size || moduleReachableRels.size) {
1875
+ const reach = candidates.filter((c) => nsReachableRels.has(c.rel) || moduleReachableRels.has(c.rel));
1876
+ if (reach.length === 1) return { id: reach[0].id };
1877
+ }
1828
1878
  if (fromLang === "swift" && fromIsTestFile && candidates.length === 1) {
1829
1879
  return { id: candidates[0].id };
1830
1880
  }
@@ -1881,6 +1931,9 @@ function tsResolveEdges(root, tsFiles, nodes) {
1881
1931
  const edges = /* @__PURE__ */ new Map();
1882
1932
  const covered = /* @__PURE__ */ new Set();
1883
1933
  const stats = empty.stats;
1934
+ const interfaceCalls = [];
1935
+ const byId = /* @__PURE__ */ new Map();
1936
+ for (const list of nodesByFile.values()) for (const n of list) byId.set(n.id, n);
1884
1937
  for (const file of tsFiles) {
1885
1938
  const sf = program.getSourceFile(file.abs);
1886
1939
  if (!sf) continue;
@@ -1898,6 +1951,9 @@ function tsResolveEdges(root, tsFiles, nodes) {
1898
1951
  if (src && src.id !== target.id) {
1899
1952
  add2(edges, callKind(target), src.id, target.id);
1900
1953
  stats.resolved++;
1954
+ if (target.kind === "interface" && ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
1955
+ interfaceCalls.push({ srcId: src.id, interfaceId: target.id, method: node.expression.name.text });
1956
+ }
1901
1957
  }
1902
1958
  }
1903
1959
  } else if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
@@ -1931,6 +1987,27 @@ function tsResolveEdges(root, tsFiles, nodes) {
1931
1987
  };
1932
1988
  visit(sf);
1933
1989
  }
1990
+ const implsByInterface = /* @__PURE__ */ new Map();
1991
+ for (const e of edges.values()) {
1992
+ if (e.kind !== "implements" && e.kind !== "extends") continue;
1993
+ if (byId.get(e.dst)?.kind !== "interface") continue;
1994
+ const list = implsByInterface.get(e.dst);
1995
+ if (list) list.push(e.src);
1996
+ else implsByInterface.set(e.dst, [e.src]);
1997
+ }
1998
+ for (const { srcId, interfaceId, method } of interfaceCalls) {
1999
+ const impls = implsByInterface.get(interfaceId);
2000
+ if (!impls || impls.length !== 1) continue;
2001
+ const impl = byId.get(impls[0]);
2002
+ if (!impl) continue;
2003
+ const target = (nodesByFile.get(impl.file) ?? []).find(
2004
+ (n) => (n.kind === "method" || n.kind === "function") && n.qualifiedName === `${impl.qualifiedName}.${method}`
2005
+ );
2006
+ if (target && target.id !== srcId) {
2007
+ add2(edges, "call", srcId, target.id);
2008
+ stats.resolved++;
2009
+ }
2010
+ }
1934
2011
  return { edges: [...edges.values()], coveredFiles: covered, stats };
1935
2012
  }
1936
2013
  function callKind(target) {
@@ -2044,50 +2121,7 @@ function enclosing3(nodes, line) {
2044
2121
  return best;
2045
2122
  }
2046
2123
  function normalize2(p) {
2047
- return path17.resolve(p).split(path17.sep).join("/");
2048
- }
2049
- var CACHE_VERSION = "vg-parse-cache/2";
2050
- function cacheDir(root) {
2051
- return path17.join(root, ".vibgrate", "cache");
2052
- }
2053
- function cachePath(root) {
2054
- return path17.join(cacheDir(root), "parse-cache.json");
2055
- }
2056
- function loadCache(root, opts) {
2057
- const file = cachePath(root);
2058
- let data = {
2059
- version: CACHE_VERSION,
2060
- toolVersion: opts.toolVersion,
2061
- grammars: opts.grammars,
2062
- entries: {}
2063
- };
2064
- if (!opts.disabled && fs17.existsSync(file)) {
2065
- try {
2066
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2067
- if (loaded.version === CACHE_VERSION && loaded.toolVersion === opts.toolVersion && loaded.grammars === opts.grammars && loaded.entries) {
2068
- data = loaded;
2069
- }
2070
- } catch {
2071
- }
2072
- }
2073
- return {
2074
- get(rel2, hash) {
2075
- const entry = data.entries[rel2];
2076
- return entry && entry.hash === hash ? entry.parse : void 0;
2077
- },
2078
- set(rel2, parse) {
2079
- data.entries[rel2] = { hash: parse.hash, parse };
2080
- },
2081
- prune(currentRels) {
2082
- for (const rel2 of Object.keys(data.entries)) {
2083
- if (!currentRels.has(rel2)) delete data.entries[rel2];
2084
- }
2085
- },
2086
- save() {
2087
- fs17.mkdirSync(cacheDir(root), { recursive: true });
2088
- fs17.writeFileSync(file, stableStringify(data, 0));
2089
- }
2090
- };
2124
+ return path2.resolve(p).split(path2.sep).join("/");
2091
2125
  }
2092
2126
 
2093
2127
  // src/engine/epistemic.ts
@@ -2117,7 +2151,7 @@ function epistemicBreakdown(edges) {
2117
2151
  // src/engine/build.ts
2118
2152
  async function buildGraph(options) {
2119
2153
  const start = nowMs();
2120
- const root = path17.resolve(options.root);
2154
+ const root = path2.resolve(options.root);
2121
2155
  const files = discover({
2122
2156
  root,
2123
2157
  only: options.only,
@@ -2145,7 +2179,7 @@ async function buildGraph(options) {
2145
2179
  for (const file of files) {
2146
2180
  let stat;
2147
2181
  try {
2148
- stat = fs17.statSync(file.abs);
2182
+ stat = fs18.statSync(file.abs);
2149
2183
  } catch {
2150
2184
  continue;
2151
2185
  }
@@ -2163,7 +2197,7 @@ async function buildGraph(options) {
2163
2197
  }
2164
2198
  let hash = "";
2165
2199
  try {
2166
- hash = hashBytes(fs17.readFileSync(file.abs));
2200
+ hash = hashBytes(fs18.readFileSync(file.abs));
2167
2201
  } catch {
2168
2202
  continue;
2169
2203
  }
@@ -2255,7 +2289,7 @@ async function buildGraph(options) {
2255
2289
  toolchain
2256
2290
  },
2257
2291
  meta: {
2258
- root: path17.basename(root) === "" ? "." : ".",
2292
+ root: path2.basename(root) === "" ? "." : ".",
2259
2293
  languages,
2260
2294
  counts: {
2261
2295
  nodes: analysis.nodes.length,
@@ -2301,14 +2335,14 @@ function toRepoRel(p) {
2301
2335
  function loadScipIndex(root, explicit) {
2302
2336
  const candidates = [
2303
2337
  explicit,
2304
- path17.join(root, "index.scip"),
2305
- path17.join(root, ".vibgrate", "index.scip")
2338
+ path2.join(root, "index.scip"),
2339
+ path2.join(root, ".vibgrate", "index.scip")
2306
2340
  ].filter((p) => Boolean(p));
2307
2341
  for (const file of candidates) {
2308
- const abs = path17.isAbsolute(file) ? file : path17.resolve(root, file);
2309
- if (!fs17.existsSync(abs)) continue;
2342
+ const abs = path2.isAbsolute(file) ? file : path2.resolve(root, file);
2343
+ if (!fs18.existsSync(abs)) continue;
2310
2344
  try {
2311
- const index = decodeScipIndex(new Uint8Array(fs17.readFileSync(abs)));
2345
+ const index = decodeScipIndex(new Uint8Array(fs18.readFileSync(abs)));
2312
2346
  if (index.documents.length) {
2313
2347
  return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
2314
2348
  }
@@ -2469,7 +2503,7 @@ function esc(s) {
2469
2503
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2470
2504
  }
2471
2505
  function vibgrateDir(root) {
2472
- return path17.join(root, ".vibgrate");
2506
+ return path2.join(root, ".vibgrate");
2473
2507
  }
2474
2508
  var DEFAULT_GITIGNORE = [
2475
2509
  "# Created once by vg \u2014 local graph artifacts stay out of git by default.",
@@ -2485,51 +2519,51 @@ var DEFAULT_GITIGNORE = [
2485
2519
  "mcp-navigation.json"
2486
2520
  ];
2487
2521
  function ensureVibgrateGitignore(root) {
2488
- const file = path17.join(vibgrateDir(root), ".gitignore");
2489
- if (fs17.existsSync(file)) return;
2490
- fs17.mkdirSync(vibgrateDir(root), { recursive: true });
2491
- fs17.writeFileSync(file, `${DEFAULT_GITIGNORE.join("\n")}
2522
+ const file = path2.join(vibgrateDir(root), ".gitignore");
2523
+ if (fs18.existsSync(file)) return;
2524
+ fs18.mkdirSync(vibgrateDir(root), { recursive: true });
2525
+ fs18.writeFileSync(file, `${DEFAULT_GITIGNORE.join("\n")}
2492
2526
  `);
2493
2527
  }
2494
2528
  function defaultGraphPath(root) {
2495
- return path17.join(vibgrateDir(root), "graph.json");
2529
+ return path2.join(vibgrateDir(root), "graph.json");
2496
2530
  }
2497
2531
  function writeArtifacts(graph, options) {
2498
2532
  const dir = vibgrateDir(options.root);
2499
- fs17.mkdirSync(dir, { recursive: true });
2533
+ fs18.mkdirSync(dir, { recursive: true });
2500
2534
  ensureVibgrateGitignore(options.root);
2501
2535
  const graphPath = options.graphPath ?? defaultGraphPath(options.root);
2502
- fs17.mkdirSync(path17.dirname(graphPath), { recursive: true });
2536
+ fs18.mkdirSync(path2.dirname(graphPath), { recursive: true });
2503
2537
  const tmp = `${graphPath}.${process.pid}.tmp`;
2504
2538
  try {
2505
- fs17.writeFileSync(tmp, serializeGraph(graph));
2506
- fs17.renameSync(tmp, graphPath);
2539
+ fs18.writeFileSync(tmp, serializeGraph(graph));
2540
+ fs18.renameSync(tmp, graphPath);
2507
2541
  } catch (err) {
2508
- fs17.rmSync(tmp, { force: true });
2542
+ fs18.rmSync(tmp, { force: true });
2509
2543
  throw err;
2510
2544
  }
2511
2545
  const written = { graphPath };
2512
2546
  if (options.report !== false) {
2513
- const reportPath = path17.join(dir, "GRAPH_REPORT.md");
2514
- fs17.writeFileSync(reportPath, renderReport(graph));
2547
+ const reportPath = path2.join(dir, "GRAPH_REPORT.md");
2548
+ fs18.writeFileSync(reportPath, renderReport(graph));
2515
2549
  written.reportPath = reportPath;
2516
2550
  }
2517
2551
  if (options.html !== false) {
2518
- const htmlPath = path17.join(dir, "graph.html");
2519
- fs17.writeFileSync(htmlPath, renderHtml(graph));
2552
+ const htmlPath = path2.join(dir, "graph.html");
2553
+ fs18.writeFileSync(htmlPath, renderHtml(graph));
2520
2554
  written.htmlPath = htmlPath;
2521
2555
  }
2522
2556
  if (graph.facts && graph.facts.length) {
2523
- const factsPath = path17.join(dir, "facts.jsonl");
2524
- fs17.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
2557
+ const factsPath = path2.join(dir, "facts.jsonl");
2558
+ fs18.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
2525
2559
  written.factsPath = factsPath;
2526
2560
  }
2527
2561
  return written;
2528
2562
  }
2529
2563
  function loadGraph(root, graphPath) {
2530
2564
  const file = graphPath ?? defaultGraphPath(root);
2531
- if (!fs17.existsSync(file)) return null;
2532
- return parseGraph(fs17.readFileSync(file, "utf8"));
2565
+ if (!fs18.existsSync(file)) return null;
2566
+ return parseGraph(fs18.readFileSync(file, "utf8"));
2533
2567
  }
2534
2568
 
2535
2569
  // src/engine/verify.ts
@@ -2595,361 +2629,23 @@ var CliError = class extends Error {
2595
2629
  function usageError(message) {
2596
2630
  return new CliError(message, ExitCode.USAGE_ERROR);
2597
2631
  }
2598
- var DEFAULT_LOCK_STALE_MS = 15 * 60 * 1e3;
2599
- function isProcessAlive(pid) {
2600
- try {
2601
- process.kill(pid, 0);
2602
- return true;
2603
- } catch (e) {
2604
- return e.code === "EPERM";
2605
- }
2606
- }
2607
- function lockIsStale(file, staleMs) {
2608
- try {
2609
- const { pid, at } = JSON.parse(fs17.readFileSync(file, "utf8"));
2610
- if (typeof at === "number" && Date.now() - at > staleMs) return true;
2611
- if (typeof pid === "number" && !isProcessAlive(pid)) return true;
2612
- return false;
2613
- } catch {
2614
- return true;
2615
- }
2616
- }
2617
- function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
2618
- const write = () => {
2619
- try {
2620
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
2621
- const fd = fs17.openSync(file, "wx");
2622
- fs17.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
2623
- fs17.closeSync(fd);
2624
- return true;
2625
- } catch {
2626
- return false;
2627
- }
2628
- };
2629
- if (write()) return true;
2630
- if (lockIsStale(file, staleMs)) {
2631
- try {
2632
- fs17.rmSync(file, { force: true });
2633
- } catch {
2634
- }
2635
- return write();
2636
- }
2637
- return false;
2638
- }
2639
- function releaseLock(file) {
2640
- try {
2641
- fs17.rmSync(file, { force: true });
2642
- } catch {
2643
- }
2644
- }
2645
-
2646
- // src/engine/embeddings.ts
2647
- function resolveEmbedModel(model) {
2648
- return model ?? "bge-small-en-v1.5";
2649
- }
2650
- function modelCacheDir() {
2651
- const base = process.env.XDG_CACHE_HOME || path17.join(os.homedir(), ".cache");
2652
- return path17.join(base, "vibgrate", "models");
2653
- }
2654
- function modelReadyMarker(modelId) {
2655
- return path17.join(modelCacheDir(), `.ready-${safe(modelId)}`);
2656
- }
2657
- function isModelReady(modelId = resolveEmbedModel()) {
2658
- return fs17.existsSync(modelReadyMarker(modelId));
2659
- }
2660
- function unavailableMessage(reason) {
2661
- switch (reason) {
2662
- case "no-permission":
2663
- return `semantic search couldn't write its model cache at ${modelCacheDir()} \u2014 using fast lexical search instead. Relocate it with the standard XDG_CACHE_HOME, or run with --local to stay lexical quietly. (No admin/sudo is ever needed \u2014 the cache lives in your home folder.)`;
2664
- case "download-failed":
2665
- return `the semantic model isn't downloaded yet and couldn't be fetched (offline?) \u2014 using fast lexical search. It'll try again next time; run with --local to keep lexical without retrying.`;
2666
- case "not-installed":
2667
- return `the optional semantic backend isn't available here \u2014 using fast lexical search.`;
2668
- case "init-failed":
2669
- default:
2670
- return `the semantic model couldn't load \u2014 using fast lexical search.`;
2671
- }
2672
- }
2673
- function modelCacheInfo(modelId = resolveEmbedModel()) {
2674
- const dir = modelCacheDir();
2675
- return { dir, present: isModelReady(modelId) || fs17.existsSync(dir), bytes: dirSize(dir) };
2676
- }
2677
- function clearModelCache() {
2678
- const dir = modelCacheDir();
2679
- const bytes = dirSize(dir);
2680
- try {
2681
- fs17.rmSync(dir, { recursive: true, force: true });
2682
- } catch {
2683
- }
2684
- return bytes;
2685
- }
2686
- function dirSize(dir) {
2687
- let total = 0;
2688
- let entries;
2689
- try {
2690
- entries = fs17.readdirSync(dir, { withFileTypes: true });
2691
- } catch {
2692
- return 0;
2693
- }
2694
- for (const e of entries) {
2695
- const p = path17.join(dir, e.name);
2696
- if (e.isDirectory()) total += dirSize(p);
2697
- else {
2698
- try {
2699
- total += fs17.statSync(p).size;
2700
- } catch {
2701
- }
2702
- }
2703
- }
2704
- return total;
2705
- }
2706
- function isPermissionError(e) {
2707
- const code = e?.code;
2708
- return code === "EACCES" || code === "EPERM" || code === "EROFS";
2709
- }
2710
- function embeddingsCached(root, modelId) {
2711
- return fs17.existsSync(path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
2712
- }
2713
- async function loadEmbedder(options = {}) {
2714
- if (options.local) return null;
2715
- const modelId = resolveEmbedModel(options.model);
2716
- if (options.noDownload && !isModelReady(modelId)) return null;
2717
- const fail = (reason) => {
2718
- options.onUnavailable?.(reason);
2719
- return null;
2720
- };
2721
- const mod = await importEmbedBackend();
2722
- if (!mod?.FlagEmbedding) return fail("not-installed");
2723
- const cache = modelCacheDir();
2724
- try {
2725
- fs17.mkdirSync(cache, { recursive: true });
2726
- } catch (e) {
2727
- return fail(isPermissionError(e) ? "no-permission" : "init-failed");
2728
- }
2729
- try {
2730
- const model = await withTimeout(
2731
- mod.FlagEmbedding.init({
2732
- model: mapModel(mod, modelId),
2733
- cacheDir: cache,
2734
- showDownloadProgress: options.showDownloadProgress ?? false
2735
- }),
2736
- embedInitTimeoutMs(),
2737
- "embedding model init timed out"
2738
- );
2739
- try {
2740
- fs17.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
2741
- } catch {
2742
- }
2743
- return {
2744
- id: modelId,
2745
- async embed(texts) {
2746
- const out = [];
2747
- for await (const batch of model.embed(texts, 64)) {
2748
- for (const v of batch) out.push(Array.from(v));
2749
- }
2750
- return out;
2751
- },
2752
- async embedQuery(text) {
2753
- const v = await model.queryEmbed(text);
2754
- return Array.from(v);
2755
- }
2756
- };
2757
- } catch (e) {
2758
- return fail(isPermissionError(e) ? "no-permission" : "download-failed");
2759
- }
2760
- }
2761
- async function importEmbedBackend() {
2762
- const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;
2763
- if (hostDir) {
2764
- try {
2765
- const req = createRequire(path17.join(hostDir, "package.json"));
2766
- const mod = await import(pathToFileURL(req.resolve("fastembed")).href);
2767
- const resolved = mod?.FlagEmbedding ? mod : mod?.default;
2768
- if (resolved?.FlagEmbedding) return resolved;
2769
- } catch {
2770
- }
2771
- }
2772
- return await import('fastembed').catch(() => null);
2773
- }
2774
- function embedInitTimeoutMs() {
2775
- const n = Number(process.env.VG_EMBED_TIMEOUT_MS);
2776
- return Number.isFinite(n) && n > 0 ? n : 6e4;
2777
- }
2778
- async function withTimeout(promise, ms, message) {
2779
- let timer;
2780
- try {
2781
- return await Promise.race([
2782
- promise,
2783
- new Promise((_, reject) => {
2784
- timer = setTimeout(() => reject(new Error(message)), ms);
2785
- timer.unref?.();
2786
- })
2787
- ]);
2788
- } finally {
2789
- clearTimeout(timer);
2790
- }
2791
- }
2792
- function mapModel(mod, id) {
2793
- const m = mod.EmbeddingModel ?? {};
2794
- if (/bge-small/i.test(id)) return m.BGESmallENV15 ?? m.BGESmallEN ?? id;
2795
- if (/bge-base/i.test(id)) return m.BGEBaseENV15 ?? id;
2796
- if (/all-minilm/i.test(id)) return m.AllMiniLML6V2 ?? id;
2797
- return id;
2798
- }
2799
- var PATH_NOISE = /* @__PURE__ */ new Set([
2800
- "src",
2801
- "app",
2802
- "apps",
2803
- "lib",
2804
- "libs",
2805
- "dist",
2806
- "build",
2807
- "out",
2808
- "index",
2809
- "main",
2810
- "packages",
2811
- "pkg",
2812
- "test",
2813
- "tests",
2814
- "__tests__",
2815
- "spec",
2816
- "node_modules",
2817
- "internal",
2818
- "com",
2819
- "example",
2820
- "demo",
2821
- "java",
2822
- "net",
2823
- "org",
2824
- "www"
2825
- ]);
2826
- function pathContext(file) {
2827
- const parts = file.split("/");
2828
- const stem = (parts.pop() ?? "").replace(/\.[^.]+$/, "");
2829
- const dirs = parts.filter((p) => p && !PATH_NOISE.has(p.toLowerCase()));
2830
- return [...dirs.slice(-2), stem].filter(Boolean).join(" ");
2831
- }
2832
- function nodeEmbedText(node, areaLabel) {
2833
- return [node.qualifiedName, node.kind, node.signature ?? "", node.doc ?? "", pathContext(node.file), areaLabel ?? ""].filter(Boolean).join(" ");
2834
- }
2835
- function cosine(a, b) {
2836
- let dot2 = 0;
2837
- let na = 0;
2838
- let nb = 0;
2839
- const n = Math.min(a.length, b.length);
2840
- for (let i = 0; i < n; i++) {
2841
- dot2 += a[i] * b[i];
2842
- na += a[i] * a[i];
2843
- nb += b[i] * b[i];
2844
- }
2845
- if (na === 0 || nb === 0) return 0;
2846
- return dot2 / (Math.sqrt(na) * Math.sqrt(nb));
2847
- }
2848
- var EMBED_CHUNK = 256;
2849
- var CACHE_WRITE_INTERVAL_MS = 1500;
2850
- function vectorCachePath(root, modelId) {
2851
- return path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
2852
- }
2853
- function countPending(graph, root, modelId) {
2854
- const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
2855
- const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
2856
- let pending = 0;
2857
- for (const n of graph.nodes) {
2858
- if (n.kind === "file" || n.kind === "external") continue;
2859
- const h = hashString(nodeEmbedText(n, areaLabel.get(n.area)));
2860
- const cached2 = entries[n.id];
2861
- if (!cached2 || cached2.hash !== h) pending++;
2862
- }
2863
- return pending;
2864
- }
2865
- function readCacheEntries(file, modelId) {
2866
- if (!fs17.existsSync(file)) return {};
2867
- try {
2868
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2869
- if (loaded.model === modelId && loaded.entries) return loaded.entries;
2870
- } catch {
2871
- }
2872
- return {};
2873
- }
2874
- async function getNodeEmbeddings(graph, embedder, root, onProgress) {
2875
- const file = vectorCachePath(root, embedder.id);
2876
- let cache = { model: embedder.id, entries: {} };
2877
- if (fs17.existsSync(file)) {
2878
- try {
2879
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2880
- if (loaded.model === embedder.id && loaded.entries) cache = loaded;
2881
- } catch {
2882
- }
2883
- }
2884
- const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
2885
- const targets = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external");
2886
- const toEmbed = [];
2887
- const vectors = /* @__PURE__ */ new Map();
2888
- for (const n of targets) {
2889
- const text = nodeEmbedText(n, areaLabel.get(n.area));
2890
- const h = hashString(text);
2891
- const cached2 = cache.entries[n.id];
2892
- if (cached2 && cached2.hash === h) vectors.set(n.id, cached2.vec);
2893
- else toEmbed.push({ id: n.id, text, hash: h });
2894
- }
2895
- const persist = () => {
2896
- try {
2897
- fs17.mkdirSync(cacheDir(root), { recursive: true });
2898
- const tmp = `${file}.tmp.${process.pid}`;
2899
- fs17.writeFileSync(tmp, JSON.stringify(cache));
2900
- fs17.renameSync(tmp, file);
2901
- } catch {
2902
- }
2903
- };
2904
- if (toEmbed.length) {
2905
- const lock = `${file}.lock`;
2906
- if (!acquireLock(lock)) return vectors;
2907
- try {
2908
- onProgress?.(0, toEmbed.length);
2909
- let lastWrite = Date.now();
2910
- for (let i = 0; i < toEmbed.length; i += EMBED_CHUNK) {
2911
- const slice = toEmbed.slice(i, i + EMBED_CHUNK);
2912
- const vecs = await embedder.embed(slice.map((t) => t.text));
2913
- slice.forEach((t, j) => {
2914
- const vec = vecs[j] ?? [];
2915
- vectors.set(t.id, vec);
2916
- cache.entries[t.id] = { hash: t.hash, vec };
2917
- });
2918
- onProgress?.(Math.min(i + EMBED_CHUNK, toEmbed.length), toEmbed.length);
2919
- if (Date.now() - lastWrite >= CACHE_WRITE_INTERVAL_MS) {
2920
- persist();
2921
- lastWrite = Date.now();
2922
- }
2923
- }
2924
- const live = new Set(targets.map((n) => n.id));
2925
- for (const id of Object.keys(cache.entries)) if (!live.has(id)) delete cache.entries[id];
2926
- persist();
2927
- } finally {
2928
- releaseLock(lock);
2929
- }
2930
- }
2931
- return vectors;
2932
- }
2933
- function safe(id) {
2934
- return id.replace(/[^a-z0-9.-]+/gi, "_");
2935
- }
2936
2632
  var SNAPSHOT_VERSION = "vg-freshness/1";
2937
2633
  function snapshotPath(root) {
2938
- return path17.join(cacheDir(root), "freshness.json");
2634
+ return path2.join(cacheDir(root), "freshness.json");
2939
2635
  }
2940
2636
  function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
2941
2637
  const files = {};
2942
2638
  for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
2943
2639
  const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
2944
2640
  try {
2945
- fs17.mkdirSync(cacheDir(root), { recursive: true });
2946
- fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
2641
+ fs18.mkdirSync(cacheDir(root), { recursive: true });
2642
+ fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
2947
2643
  } catch {
2948
2644
  }
2949
2645
  }
2950
2646
  function loadSnapshot(root) {
2951
2647
  try {
2952
- const loaded = JSON.parse(fs17.readFileSync(snapshotPath(root), "utf8"));
2648
+ const loaded = JSON.parse(fs18.readFileSync(snapshotPath(root), "utf8"));
2953
2649
  if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
2954
2650
  return { ...loaded, scope: loaded.scope ?? {} };
2955
2651
  }
@@ -2985,7 +2681,7 @@ function probeFreshness(root) {
2985
2681
  }
2986
2682
  let stat;
2987
2683
  try {
2988
- stat = fs17.statSync(file.abs);
2684
+ stat = fs18.statSync(file.abs);
2989
2685
  } catch {
2990
2686
  drift.removed.push(file.rel);
2991
2687
  continue;
@@ -2993,7 +2689,7 @@ function probeFreshness(root) {
2993
2689
  if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
2994
2690
  let hash = "";
2995
2691
  try {
2996
- hash = hashBytes(fs17.readFileSync(file.abs));
2692
+ hash = hashBytes(fs18.readFileSync(file.abs));
2997
2693
  } catch {
2998
2694
  drift.removed.push(file.rel);
2999
2695
  continue;
@@ -3010,7 +2706,7 @@ function probeFreshness(root) {
3010
2706
  }
3011
2707
  if (absorbed && !hasDrift(drift)) {
3012
2708
  try {
3013
- fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
2709
+ fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
3014
2710
  } catch {
3015
2711
  }
3016
2712
  }
@@ -3033,348 +2729,6 @@ function pruneScope(scope) {
3033
2729
  if (scope.grammarsDir) out.grammarsDir = scope.grammarsDir;
3034
2730
  return out;
3035
2731
  }
3036
-
3037
- // src/engine/relations.ts
3038
- var GraphIndex = class {
3039
- constructor(graph) {
3040
- this.graph = graph;
3041
- for (const n of graph.nodes) this.nodeById.set(n.id, n);
3042
- for (const e of graph.edges) {
3043
- push2(this.outById, e.src, e);
3044
- push2(this.inById, e.dst, e);
3045
- }
3046
- }
3047
- graph;
3048
- nodeById = /* @__PURE__ */ new Map();
3049
- outById = /* @__PURE__ */ new Map();
3050
- inById = /* @__PURE__ */ new Map();
3051
- out(id, kind) {
3052
- const all = this.outById.get(id) ?? [];
3053
- return kind ? all.filter((e) => e.kind === kind) : all;
3054
- }
3055
- in(id, kind) {
3056
- const all = this.inById.get(id) ?? [];
3057
- return kind ? all.filter((e) => e.kind === kind) : all;
3058
- }
3059
- node(id) {
3060
- return this.nodeById.get(id);
3061
- }
3062
- /**
3063
- * Resolved nodes called by `id` — invocations (`call`) plus structural
3064
- * dependency references (`references`, e.g. a constructor-injected field's
3065
- * type), since both represent real usage a caller/impact question cares
3066
- * about. `references` is otherwise only emitted by the precise SCIP/tsc
3067
- * rungs and (for Java DI wiring) the heuristic rung — never a guess.
3068
- */
3069
- callees(id) {
3070
- return this.resolveTargets(this.out(id).filter(isUsageEdge), "dst");
3071
- }
3072
- /** Resolved nodes that call or structurally reference `id`. */
3073
- callers(id) {
3074
- return this.resolveTargets(this.in(id).filter(isUsageEdge), "src");
3075
- }
3076
- resolveTargets(edges, end) {
3077
- const out = [];
3078
- for (const e of edges) {
3079
- const node = this.nodeById.get(e[end]);
3080
- if (node) out.push({ edge: e, node });
3081
- }
3082
- return out;
3083
- }
3084
- };
3085
- var USAGE_KINDS = /* @__PURE__ */ new Set(["call", "references"]);
3086
- function isUsageEdge(e) {
3087
- return USAGE_KINDS.has(e.kind);
3088
- }
3089
- function push2(map, key, value) {
3090
- const list = map.get(key);
3091
- if (list) list.push(value);
3092
- else map.set(key, [value]);
3093
- }
3094
- var INDEX_CACHE = /* @__PURE__ */ new WeakMap();
3095
- function indexFor(graph) {
3096
- let idx = INDEX_CACHE.get(graph);
3097
- if (!idx) {
3098
- idx = new GraphIndex(graph);
3099
- INDEX_CACHE.set(graph, idx);
3100
- }
3101
- return idx;
3102
- }
3103
-
3104
- // src/engine/query.ts
3105
- var STOPWORDS = /* @__PURE__ */ new Set([
3106
- "the",
3107
- "a",
3108
- "an",
3109
- "is",
3110
- "are",
3111
- "was",
3112
- "were",
3113
- "be",
3114
- "to",
3115
- "of",
3116
- "in",
3117
- "on",
3118
- "for",
3119
- "and",
3120
- "or",
3121
- "where",
3122
- "what",
3123
- "which",
3124
- "how",
3125
- "do",
3126
- "does",
3127
- "did",
3128
- "i",
3129
- "we",
3130
- "it",
3131
- "this",
3132
- "that",
3133
- "with",
3134
- "when",
3135
- "who",
3136
- "why",
3137
- "can",
3138
- "should",
3139
- "my",
3140
- "our",
3141
- "you",
3142
- "your",
3143
- "from",
3144
- "by",
3145
- "at",
3146
- "as",
3147
- // Discovery-question scaffolding: words a caller uses to FRAME the ask
3148
- // ("find the code responsible for X", "I need to modify X, where do I
3149
- // start?", "explain how X works in this codebase") rather than to name the
3150
- // target. Left in, these compete on equal footing with the real identifier
3151
- // terms and can outrank it outright — e.g. "find the code responsible for
3152
- // deleteAsync" let "find" alone drag in every FindByIdAsync/FindAll method
3153
- // in the repo, none of them the target (VG-LOCATE-FAILURE-ANALYSIS.md).
3154
- "find",
3155
- "code",
3156
- "responsible",
3157
- "need",
3158
- "modify",
3159
- "me",
3160
- "implementation",
3161
- "explain",
3162
- "works",
3163
- "codebase",
3164
- "contains",
3165
- "file"
3166
- ]);
3167
- function queryGraph(graph, question, options = {}) {
3168
- const budget = options.budget ?? 2e3;
3169
- const limit = options.limit ?? 12;
3170
- const terms = tokenize(question);
3171
- const weightOf = termWeights(graph, terms);
3172
- const index = indexFor(graph);
3173
- const scored = [];
3174
- for (const node of graph.nodes) {
3175
- if (node.kind === "file" || node.kind === "external") continue;
3176
- const { score, why } = scoreNode(node, terms, weightOf);
3177
- if (score > 0) scored.push({ node, score: round2(score * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
3178
- }
3179
- scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
3180
- const seeds = scored.slice(0, limit);
3181
- const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget);
3182
- return { question, matches: seeds, context, tokensEstimate };
3183
- }
3184
- async function queryGraphSemantic(graph, question, options) {
3185
- const budget = options.budget ?? 2e3;
3186
- const limit = options.limit ?? 12;
3187
- const terms = tokenize(question);
3188
- const weightOf = termWeights(graph, terms);
3189
- const index = indexFor(graph);
3190
- const queryVec = await options.embedder.embedQuery(question);
3191
- const lexRaw = /* @__PURE__ */ new Map();
3192
- let lexMax = 0;
3193
- const whyById = /* @__PURE__ */ new Map();
3194
- for (const node of graph.nodes) {
3195
- if (node.kind === "file" || node.kind === "external") continue;
3196
- const { score, why } = scoreNode(node, terms, weightOf);
3197
- lexRaw.set(node.id, score);
3198
- whyById.set(node.id, why);
3199
- if (score > lexMax) lexMax = score;
3200
- }
3201
- const scored = [];
3202
- for (const node of graph.nodes) {
3203
- if (node.kind === "file" || node.kind === "external") continue;
3204
- const lexNorm = lexMax > 0 ? (lexRaw.get(node.id) ?? 0) / lexMax : 0;
3205
- const vec = options.nodeVectors.get(node.id);
3206
- const sem = vec ? Math.max(0, cosine(queryVec, vec)) : 0;
3207
- const hybrid = 0.5 * lexNorm + 0.5 * sem;
3208
- if (hybrid <= 0) continue;
3209
- const lexWhy = whyById.get(node.id);
3210
- const why = lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : "weak match");
3211
- scored.push({ node, score: round2(hybrid * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
3212
- }
3213
- scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
3214
- const seeds = scored.slice(0, limit);
3215
- const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget);
3216
- return { question, matches: seeds, context, tokensEstimate };
3217
- }
3218
- function tokenize(q2) {
3219
- return [
3220
- ...new Set(
3221
- q2.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 1 && !STOPWORDS.has(t))
3222
- )
3223
- ];
3224
- }
3225
- function scoreNode(node, terms, weightOf = () => 1) {
3226
- let score = 0;
3227
- const hits = [];
3228
- const name = node.name.toLowerCase();
3229
- const qn = node.qualifiedName.toLowerCase();
3230
- const file = node.file.toLowerCase();
3231
- const nameParts = identifierParts(node.name);
3232
- for (const t of terms) {
3233
- const w = weightOf(t);
3234
- if (name === t) {
3235
- score += 10 * w;
3236
- hits.push(t);
3237
- } else if (nameParts.has(t)) {
3238
- score += 6 * w;
3239
- hits.push(t);
3240
- } else if (name.includes(t)) {
3241
- score += 4 * w;
3242
- hits.push(t);
3243
- } else if (qn.includes(t)) {
3244
- score += 3 * w;
3245
- hits.push(t);
3246
- } else if (fuzzyPartMatch(t, nameParts)) {
3247
- score += 2 * w;
3248
- hits.push(`~${t}`);
3249
- } else if (file.includes(t)) {
3250
- score += 1 * w;
3251
- hits.push(t);
3252
- }
3253
- }
3254
- return { score, why: hits.length ? `matched: ${hits.join(", ")}` : "" };
3255
- }
3256
- var IMPORTANCE_WEIGHT = 0.4;
3257
- function termWeights(graph, terms) {
3258
- if (terms.length === 0) return () => 1;
3259
- const df = /* @__PURE__ */ new Map();
3260
- let n = 0;
3261
- for (const node of graph.nodes) {
3262
- if (node.kind === "file" || node.kind === "external") continue;
3263
- n++;
3264
- const name = node.name.toLowerCase();
3265
- const parts = identifierParts(node.name);
3266
- for (const t of terms) {
3267
- if (parts.has(t) || name.includes(t)) df.set(t, (df.get(t) ?? 0) + 1);
3268
- }
3269
- }
3270
- const w = /* @__PURE__ */ new Map();
3271
- for (const t of terms) {
3272
- const idf = Math.log((n + 1) / ((df.get(t) ?? 0) + 1)) + 1;
3273
- w.set(t, Math.max(0.5, Math.min(8, idf)));
3274
- }
3275
- return (t) => w.get(t) ?? 1;
3276
- }
3277
- function identifierParts(name) {
3278
- return new Set(
3279
- name.split(/[^\p{L}\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/u).filter(Boolean).map((s) => s.toLowerCase())
3280
- );
3281
- }
3282
- function fuzzyPartMatch(term, parts) {
3283
- for (const part of parts) {
3284
- const shared = sharedPrefixLen(term, part);
3285
- if (shared >= 5 && shared >= 0.6 * Math.min(term.length, part.length)) return true;
3286
- }
3287
- return false;
3288
- }
3289
- function sharedPrefixLen(a, b) {
3290
- const n = Math.min(a.length, b.length);
3291
- let i = 0;
3292
- while (i < n && a[i] === b[i]) i++;
3293
- return i;
3294
- }
3295
- function buildContext(graph, index, question, seeds, budget) {
3296
- const lines = [];
3297
- lines.push(`# Context for: ${question}`);
3298
- lines.push("");
3299
- if (seeds.length === 0) {
3300
- lines.push("_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._");
3301
- const text2 = lines.join("\n");
3302
- return { context: text2, tokensEstimate: estimateTokens(text2) };
3303
- }
3304
- for (const { node, why } of seeds) {
3305
- const block = [];
3306
- block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);
3307
- if (node.signature) block.push("`" + node.signature + "`");
3308
- const callees = index.callees(node.id).map((x) => x.node.qualifiedName);
3309
- const callers = index.callers(node.id).map((x) => x.node.qualifiedName);
3310
- if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(", ")}`);
3311
- if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(", ")}`);
3312
- block.push(`importance ${node.importance.toFixed(3)} \xB7 area #${node.area}${node.isHub ? " \xB7 hub" : ""} \xB7 ${why}`);
3313
- block.push("");
3314
- const candidate = lines.concat(block).join("\n");
3315
- if (estimateTokens(candidate) > budget && lines.length > 2) break;
3316
- lines.push(...block);
3317
- }
3318
- const text = lines.join("\n");
3319
- return { context: text, tokensEstimate: estimateTokens(text) };
3320
- }
3321
- function unique(xs) {
3322
- return [...new Set(xs)];
3323
- }
3324
- function estimateTokens(text) {
3325
- return Math.ceil(text.length / 4);
3326
- }
3327
- function round2(x) {
3328
- return Math.round(x * 1e6) / 1e6;
3329
- }
3330
-
3331
- // src/engine/lookup.ts
3332
- function findNodes(graph, query) {
3333
- const q2 = query.trim();
3334
- if (!q2) return [];
3335
- const byId = graph.nodes.filter((n) => n.id === q2);
3336
- if (byId.length) return byId;
3337
- const fileLine = /^(.*):(\d+)$/.exec(q2);
3338
- if (fileLine) {
3339
- const file = fileLine[1];
3340
- const line = Number(fileLine[2]);
3341
- const spanning = graph.nodes.filter((n) => n.file === file && n.span.start <= line && n.span.end >= line && n.kind !== "file").sort((a, b) => a.span.end - a.span.start - (b.span.end - b.span.start));
3342
- if (spanning.length) return spanning;
3343
- }
3344
- if (/[*?]/.test(q2)) {
3345
- const re = globToRegExp(q2);
3346
- return rank(graph.nodes.filter((n) => re.test(n.qualifiedName) || re.test(n.name) || re.test(n.file)));
3347
- }
3348
- const byQn = graph.nodes.filter((n) => n.qualifiedName === q2);
3349
- if (byQn.length) return rank(byQn);
3350
- const byName = graph.nodes.filter((n) => n.name === q2);
3351
- if (byName.length) return rank(byName);
3352
- const lower = q2.toLowerCase();
3353
- const ci = graph.nodes.filter(
3354
- (n) => n.qualifiedName.toLowerCase() === lower || n.name.toLowerCase() === lower
3355
- );
3356
- if (ci.length) return rank(ci);
3357
- return rank(graph.nodes.filter((n) => n.qualifiedName.toLowerCase().includes(lower))).slice(0, 25);
3358
- }
3359
- function resolveOne(graph, query, pick) {
3360
- const candidates = findNodes(graph, query);
3361
- if (candidates.length === 0) return { candidates };
3362
- if (pick && pick >= 1 && pick <= candidates.length) return { node: candidates[pick - 1], candidates };
3363
- if (candidates.length === 1) return { node: candidates[0], candidates };
3364
- return { candidates };
3365
- }
3366
- function nodeById(graph, id) {
3367
- return graph.nodes.find((n) => n.id === id);
3368
- }
3369
- function rank(nodes) {
3370
- return [...nodes].sort(
3371
- (a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)
3372
- );
3373
- }
3374
- function globToRegExp(glob) {
3375
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3376
- return new RegExp(`^${escaped}$`, "i");
3377
- }
3378
2732
  function buildGraphologyGraph(nodes, edges) {
3379
2733
  const g = new Graph({ type: "directed", multi: true, allowSelfLoops: true });
3380
2734
  for (const n of nodes) {
@@ -3398,57 +2752,6 @@ function shortestPath(graph, srcId, dstId) {
3398
2752
  if (reverse) return { ids: reverse.slice().reverse(), direction: "reverse" };
3399
2753
  return null;
3400
2754
  }
3401
-
3402
- // src/engine/impact.ts
3403
- var DEPEND_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
3404
- function impactOf(graph, rootId, opts = {}) {
3405
- const maxDepth = Math.max(1, opts.depth ?? 4);
3406
- const index = indexFor(graph);
3407
- const root = index.node(rootId);
3408
- const affected = /* @__PURE__ */ new Map();
3409
- let minEdgeConfidence = 1;
3410
- let frontier = [{ id: rootId, conf: 1 }];
3411
- for (let depth = 1; depth <= maxDepth; depth++) {
3412
- const nextFrontier = [];
3413
- for (const { id, conf } of frontier) {
3414
- for (const e of index.in(id)) {
3415
- if (!DEPEND_EDGES.has(e.kind)) continue;
3416
- minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);
3417
- const dependentId = e.src;
3418
- if (dependentId === rootId || affected.has(dependentId)) continue;
3419
- const node = index.node(dependentId);
3420
- if (!node) continue;
3421
- const conf2 = round3(conf * e.confidence * 0.9);
3422
- affected.set(dependentId, {
3423
- id: node.id,
3424
- name: node.qualifiedName,
3425
- kind: node.kind,
3426
- file: node.file,
3427
- line: node.span.start,
3428
- depth,
3429
- confidence: conf2
3430
- });
3431
- nextFrontier.push({ id: dependentId, conf: conf2 });
3432
- }
3433
- }
3434
- frontier = nextFrontier;
3435
- if (frontier.length === 0) break;
3436
- }
3437
- const items = [...affected.values()].sort(
3438
- (a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name)
3439
- );
3440
- return {
3441
- root: { id: rootId, name: root?.qualifiedName ?? rootId },
3442
- depth: maxDepth,
3443
- affected: items,
3444
- direct: items.filter((i) => i.depth === 1).length,
3445
- transitive: items.filter((i) => i.depth > 1).length,
3446
- minEdgeConfidence: round3(minEdgeConfidence)
3447
- };
3448
- }
3449
- function round3(x) {
3450
- return Math.round(x * 1e6) / 1e6;
3451
- }
3452
2755
  function coveringTests(graph, node, index) {
3453
2756
  const idx = index ?? indexFor(graph);
3454
2757
  const byId = idx.nodeById;
@@ -3484,10 +2787,10 @@ function testsToRun(graph, rootId, depth = 4) {
3484
2787
  };
3485
2788
  }
3486
2789
  function detectRunner(root, lang) {
3487
- const pkgPath = path17.join(root, "package.json");
3488
- if (fs17.existsSync(pkgPath)) {
2790
+ const pkgPath = path2.join(root, "package.json");
2791
+ if (fs18.existsSync(pkgPath)) {
3489
2792
  try {
3490
- const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf8"));
2793
+ const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf8"));
3491
2794
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3492
2795
  if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
3493
2796
  if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
@@ -3495,10 +2798,10 @@ function detectRunner(root, lang) {
3495
2798
  } catch {
3496
2799
  }
3497
2800
  }
3498
- if (lang === "py" || fs17.existsSync(path17.join(root, "pyproject.toml")) || fs17.existsSync(path17.join(root, "pytest.ini"))) {
2801
+ if (lang === "py" || fs18.existsSync(path2.join(root, "pyproject.toml")) || fs18.existsSync(path2.join(root, "pytest.ini"))) {
3499
2802
  return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
3500
2803
  }
3501
- if (lang === "go" || fs17.existsSync(path17.join(root, "go.mod"))) {
2804
+ if (lang === "go" || fs18.existsSync(path2.join(root, "go.mod"))) {
3502
2805
  return { name: "go test", command: () => "go test ./..." };
3503
2806
  }
3504
2807
  if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
@@ -3507,469 +2810,12 @@ function detectRunner(root, lang) {
3507
2810
  if (lang === "rb") return { name: "rspec", command: (f) => `bundle exec rspec ${f.join(" ")}`.trim() };
3508
2811
  return { name: "tests", command: (f) => f.length ? f.join(" ") : "(no runner detected)" };
3509
2812
  }
3510
- function hasFile(root, re) {
3511
- try {
3512
- return fs17.readdirSync(root).some((f) => re.test(f));
3513
- } catch {
3514
- return false;
3515
- }
3516
- }
3517
- var ECOSYSTEMS = ["npm", "pypi", "go", "rust", "ruby", "php", "dotnet", "swift", "dart", "java"];
3518
- function inventory(root) {
3519
- const manifests = findManifests(root);
3520
- const records = [];
3521
- records.push(...npmDeps(manifests.npm));
3522
- records.push(...pypiDeps(manifests.pypi));
3523
- records.push(...goDeps(manifests.go));
3524
- records.push(...cargoDeps(manifests.rust));
3525
- records.push(...rubyDeps(manifests.ruby));
3526
- records.push(...phpDeps(manifests.php));
3527
- records.push(...dotnetDeps(manifests.dotnet));
3528
- records.push(...swiftDeps(manifests.swift));
3529
- records.push(...dartDeps(manifests.dart));
3530
- records.push(...javaDeps(manifests.java));
3531
- const seen = /* @__PURE__ */ new Set();
3532
- const deduped = records.filter((r) => {
3533
- const key = `${r.ecosystem}\0${r.name}`;
3534
- if (seen.has(key)) return false;
3535
- seen.add(key);
3536
- return true;
3537
- });
3538
- deduped.sort((a, b) => a.ecosystem.localeCompare(b.ecosystem) || a.name.localeCompare(b.name));
3539
- const counts = { total: deduped.length };
3540
- for (const e of ECOSYSTEMS) counts[e] = 0;
3541
- for (const r of deduped) counts[r.ecosystem]++;
3542
- return { records: deduped, counts };
3543
- }
3544
- var SKIP_DIRS2 = /* @__PURE__ */ new Set([
3545
- "node_modules",
3546
- ".git",
3547
- "dist",
3548
- "build",
3549
- "out",
3550
- ".vibgrate",
3551
- "vendor",
3552
- ".venv",
3553
- "venv",
3554
- "env",
3555
- "__pycache__",
3556
- "target",
3557
- ".next",
3558
- ".nuxt",
3559
- "coverage",
3560
- ".cache"
3561
- ]);
3562
- var MANIFEST_BY_FILE = {
3563
- "package.json": "npm",
3564
- "requirements.txt": "pypi",
3565
- "pyproject.toml": "pypi",
3566
- "go.mod": "go",
3567
- "Cargo.toml": "rust",
3568
- Gemfile: "ruby",
3569
- "composer.json": "php",
3570
- "Package.swift": "swift",
3571
- "pubspec.yaml": "dart",
3572
- "pom.xml": "java",
3573
- "build.gradle": "java",
3574
- "build.gradle.kts": "java"
3575
- };
3576
- function findManifests(root) {
3577
- const set = Object.fromEntries(ECOSYSTEMS.map((e) => [e, []]));
3578
- const MAX_ENTRIES2 = 2e4;
3579
- let scanned = 0;
3580
- const walk2 = (dir, depth) => {
3581
- if (depth > 8 || scanned > MAX_ENTRIES2) return;
3582
- let entries;
3583
- try {
3584
- entries = fs17.readdirSync(dir, { withFileTypes: true });
3585
- } catch {
3586
- return;
3587
- }
3588
- for (const e of entries) {
3589
- scanned++;
3590
- if (scanned > MAX_ENTRIES2) break;
3591
- if (e.isDirectory()) {
3592
- if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(path17.join(dir, e.name), depth + 1);
3593
- continue;
3594
- }
3595
- const eco = MANIFEST_BY_FILE[e.name] ?? (/\.(cs|fs)proj$/i.test(e.name) ? "dotnet" : void 0);
3596
- if (eco) set[eco].push(path17.join(dir, e.name));
3597
- }
3598
- };
3599
- walk2(root, 0);
3600
- for (const e of ECOSYSTEMS) set[e].sort();
3601
- return set;
3602
- }
3603
- function npmDeps(files) {
3604
- const out = [];
3605
- for (const file of files) {
3606
- let pkg;
3607
- try {
3608
- pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
3609
- } catch {
3610
- continue;
3611
- }
3612
- const dir = path17.dirname(file);
3613
- const all = { ...pkg.dependencies, ...pkg.devDependencies };
3614
- for (const [name, declared] of Object.entries(all)) {
3615
- out.push({ name, ecosystem: "npm", declared, installed: installedNpmVersion(dir, name) });
3616
- }
3617
- }
3618
- return out;
3619
- }
3620
- function installedNpmVersion(dir, name) {
3621
- let cur = dir;
3622
- for (let i = 0; i < 12; i++) {
3623
- const p = path17.join(cur, "node_modules", name, "package.json");
3624
- try {
3625
- return JSON.parse(fs17.readFileSync(p, "utf8")).version;
3626
- } catch {
3627
- }
3628
- const parent = path17.dirname(cur);
3629
- if (parent === cur) break;
3630
- cur = parent;
3631
- }
3632
- return void 0;
3633
- }
3634
- function installedTreeVersion(root, ecosystem, name) {
3635
- if (ecosystem === "pypi") return installedPypiVersion(root, name);
3636
- if (ecosystem === "php") return installedPhpVersion(root, name);
3637
- return void 0;
3638
- }
3639
- function normalizePep503(name) {
3640
- return name.trim().toLowerCase().replace(/[-_.]+/g, "-");
3641
- }
3642
- function sitePackagesDirs(base) {
3643
- const out = [];
3644
- try {
3645
- for (const d of fs17.readdirSync(path17.join(base, "lib"))) {
3646
- if (d.startsWith("python")) out.push(path17.join(base, "lib", d, "site-packages"));
3647
- }
3648
- } catch {
3649
- }
3650
- out.push(path17.join(base, "Lib", "site-packages"));
3651
- return out;
3652
- }
3653
- function installedPypiVersion(root, name) {
3654
- const target = normalizePep503(name);
3655
- for (const venv of [".venv", "venv", "env", ".tox"]) {
3656
- for (const sp of sitePackagesDirs(path17.join(root, venv))) {
3657
- let entries;
3658
- try {
3659
- entries = fs17.readdirSync(sp);
3660
- } catch {
3661
- continue;
3662
- }
3663
- for (const entry of entries) {
3664
- const m = /^(.+?)-(\d[^-]*)\.dist-info$/.exec(entry);
3665
- if (m && normalizePep503(m[1]) === target) return m[2];
3666
- }
3667
- }
3668
- }
3669
- return void 0;
3670
- }
3671
- function installedPhpVersion(root, name) {
3672
- let data;
3673
- try {
3674
- data = JSON.parse(fs17.readFileSync(path17.join(root, "vendor", "composer", "installed.json"), "utf8"));
3675
- } catch {
3676
- return void 0;
3677
- }
3678
- const pkgs = Array.isArray(data) ? data : data.packages;
3679
- if (!Array.isArray(pkgs)) return void 0;
3680
- const p = pkgs.find((x) => x && typeof x === "object" && x.name === name);
3681
- return typeof p?.version === "string" ? p.version.replace(/^v/, "") : void 0;
3682
- }
3683
- function pypiDeps(files) {
3684
- const byName = /* @__PURE__ */ new Map();
3685
- const add3 = (name, declared) => {
3686
- const clean = name.trim().replace(/\[.*\]$/, "");
3687
- if (!clean || clean.toLowerCase() === "python") return;
3688
- if (!byName.has(clean)) byName.set(clean, { name: clean, ecosystem: "pypi", declared: declared.trim() || "*" });
3689
- };
3690
- for (const file of files) {
3691
- let text;
3692
- try {
3693
- text = fs17.readFileSync(file, "utf8");
3694
- } catch {
3695
- continue;
3696
- }
3697
- if (path17.basename(file) === "requirements.txt") {
3698
- for (const line of text.split("\n")) {
3699
- const t = line.trim();
3700
- if (!t || t.startsWith("#") || t.startsWith("-")) continue;
3701
- const m = /^([A-Za-z0-9._-]+(?:\[[^\]]*\])?)\s*([<>=!~).*]+.*)?$/.exec(t);
3702
- if (m) add3(m[1], m[2] ?? "*");
3703
- }
3704
- } else {
3705
- for (const dep of pep621Dependencies(text)) {
3706
- const m = /^([A-Za-z0-9._-]+(?:\[[^\]]*\])?)\s*(.*)$/.exec(dep.trim());
3707
- if (m) add3(m[1], m[2] ?? "*");
3708
- }
3709
- for (const [name, spec] of poetryDependencies(text)) add3(name, spec);
3710
- }
3711
- }
3712
- return [...byName.values()];
3713
- }
3714
- function pep621Dependencies(text) {
3715
- const section = sectionBody(text, "project");
3716
- if (!section) return [];
3717
- const start = /dependencies\s*=\s*\[/.exec(section);
3718
- if (!start) return [];
3719
- let depth = 0;
3720
- let inStr = false;
3721
- let strCh = "";
3722
- const out = [];
3723
- let cur = "";
3724
- for (let i = start.index + start[0].length - 1; i < section.length; i++) {
3725
- const ch = section[i];
3726
- if (inStr) {
3727
- if (ch === strCh) {
3728
- out.push(cur);
3729
- inStr = false;
3730
- } else cur += ch;
3731
- continue;
3732
- }
3733
- if (ch === '"' || ch === "'") {
3734
- inStr = true;
3735
- strCh = ch;
3736
- cur = "";
3737
- } else if (ch === "[") depth++;
3738
- else if (ch === "]") {
3739
- depth--;
3740
- if (depth === 0) break;
3741
- }
3742
- }
3743
- return out;
3744
- }
3745
- function poetryDependencies(text) {
3746
- const out = [];
3747
- const lines = text.split("\n");
3748
- let inDeps = false;
3749
- for (const raw of lines) {
3750
- const line = raw.trim();
3751
- if (line.startsWith("[")) {
3752
- inDeps = /^\[tool\.poetry(\.group\.[^.\]]+)?\.dependencies\]$/.test(line);
3753
- continue;
3754
- }
3755
- if (!inDeps || !line || line.startsWith("#")) continue;
3756
- const m = /^([A-Za-z0-9._-]+)\s*=\s*["']([^"']*)["']/.exec(line);
3757
- if (m) out.push([m[1], m[2] || "*"]);
3758
- }
3759
- return out;
3760
- }
3761
- function sectionBody(text, name) {
3762
- const lines = text.split("\n");
3763
- let capturing = false;
3764
- const body = [];
3765
- for (const raw of lines) {
3766
- const line = raw.trim();
3767
- if (/^\[/.test(line)) {
3768
- if (capturing) break;
3769
- capturing = line === `[${name}]`;
3770
- continue;
3771
- }
3772
- if (capturing) body.push(raw);
3773
- }
3774
- return capturing || body.length ? body.join("\n") : null;
3775
- }
3776
- function goDeps(files) {
3777
- const out = [];
3778
- for (const mod of files) {
3779
- let text;
3780
- try {
3781
- text = fs17.readFileSync(mod, "utf8");
3782
- } catch {
3783
- continue;
3784
- }
3785
- const re = /^\s*(?:require\s+)?([\w./-]+)\s+v(\S+)/gm;
3786
- let m;
3787
- while (m = re.exec(text)) {
3788
- if (m[1] === "go" || m[1] === "require" || m[1].startsWith("module")) continue;
3789
- out.push({ name: m[1], ecosystem: "go", declared: `v${m[2]}` });
3790
- }
3791
- }
3792
- return out;
3793
- }
3794
- function cargoDeps(files) {
3795
- const out = [];
3796
- for (const file of files) {
3797
- let text;
3798
- try {
3799
- text = fs17.readFileSync(file, "utf8");
3800
- } catch {
3801
- continue;
3802
- }
3803
- for (const table of ["dependencies", "dev-dependencies", "build-dependencies"]) {
3804
- const body = sectionBody(text, table);
3805
- if (!body) continue;
3806
- for (const raw of body.split("\n")) {
3807
- const line = raw.trim();
3808
- if (!line || line.startsWith("#")) continue;
3809
- const m = /^([A-Za-z0-9._-]+)\s*=\s*(.+)$/.exec(line);
3810
- if (!m) continue;
3811
- const rhs = m[2].trim();
3812
- let declared = "*";
3813
- if (rhs.startsWith('"')) {
3814
- const sm = /^"([^"]+)"/.exec(rhs);
3815
- if (sm) declared = sm[1];
3816
- } else if (rhs.startsWith("{")) {
3817
- const vm = /version\s*=\s*"([^"]+)"/.exec(rhs);
3818
- if (vm) declared = vm[1];
3819
- }
3820
- out.push({ name: m[1], ecosystem: "rust", declared });
3821
- }
3822
- }
3823
- }
3824
- return out;
3825
- }
3826
- function rubyDeps(files) {
3827
- const out = [];
3828
- for (const file of files) {
3829
- let text;
3830
- try {
3831
- text = fs17.readFileSync(file, "utf8");
3832
- } catch {
3833
- continue;
3834
- }
3835
- const re = /^\s*gem\s+["']([^"']+)["']\s*(?:,\s*["']([^"']+)["'])?/gm;
3836
- let m;
3837
- while (m = re.exec(text)) out.push({ name: m[1], ecosystem: "ruby", declared: m[2] ?? "*" });
3838
- }
3839
- return out;
3840
- }
3841
- function phpDeps(files) {
3842
- const out = [];
3843
- for (const file of files) {
3844
- let pkg;
3845
- try {
3846
- pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
3847
- } catch {
3848
- continue;
3849
- }
3850
- const all = { ...pkg.require, ...pkg["require-dev"] };
3851
- for (const [name, declared] of Object.entries(all)) {
3852
- if (!name.includes("/")) continue;
3853
- out.push({ name, ecosystem: "php", declared });
3854
- }
3855
- }
3856
- return out;
3857
- }
3858
- function dotnetDeps(files) {
3859
- const out = [];
3860
- for (const file of files) {
3861
- let text;
3862
- try {
3863
- text = fs17.readFileSync(file, "utf8");
3864
- } catch {
3865
- continue;
3866
- }
3867
- const tagRe = /<PackageReference\b[^>]*>/gi;
3868
- let m;
3869
- while (m = tagRe.exec(text)) {
3870
- const inc = /Include\s*=\s*"([^"]+)"/i.exec(m[0]);
3871
- if (!inc) continue;
3872
- const ver = /Version\s*=\s*"([^"]+)"/i.exec(m[0]);
3873
- out.push({ name: inc[1], ecosystem: "dotnet", declared: ver?.[1] ?? "*" });
3874
- }
3875
- }
3876
- return out;
3877
- }
3878
- function swiftDeps(files) {
3879
- const out = [];
3880
- for (const file of files) {
3881
- let text;
3882
- try {
3883
- text = fs17.readFileSync(file, "utf8");
3884
- } catch {
3885
- continue;
3886
- }
3887
- const re = /\.package\(/g;
3888
- let m;
3889
- while (m = re.exec(text)) {
3890
- const win = text.slice(m.index, m.index + 200);
3891
- const urlM = /url:\s*"([^"]+)"/.exec(win);
3892
- if (!urlM) continue;
3893
- const name = (urlM[1].replace(/\.git$/, "").split("/").filter(Boolean).pop() ?? urlM[1]).toLowerCase();
3894
- const verM = /"(\d+\.\d+(?:\.\d+)?)"/.exec(win.slice(urlM.index + urlM[0].length));
3895
- out.push({ name, ecosystem: "swift", declared: verM?.[1] ?? "*" });
3896
- }
3897
- }
3898
- return out;
3899
- }
3900
- function dartDeps(files) {
3901
- const out = [];
3902
- for (const file of files) {
3903
- let text;
3904
- try {
3905
- text = fs17.readFileSync(file, "utf8");
3906
- } catch {
3907
- continue;
3908
- }
3909
- let section = "";
3910
- for (const raw of text.split("\n")) {
3911
- const top = /^([a-z_]+):\s*$/.exec(raw);
3912
- if (top) {
3913
- section = top[1];
3914
- continue;
3915
- }
3916
- if (section !== "dependencies" && section !== "dev_dependencies") continue;
3917
- const m = /^ ([A-Za-z0-9_.]+):\s*(\S.*)?$/.exec(raw);
3918
- if (!m || m[1] === "flutter" || m[1] === "sdk") continue;
3919
- const declared = (m[2] ?? "").replace(/^["']|["']$/g, "").trim() || "*";
3920
- out.push({ name: m[1], ecosystem: "dart", declared });
3921
- }
3922
- }
3923
- return out;
3924
- }
3925
- function javaDeps(files) {
3926
- const out = [];
3927
- for (const file of files) {
3928
- let text;
3929
- try {
3930
- text = fs17.readFileSync(file, "utf8");
3931
- } catch {
3932
- continue;
3933
- }
3934
- if (path17.basename(file) === "pom.xml") {
3935
- for (const block of text.match(/<dependency>[\s\S]*?<\/dependency>/g) ?? []) {
3936
- const g = /<groupId>([^<]+)<\/groupId>/.exec(block);
3937
- const a = /<artifactId>([^<]+)<\/artifactId>/.exec(block);
3938
- if (!g || !a) continue;
3939
- const v = /<version>([^<]+)<\/version>/.exec(block);
3940
- const declared = v ? v[1].trim().startsWith("${") ? "*" : v[1].trim() : "*";
3941
- out.push({ name: `${g[1].trim()}:${a[1].trim()}`, ecosystem: "java", declared });
3942
- }
3943
- } else {
3944
- const re = /(?:implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|annotationProcessor)\s*[(]?\s*["']([\w.-]+):([\w.-]+):([^"']+)["']/g;
3945
- let m;
3946
- while (m = re.exec(text)) out.push({ name: `${m[1]}:${m[2]}`, ecosystem: "java", declared: m[3] });
3947
- }
3948
- }
3949
- return out;
3950
- }
3951
- async function enrichOnline(records, fetchImpl = globalThis.fetch) {
3952
- for (const r of records) {
3953
- if (r.ecosystem !== "npm" || !r.installed) continue;
3954
- try {
3955
- const res = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(r.name)}/latest`);
3956
- if (!res.ok) continue;
3957
- const data = await res.json();
3958
- r.latest = data.version;
3959
- r.drift = classify(r.installed, r.latest);
3960
- } catch {
3961
- r.drift = "unknown";
3962
- }
3963
- }
3964
- }
3965
- function classify(installed, latest) {
3966
- if (!latest) return "unknown";
3967
- const a = installed.split(".").map(Number);
3968
- const b = latest.split(".").map(Number);
3969
- if (a[0] !== b[0]) return "major";
3970
- if (a[1] !== b[1]) return "minor";
3971
- if (a[2] !== b[2]) return "patch";
3972
- return "current";
2813
+ function hasFile(root, re) {
2814
+ try {
2815
+ return fs18.readdirSync(root).some((f) => re.test(f));
2816
+ } catch {
2817
+ return false;
2818
+ }
3973
2819
  }
3974
2820
  function discoverModels(home = os.homedir()) {
3975
2821
  return [...ollama(home), ...lmStudio(home), ...looseGguf(home)].sort(
@@ -3977,10 +2823,10 @@ function discoverModels(home = os.homedir()) {
3977
2823
  );
3978
2824
  }
3979
2825
  function ollama(home) {
3980
- const base = path17.join(home, ".ollama", "models", "manifests");
2826
+ const base = path2.join(home, ".ollama", "models", "manifests");
3981
2827
  const out = [];
3982
2828
  walk(base, 4, (file) => {
3983
- const rel2 = path17.relative(base, file).split(path17.sep);
2829
+ const rel2 = path2.relative(base, file).split(path2.sep);
3984
2830
  if (rel2.length >= 2) {
3985
2831
  const tag = rel2[rel2.length - 1];
3986
2832
  const model = rel2[rel2.length - 2];
@@ -3990,24 +2836,24 @@ function ollama(home) {
3990
2836
  return dedupe(out);
3991
2837
  }
3992
2838
  function lmStudio(home) {
3993
- const bases = [path17.join(home, ".lmstudio", "models"), path17.join(home, ".cache", "lm-studio", "models")];
2839
+ const bases = [path2.join(home, ".lmstudio", "models"), path2.join(home, ".cache", "lm-studio", "models")];
3994
2840
  const out = [];
3995
2841
  for (const base of bases) {
3996
2842
  walk(base, 5, (file) => {
3997
2843
  if (!file.endsWith(".gguf")) return;
3998
- const rel2 = path17.relative(base, file).replace(/\\/g, "/");
2844
+ const rel2 = path2.relative(base, file).replace(/\\/g, "/");
3999
2845
  out.push({ runtime: "lm-studio", name: rel2, path: file });
4000
2846
  });
4001
2847
  }
4002
2848
  return dedupe(out);
4003
2849
  }
4004
2850
  function looseGguf(home) {
4005
- const bases = [path17.join(home, "models"), path17.join(home, ".cache", "huggingface")];
2851
+ const bases = [path2.join(home, "models"), path2.join(home, ".cache", "huggingface")];
4006
2852
  const out = [];
4007
2853
  for (const base of bases) {
4008
2854
  walk(base, 4, (file) => {
4009
2855
  if (!file.endsWith(".gguf")) return;
4010
- out.push({ runtime: "gguf", name: path17.basename(file), path: file });
2856
+ out.push({ runtime: "gguf", name: path2.basename(file), path: file });
4011
2857
  });
4012
2858
  }
4013
2859
  return dedupe(out);
@@ -4016,12 +2862,12 @@ function walk(dir, depth, onFile) {
4016
2862
  if (depth < 0) return;
4017
2863
  let entries;
4018
2864
  try {
4019
- entries = fs17.readdirSync(dir, { withFileTypes: true });
2865
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
4020
2866
  } catch {
4021
2867
  return;
4022
2868
  }
4023
2869
  for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
4024
- const abs = path17.join(dir, e.name);
2870
+ const abs = path2.join(dir, e.name);
4025
2871
  if (e.isDirectory()) walk(abs, depth - 1, onFile);
4026
2872
  else if (e.isFile()) onFile(abs);
4027
2873
  }
@@ -4037,457 +2883,11 @@ function dedupe(models) {
4037
2883
  }
4038
2884
  return out;
4039
2885
  }
4040
- function lockfileVersion(root, ecosystem, name) {
4041
- if (ecosystem === "npm") return packageLockVersion(root, name) ?? yarnLockVersion(root, name);
4042
- if (ecosystem === "pypi") return pypiLockVersion(root, name);
4043
- if (ecosystem === "rust") return tomlPackageLock(root, "Cargo.lock", name, rustName);
4044
- if (ecosystem === "ruby") return gemfileLock(root, name);
4045
- if (ecosystem === "php") return composerLock(root, name);
4046
- if (ecosystem === "dotnet") return packagesLock(root, name);
4047
- if (ecosystem === "swift") return packageResolved(root, name);
4048
- if (ecosystem === "dart") return pubspecLock(root, name);
4049
- if (ecosystem === "java") return gradleLock(root, name);
4050
- return void 0;
4051
- }
4052
- function gradleLock(root, name) {
4053
- let text;
4054
- try {
4055
- text = fs17.readFileSync(path17.join(root, "gradle.lockfile"), "utf8");
4056
- } catch {
4057
- return void 0;
4058
- }
4059
- const m = new RegExp(`^${escapeRegExp(name)}:([^=\\s]+)=`, "m").exec(text);
4060
- return m ? m[1] : void 0;
4061
- }
4062
- function packageResolved(root, name) {
4063
- let data;
4064
- try {
4065
- data = JSON.parse(fs17.readFileSync(path17.join(root, "Package.resolved"), "utf8"));
4066
- } catch {
4067
- return void 0;
4068
- }
4069
- const pins = data.pins ?? data.object?.pins;
4070
- if (!Array.isArray(pins)) return void 0;
4071
- const target = name.toLowerCase();
4072
- for (const p of pins) {
4073
- const id = (p.identity ?? p.package ?? "").toLowerCase();
4074
- if (id === target && p.state?.version) return p.state.version;
4075
- }
4076
- return void 0;
4077
- }
4078
- function pubspecLock(root, name) {
4079
- let text;
4080
- try {
4081
- text = fs17.readFileSync(path17.join(root, "pubspec.lock"), "utf8");
4082
- } catch {
4083
- return void 0;
4084
- }
4085
- let inPkg = false;
4086
- for (const line of text.split("\n")) {
4087
- const key = /^ ([A-Za-z0-9_.]+):\s*$/.exec(line);
4088
- if (key) {
4089
- inPkg = key[1] === name;
4090
- continue;
4091
- }
4092
- if (inPkg) {
4093
- const vm = /^\s+version:\s*"?([^"\s]+)"?/.exec(line);
4094
- if (vm) return vm[1];
4095
- }
4096
- }
4097
- return void 0;
4098
- }
4099
- function escapeRegExp(s) {
4100
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4101
- }
4102
- function gemfileLock(root, name) {
4103
- let text;
4104
- try {
4105
- text = fs17.readFileSync(path17.join(root, "Gemfile.lock"), "utf8");
4106
- } catch {
4107
- return void 0;
4108
- }
4109
- const m = new RegExp(`^ ${escapeRegExp(name)} \\((\\d[^)]*)\\)`, "m").exec(text);
4110
- return m ? m[1] : void 0;
4111
- }
4112
- function composerLock(root, name) {
4113
- let data;
4114
- try {
4115
- data = JSON.parse(fs17.readFileSync(path17.join(root, "composer.lock"), "utf8"));
4116
- } catch {
4117
- return void 0;
4118
- }
4119
- for (const section of ["packages", "packages-dev"]) {
4120
- const arr = data[section];
4121
- if (!Array.isArray(arr)) continue;
4122
- const p = arr.find((x) => x && x.name === name);
4123
- if (p && typeof p.version === "string") return p.version.replace(/^v/, "");
4124
- }
4125
- return void 0;
4126
- }
4127
- function packagesLock(root, name) {
4128
- let data;
4129
- try {
4130
- data = JSON.parse(fs17.readFileSync(path17.join(root, "packages.lock.json"), "utf8"));
4131
- } catch {
4132
- return void 0;
4133
- }
4134
- const target = name.toLowerCase();
4135
- for (const fw of Object.values(data.dependencies ?? {})) {
4136
- if (!fw || typeof fw !== "object") continue;
4137
- for (const [id, v] of Object.entries(fw)) {
4138
- if (id.toLowerCase() === target && v && typeof v.resolved === "string") return v.resolved;
4139
- }
4140
- }
4141
- return void 0;
4142
- }
4143
- function pep503(name) {
4144
- return name.trim().toLowerCase().replace(/[-_.]+/g, "-");
4145
- }
4146
- function rustName(name) {
4147
- return name.trim().toLowerCase();
4148
- }
4149
- function pypiLockVersion(root, name) {
4150
- return tomlPackageLock(root, "poetry.lock", name, pep503) ?? tomlPackageLock(root, "uv.lock", name, pep503) ?? pipfileLock(root, name);
4151
- }
4152
- function tomlPackageLock(root, file, name, normalize3) {
4153
- let text;
4154
- try {
4155
- text = fs17.readFileSync(path17.join(root, file), "utf8");
4156
- } catch {
4157
- return void 0;
4158
- }
4159
- const target = normalize3(name);
4160
- for (const block of text.split(/\[\[package\]\]/)) {
4161
- const nm = /(?:^|\n)\s*name\s*=\s*"([^"]+)"/.exec(block);
4162
- if (nm && normalize3(nm[1]) === target) {
4163
- const ver = /(?:^|\n)\s*version\s*=\s*"([^"]+)"/.exec(block);
4164
- if (ver) return ver[1];
4165
- }
4166
- }
4167
- return void 0;
4168
- }
4169
- function pipfileLock(root, name) {
4170
- let data;
4171
- try {
4172
- data = JSON.parse(fs17.readFileSync(path17.join(root, "Pipfile.lock"), "utf8"));
4173
- } catch {
4174
- return void 0;
4175
- }
4176
- const target = pep503(name);
4177
- for (const section of ["default", "develop"]) {
4178
- const deps = data[section];
4179
- if (!deps || typeof deps !== "object") continue;
4180
- for (const [k, v] of Object.entries(deps)) {
4181
- if (pep503(k) === target && v && typeof v.version === "string") {
4182
- return v.version.replace(/^==/, "");
4183
- }
4184
- }
4185
- }
4186
- return void 0;
4187
- }
4188
- function packageLockVersion(root, name) {
4189
- let data;
4190
- try {
4191
- data = JSON.parse(fs17.readFileSync(path17.join(root, "package-lock.json"), "utf8"));
4192
- } catch {
4193
- return void 0;
4194
- }
4195
- const top = data.packages?.[`node_modules/${name}`]?.version;
4196
- if (typeof top === "string") return top;
4197
- const v1 = data.dependencies?.[name]?.version;
4198
- return typeof v1 === "string" ? v1 : void 0;
4199
- }
4200
- function yarnLockVersion(root, name) {
4201
- let text;
4202
- try {
4203
- text = fs17.readFileSync(path17.join(root, "yarn.lock"), "utf8");
4204
- } catch {
4205
- return void 0;
4206
- }
4207
- let inBlock = false;
4208
- for (const line of text.split("\n")) {
4209
- if (line && !/^\s/.test(line) && !line.startsWith("#")) {
4210
- inBlock = headerNamesPackage(line, name);
4211
- } else if (inBlock) {
4212
- const m = /^\s+version:?\s+"?([^"\s]+)"?/.exec(line);
4213
- if (m) return m[1];
4214
- }
4215
- }
4216
- return void 0;
4217
- }
4218
- function headerNamesPackage(header, name) {
4219
- return header.replace(/:\s*$/, "").split(",").some((spec) => {
4220
- const s = spec.trim().replace(/^"|"$/g, "");
4221
- const at = s.lastIndexOf("@");
4222
- if (at <= 0) return false;
4223
- const specName = s.slice(0, at);
4224
- return specName === name;
4225
- });
4226
- }
4227
-
4228
- // src/engine/lib.ts
4229
- var LIB_SCHEMA = "vg-lib/1.0";
4230
- function catalogPath(root) {
4231
- return path17.join(root, "vibgrate.lib.json");
4232
- }
4233
- function libDir(root) {
4234
- return path17.join(root, ".vibgrate", "lib");
4235
- }
4236
- function libId(name) {
4237
- return name.trim().toLowerCase().replace(/^@/, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
4238
- }
4239
- function loadCatalog(root) {
4240
- const file = catalogPath(root);
4241
- if (fs17.existsSync(file)) {
4242
- try {
4243
- const data = JSON.parse(fs17.readFileSync(file, "utf8"));
4244
- if (data.schemaVersion === LIB_SCHEMA && data.libraries) return data;
4245
- } catch {
4246
- }
4247
- }
4248
- return { schemaVersion: LIB_SCHEMA, libraries: {} };
4249
- }
4250
- function saveCatalog(root, catalog) {
4251
- fs17.writeFileSync(catalogPath(root), `${stableStringify(catalog, 2)}
4252
- `);
4253
- }
4254
- function resolveLib(catalog, name) {
4255
- const id = libId(name);
4256
- if (catalog.libraries[id]) return catalog.libraries[id];
4257
- const lower = name.toLowerCase();
4258
- const all = Object.values(catalog.libraries);
4259
- return all.find((e) => e.name.toLowerCase() === lower) ?? all.find((e) => e.id.includes(id) || e.name.toLowerCase().includes(lower));
4260
- }
4261
- function installedVersion(root, name) {
4262
- const rec = inventory(root).records.find((r) => r.name === name);
4263
- return rec?.installed ?? rec?.declared;
4264
- }
4265
- var stripRange = (v) => v?.replace(/^[v^~>=<\s]+/, "");
4266
- function resolveVersion(root, name) {
4267
- const rec = inventory(root).records.find((r) => r.name === name);
4268
- const declared = rec?.declared;
4269
- const installed = rec?.installed ?? (rec ? installedTreeVersion(root, rec.ecosystem, name) : void 0);
4270
- const lockfile = rec ? lockfileVersion(root, rec.ecosystem, name) : void 0;
4271
- let mismatch;
4272
- if (lockfile && installed && stripRange(lockfile) !== stripRange(installed)) {
4273
- mismatch = {
4274
- lockfile,
4275
- installed,
4276
- note: `lockfile pins ${lockfile} but the installed tree has ${installed} \u2014 your install is out of sync with the lockfile (re-install or commit the lock)`
4277
- };
4278
- }
4279
- const served = lockfile ?? installed ?? declared;
4280
- const source = lockfile ? "lockfile" : installed ? "installed" : declared ? "declared" : "unknown";
4281
- return { served, source, lockfile, installed, declared, mismatch };
4282
- }
4283
- function driftFor(root, entry) {
4284
- const installed = installedVersion(root, entry.name);
4285
- if (!installed || entry.version === "*") return { cataloged: entry.version, installed, drift: "unknown" };
4286
- const norm = (v) => v.replace(/^[v^~>=<\s]+/, "");
4287
- const a = norm(entry.version);
4288
- const b = norm(installed);
4289
- if (a === b) return { cataloged: entry.version, installed, drift: "current" };
4290
- return { cataloged: entry.version, installed, drift: cmp(a, b) < 0 ? "behind" : "ahead" };
4291
- }
4292
- function cmp(a, b) {
4293
- const pa = a.split(".").map(Number);
4294
- const pb = b.split(".").map(Number);
4295
- for (let i = 0; i < 3; i++) {
4296
- const d = (pa[i] || 0) - (pb[i] || 0);
4297
- if (d) return d;
4298
- }
4299
- return 0;
4300
- }
4301
- function parseGitSource(source) {
4302
- if (source.startsWith("git+")) return source.slice(4);
4303
- if (/^git@[^:]+:/.test(source)) return source;
4304
- if (/\.git\/?$/.test(source)) return source;
4305
- return null;
4306
- }
4307
- async function addLibrary(source, opts) {
4308
- const { root } = opts;
4309
- let content;
4310
- let type = "local";
4311
- const gitUrl = parseGitSource(source);
4312
- if (gitUrl) {
4313
- if (!opts.allowNetwork) {
4314
- throw new Error(`refusing to clone ${source} without --online (offline by default)`);
4315
- }
4316
- content = cloneAndReadGit(gitUrl);
4317
- type = "git";
4318
- } else if (/^https?:\/\//.test(source)) {
4319
- if (!opts.allowNetwork) {
4320
- throw new Error(`refusing to fetch ${source} without --online (offline by default)`);
4321
- }
4322
- const res = await (opts.fetchImpl ?? globalThis.fetch)(source);
4323
- if (!res.ok) throw new Error(`fetch failed (${res.status}) for ${source}`);
4324
- content = await res.text();
4325
- type = source.endsWith("llms.txt") ? "llms.txt" : source.endsWith(".json") ? "openapi" : "website";
4326
- } else {
4327
- const abs = path17.resolve(root, source);
4328
- if (!fs17.existsSync(abs)) throw new Error(`source not found: ${source}`);
4329
- content = readLocal(abs);
4330
- type = "local";
4331
- }
4332
- const name = opts.name ?? inferName(source);
4333
- const id = libId(name);
4334
- const version = opts.version ?? installedVersion(root, name) ?? "*";
4335
- fs17.mkdirSync(libDir(root), { recursive: true });
4336
- const docFileAbs = path17.join(libDir(root), `${id}.md`);
4337
- fs17.writeFileSync(docFileAbs, content);
4338
- const entry = {
4339
- id,
4340
- name,
4341
- version,
4342
- source: { type, location: source },
4343
- docFile: path17.relative(root, docFileAbs).split(path17.sep).join("/"),
4344
- docHash: hashString(content),
4345
- bytes: Buffer.byteLength(content, "utf8")
4346
- };
4347
- const catalog = loadCatalog(root);
4348
- catalog.libraries[id] = entry;
4349
- saveCatalog(root, catalog);
4350
- return entry;
4351
- }
4352
- function readDoc(root, entry) {
4353
- const abs = path17.resolve(root, entry.docFile);
4354
- return fs17.existsSync(abs) ? fs17.readFileSync(abs, "utf8") : "";
4355
- }
4356
- function npmPackageDir(root, name) {
4357
- let cur = root;
4358
- for (let i = 0; i < 12; i++) {
4359
- const dir = path17.join(cur, "node_modules", name);
4360
- if (fs17.existsSync(path17.join(dir, "package.json"))) return dir;
4361
- const parent = path17.dirname(cur);
4362
- if (parent === cur) break;
4363
- cur = parent;
4364
- }
4365
- return void 0;
4366
- }
4367
- function localPackageDocs(root, name) {
4368
- const dir = npmPackageDir(root, name);
4369
- if (!dir) return void 0;
4370
- const version = resolveVersion(root, name).served;
4371
- for (const f of ["llms.txt", "README.md", "README.mdx", "README", "readme.md"]) {
4372
- const p = path17.join(dir, f);
4373
- try {
4374
- const docs = fs17.readFileSync(p, "utf8");
4375
- if (docs.trim()) {
4376
- return { docs, version, source: path17.relative(root, p).split(path17.sep).join("/") };
4377
- }
4378
- } catch {
4379
- }
4380
- }
4381
- try {
4382
- const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
4383
- if (pkg.description?.trim()) {
4384
- return {
4385
- docs: `# ${name}
4386
-
4387
- ${pkg.description}`,
4388
- version: version ?? pkg.version,
4389
- source: path17.relative(root, path17.join(dir, "package.json")).split(path17.sep).join("/")
4390
- };
4391
- }
4392
- } catch {
4393
- }
4394
- return void 0;
4395
- }
4396
- function dtsEntry(dir) {
4397
- try {
4398
- const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
4399
- const rel2 = pkg.types ?? pkg.typings;
4400
- if (typeof rel2 === "string") {
4401
- const p = path17.join(dir, rel2);
4402
- if (fs17.existsSync(p)) return p;
4403
- }
4404
- } catch {
4405
- }
4406
- for (const c of ["index.d.ts", "dist/index.d.ts", "lib/index.d.ts", "types/index.d.ts"]) {
4407
- const p = path17.join(dir, c);
4408
- if (fs17.existsSync(p)) return p;
4409
- }
4410
- return void 0;
4411
- }
4412
- function extractDtsApi(src, maxDecls = 200) {
4413
- const out = [];
4414
- let depth = 0;
4415
- let capture = null;
4416
- for (const raw of src.split("\n")) {
4417
- const line = raw.trim();
4418
- if (capture === null && depth === 0 && /^export\b/.test(line) && !/^export\s+(\{|\*|default\b|type\s+\{)/.test(line)) {
4419
- capture = [];
4420
- }
4421
- if (capture) capture.push(raw);
4422
- for (const ch of raw) {
4423
- if (ch === "{") depth++;
4424
- else if (ch === "}") depth = Math.max(0, depth - 1);
4425
- }
4426
- if (capture && depth === 0 && /[;}]\s*$/.test(line)) {
4427
- out.push(capture.join("\n").trim());
4428
- capture = null;
4429
- if (out.length >= maxDecls) break;
4430
- }
4431
- }
4432
- return out;
4433
- }
4434
- function localApiSurface(root, name) {
4435
- const dir = npmPackageDir(root, name);
4436
- if (!dir) return void 0;
4437
- const dts = dtsEntry(dir);
4438
- if (!dts) return void 0;
4439
- let src;
4440
- try {
4441
- src = fs17.readFileSync(dts, "utf8");
4442
- } catch {
4443
- return void 0;
4444
- }
4445
- const decls = extractDtsApi(src);
4446
- return decls.length ? decls.join("\n\n") : void 0;
4447
- }
4448
- function cloneAndReadGit(url) {
4449
- const dir = fs17.mkdtempSync(path17.join(os.tmpdir(), "vg-lib-git-"));
4450
- try {
4451
- execFileSync("git", ["clone", "--depth", "1", "--quiet", url, dir], {
4452
- stdio: ["ignore", "ignore", "pipe"]
4453
- });
4454
- } catch (err) {
4455
- fs17.rmSync(dir, { recursive: true, force: true });
4456
- const detail = err instanceof Error && err.message ? `: ${err.message.split("\n")[0]}` : "";
4457
- throw new Error(`git clone failed for ${url}${detail}`);
4458
- }
4459
- try {
4460
- return readGitDocs(dir);
4461
- } finally {
4462
- fs17.rmSync(dir, { recursive: true, force: true });
4463
- }
4464
- }
4465
- function readGitDocs(dir) {
4466
- const docsDir = path17.join(dir, "docs");
4467
- if (fs17.existsSync(docsDir) && fs17.statSync(docsDir).isDirectory()) return readLocal(docsDir);
4468
- const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) => path17.join(dir, f)).find((p) => fs17.existsSync(p));
4469
- if (readme) return fs17.readFileSync(readme, "utf8");
4470
- return readLocal(dir);
4471
- }
4472
- function readLocal(abs) {
4473
- const stat = fs17.statSync(abs);
4474
- if (stat.isFile()) return fs17.readFileSync(abs, "utf8");
4475
- const parts = [];
4476
- const files = fs17.readdirSync(abs, { withFileTypes: true }).filter((e) => e.isFile() && /\.(md|mdx|txt|rst)$/i.test(e.name)).map((e) => e.name).sort();
4477
- for (const f of files) parts.push(`<!-- ${f} -->
4478
- ${fs17.readFileSync(path17.join(abs, f), "utf8")}`);
4479
- return parts.join("\n\n");
4480
- }
4481
- function inferName(source) {
4482
- const cleaned = source.replace(/^git\+/, "").replace(/\.git\/?$/i, "");
4483
- const base = cleaned.replace(/\/+$/, "").split(/[/:]/).pop() ?? cleaned;
4484
- return base.replace(/\.(md|mdx|txt|rst|json)$/i, "").replace(/^llms$/, "docs");
4485
- }
4486
2886
  function findGitRoot(startDir = process.cwd()) {
4487
- let dir = path17.resolve(startDir);
2887
+ let dir = path2.resolve(startDir);
4488
2888
  for (; ; ) {
4489
- if (fs17.existsSync(path17.join(dir, ".git"))) return dir;
4490
- const parent = path17.dirname(dir);
2889
+ if (fs18.existsSync(path2.join(dir, ".git"))) return dir;
2890
+ const parent = path2.dirname(dir);
4491
2891
  if (parent === dir) return null;
4492
2892
  dir = parent;
4493
2893
  }
@@ -4498,19 +2898,19 @@ function normalizeEntry(line) {
4498
2898
  function ensureGitignored(entry, startDir = process.cwd()) {
4499
2899
  const root = findGitRoot(startDir);
4500
2900
  if (!root) return { status: "not-a-repo", entry };
4501
- const gitignorePath = path17.join(root, ".gitignore");
2901
+ const gitignorePath = path2.join(root, ".gitignore");
4502
2902
  const target = normalizeEntry(entry);
4503
2903
  let existing = "";
4504
2904
  let fileExisted = true;
4505
2905
  try {
4506
- existing = fs17.readFileSync(gitignorePath, "utf8");
2906
+ existing = fs18.readFileSync(gitignorePath, "utf8");
4507
2907
  } catch {
4508
2908
  fileExisted = false;
4509
2909
  }
4510
2910
  const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
4511
2911
  if (alreadyPresent) return { status: "present", entry, gitignorePath };
4512
2912
  const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
4513
- fs17.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
2913
+ fs18.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
4514
2914
  `, "utf8");
4515
2915
  return { status: fileExisted ? "added" : "created", entry, gitignorePath };
4516
2916
  }
@@ -4664,14 +3064,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
4664
3064
  console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
4665
3065
  }
4666
3066
  if (opts.write) {
4667
- const writePath = path17.resolve(opts.write);
3067
+ const writePath = path2.resolve(opts.write);
4668
3068
  await writeTextFile(writePath, dsn + "\n");
4669
3069
  console.log("");
4670
3070
  console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
4671
- const root = findGitRoot(path17.dirname(writePath));
4672
- const rel2 = root ? path17.relative(root, writePath).split(path17.sep).join("/") : "";
3071
+ const root = findGitRoot(path2.dirname(writePath));
3072
+ const rel2 = root ? path2.relative(root, writePath).split(path2.sep).join("/") : "";
4673
3073
  if (root && rel2 && !rel2.startsWith("..")) {
4674
- const res = ensureGitignored(rel2, path17.dirname(writePath));
3074
+ const res = ensureGitignored(rel2, path2.dirname(writePath));
4675
3075
  if (res.status === "created") {
4676
3076
  console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
4677
3077
  } else if (res.status === "added") {
@@ -4689,30 +3089,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
4689
3089
  var STORE_DIRNAME = ".vibgrate";
4690
3090
  var STORE_FILENAME = "credentials.json";
4691
3091
  function homeCredentialsPath() {
4692
- return path17.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
3092
+ return path2.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
4693
3093
  }
4694
3094
  function projectCredentialsPath(cwd = process.cwd()) {
4695
3095
  const root = findGitRoot(cwd) ?? cwd;
4696
- return path17.join(root, STORE_DIRNAME, STORE_FILENAME);
3096
+ return path2.join(root, STORE_DIRNAME, STORE_FILENAME);
4697
3097
  }
4698
3098
  function credentialsPath(opts = {}) {
4699
3099
  const override = process.env.VIBGRATE_CREDENTIALS?.trim();
4700
- if (override) return path17.resolve(override);
3100
+ if (override) return path2.resolve(override);
4701
3101
  const local = projectCredentialsPath(opts.cwd);
4702
3102
  if (opts.local) return local;
4703
- if (fs17.existsSync(local)) return local;
3103
+ if (fs18.existsSync(local)) return local;
4704
3104
  return homeCredentialsPath();
4705
3105
  }
4706
3106
  function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
4707
- const rel2 = path17.relative(repoRoot, credsFile);
4708
- if (rel2 && !rel2.startsWith("..") && !path17.isAbsolute(rel2)) {
4709
- return rel2.split(path17.sep).join("/");
3107
+ const rel2 = path2.relative(repoRoot, credsFile);
3108
+ if (rel2 && !rel2.startsWith("..") && !path2.isAbsolute(rel2)) {
3109
+ return rel2.split(path2.sep).join("/");
4710
3110
  }
4711
3111
  return `${STORE_DIRNAME}/${STORE_FILENAME}`;
4712
3112
  }
4713
3113
  function readStoredCredentials(opts = {}) {
4714
3114
  try {
4715
- const raw = fs17.readFileSync(credentialsPath(opts), "utf8");
3115
+ const raw = fs18.readFileSync(credentialsPath(opts), "utf8");
4716
3116
  const parsed = JSON.parse(raw);
4717
3117
  return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
4718
3118
  } catch {
@@ -4721,16 +3121,16 @@ function readStoredCredentials(opts = {}) {
4721
3121
  }
4722
3122
  function writeStoredCredentials(creds, opts = {}) {
4723
3123
  const file = credentialsPath(opts);
4724
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
4725
- fs17.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
3124
+ fs18.mkdirSync(path2.dirname(file), { recursive: true });
3125
+ fs18.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
4726
3126
  try {
4727
- fs17.chmodSync(file, 384);
3127
+ fs18.chmodSync(file, 384);
4728
3128
  } catch {
4729
3129
  }
4730
3130
  }
4731
3131
  function clearStoredCredentials(opts = {}) {
4732
3132
  try {
4733
- fs17.rmSync(credentialsPath(opts));
3133
+ fs18.rmSync(credentialsPath(opts));
4734
3134
  return true;
4735
3135
  } catch {
4736
3136
  return false;
@@ -5101,7 +3501,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
5101
3501
  if (opts.strict) process.exit(1);
5102
3502
  return;
5103
3503
  }
5104
- const filePath = path17.resolve(opts.file);
3504
+ const filePath = path2.resolve(opts.file);
5105
3505
  if (!await pathExists(filePath)) {
5106
3506
  console.error(chalk.red(`Scan artifact not found: ${filePath}`));
5107
3507
  console.error(chalk.dim('Run "vibgrate scan" first.'));
@@ -5164,8 +3564,8 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
5164
3564
  });
5165
3565
  function readScanArtifact(root) {
5166
3566
  try {
5167
- const file = path17.join(root, ".vibgrate", "scan_result.json");
5168
- return JSON.parse(fs17.readFileSync(file, "utf8"));
3567
+ const file = path2.join(root, ".vibgrate", "scan_result.json");
3568
+ return JSON.parse(fs18.readFileSync(file, "utf8"));
5169
3569
  } catch {
5170
3570
  return null;
5171
3571
  }
@@ -5270,7 +3670,7 @@ var DEFAULT_SELECTION_WEIGHTS = {
5270
3670
  };
5271
3671
 
5272
3672
  // src/engine/select.ts
5273
- var STOPWORDS2 = /* @__PURE__ */ new Set(["the", "a", "an", "to", "of", "in", "for", "and", "or", "is", "how", "do", "i", "use", "using", "with", "on", "my"]);
3673
+ var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "to", "of", "in", "for", "and", "or", "is", "how", "do", "i", "use", "using", "with", "on", "my"]);
5274
3674
  var USAGE_RE = /\b(usage|example|examples|quick ?start|getting started|api|guide|how to|reference|tutorial|basic)\b/i;
5275
3675
  var PREAMBLE_RE = /\b(install|installation|license|licence|contributing|contributors?|sponsors?|table of contents|toc|changelog|funding|code of conduct|security|acknowledge)\b/i;
5276
3676
  function splitSections(md) {
@@ -5317,9 +3717,9 @@ function chunkBody(body) {
5317
3717
  function stripNoise(body) {
5318
3718
  return body.split("\n").filter((l) => !/^\s*(?:!\[[^\]]*\]\([^)]*\)\s*)+$/.test(l)).join("\n").trim();
5319
3719
  }
5320
- function tokenizeQuery(q2) {
5321
- if (!q2) return [];
5322
- return [...new Set(q2.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 1 && !STOPWORDS2.has(w)))];
3720
+ function tokenizeQuery(q) {
3721
+ if (!q) return [];
3722
+ return [...new Set(q.toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 1 && !STOPWORDS.has(w)))];
5323
3723
  }
5324
3724
  function symbolsFromApi(api) {
5325
3725
  if (!api) return [];
@@ -5551,11 +3951,11 @@ async function publishPrivateLibrary(req, opts = {}) {
5551
3951
  }
5552
3952
 
5553
3953
  // src/engine/hosted-cache.ts
5554
- var CACHE_VERSION2 = "vg-hosted-docs/1";
3954
+ var CACHE_VERSION = "vg-hosted-docs/1";
5555
3955
  var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
5556
3956
  var MAX_ENTRIES = 64;
5557
- function cachePath2(root) {
5558
- return path17.join(cacheDir(root), "hosted-docs.json");
3957
+ function cachePath(root) {
3958
+ return path2.join(cacheDir(root), "hosted-docs.json");
5559
3959
  }
5560
3960
  function hostedDocsCacheKey(req, opts = {}) {
5561
3961
  const material = JSON.stringify([
@@ -5571,11 +3971,11 @@ function hostedDocsCacheKey(req, opts = {}) {
5571
3971
  }
5572
3972
  function loadFile(root) {
5573
3973
  try {
5574
- const parsed = JSON.parse(fs17.readFileSync(cachePath2(root), "utf8"));
5575
- if (parsed && parsed.version === CACHE_VERSION2 && parsed.entries && typeof parsed.entries === "object") return parsed;
3974
+ const parsed = JSON.parse(fs18.readFileSync(cachePath(root), "utf8"));
3975
+ if (parsed && parsed.version === CACHE_VERSION && parsed.entries && typeof parsed.entries === "object") return parsed;
5576
3976
  } catch {
5577
3977
  }
5578
- return { version: CACHE_VERSION2, entries: {} };
3978
+ return { version: CACHE_VERSION, entries: {} };
5579
3979
  }
5580
3980
  function saveFile(root, file) {
5581
3981
  try {
@@ -5585,8 +3985,8 @@ function saveFile(root, file) {
5585
3985
  delete file.entries[k];
5586
3986
  }
5587
3987
  }
5588
- fs17.mkdirSync(cacheDir(root), { recursive: true });
5589
- fs17.writeFileSync(cachePath2(root), JSON.stringify(file));
3988
+ fs18.mkdirSync(cacheDir(root), { recursive: true });
3989
+ fs18.writeFileSync(cachePath(root), JSON.stringify(file));
5590
3990
  } catch {
5591
3991
  }
5592
3992
  }
@@ -5711,7 +4111,7 @@ function measure(root, files) {
5711
4111
  for (const rel2 of files) {
5712
4112
  let size;
5713
4113
  try {
5714
- size = fs17.statSync(path17.join(root, rel2)).size;
4114
+ size = fs18.statSync(path2.join(root, rel2)).size;
5715
4115
  } catch {
5716
4116
  continue;
5717
4117
  }
@@ -5734,9 +4134,9 @@ function matchLines(text, needleLower) {
5734
4134
  }
5735
4135
  function readCandidate(abs, knownSize) {
5736
4136
  try {
5737
- const size = knownSize ?? fs17.statSync(abs).size;
4137
+ const size = knownSize ?? fs18.statSync(abs).size;
5738
4138
  if (size > MAX_FILE_BYTES) return null;
5739
- const text = fs17.readFileSync(abs, "utf8");
4139
+ const text = fs18.readFileSync(abs, "utf8");
5740
4140
  return text.includes(NUL) ? null : text;
5741
4141
  } catch {
5742
4142
  return null;
@@ -5747,7 +4147,7 @@ function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
5747
4147
  let total = 0;
5748
4148
  let truncated = false;
5749
4149
  for (const rel2 of files) {
5750
- const text = readCandidate(path17.join(root, rel2), sizes?.get(rel2));
4150
+ const text = readCandidate(path2.join(root, rel2), sizes?.get(rel2));
5751
4151
  if (text === null) continue;
5752
4152
  for (const h of matchLines(text, needleLower)) {
5753
4153
  total++;
@@ -5814,12 +4214,12 @@ var WORKER_SOURCE = [
5814
4214
  "parentPort.postMessage({ hits, total, truncated });"
5815
4215
  ].join("\n");
5816
4216
  function runWorker(root, files, needleLower) {
5817
- return new Promise((resolve12, reject) => {
4217
+ return new Promise((resolve11, reject) => {
5818
4218
  const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
5819
4219
  let settled = false;
5820
4220
  w.once("message", (m) => {
5821
4221
  settled = true;
5822
- resolve12(m);
4222
+ resolve11(m);
5823
4223
  });
5824
4224
  w.once("error", reject);
5825
4225
  w.once("exit", (code) => {
@@ -5888,26 +4288,26 @@ var BINARY_EXTS = /* @__PURE__ */ new Set([
5888
4288
  ".pdf"
5889
4289
  ]);
5890
4290
  function hasBinaryExt(rel2) {
5891
- return BINARY_EXTS.has(path17.extname(rel2).toLowerCase());
4291
+ return BINARY_EXTS.has(path2.extname(rel2).toLowerCase());
5892
4292
  }
5893
4293
  function toPosix2(rel2) {
5894
- return path17.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
4294
+ return path2.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
5895
4295
  }
5896
4296
  async function searchSymbols(graph, root, query, limit) {
5897
- let q2 = query.trim();
5898
- if (!q2) return { matches: [], moreAvailable: false, hint: "query is required" };
5899
- const quoted = /^(["'`])(.+)\1$/.exec(q2);
5900
- if (quoted && quoted[2].trim()) q2 = quoted[2].trim();
5901
- const isPhrase = Boolean(quoted) || /\s/.test(q2) || /[/\\]/.test(q2) && !q2.includes(":") && !/[*?]/.test(q2);
5902
- let nodes = findNodes(graph, q2).filter((n) => n.kind !== "file");
4297
+ let q = query.trim();
4298
+ if (!q) return { matches: [], moreAvailable: false, hint: "query is required" };
4299
+ const quoted = /^(["'`])(.+)\1$/.exec(q);
4300
+ if (quoted && quoted[2].trim()) q = quoted[2].trim();
4301
+ const isPhrase = Boolean(quoted) || /\s/.test(q) || /[/\\]/.test(q) && !q.includes(":") && !/[*?]/.test(q);
4302
+ let nodes = findNodes(graph, q).filter((n) => n.kind !== "file");
5903
4303
  if (nodes.length === 0) {
5904
- nodes = fileSymbolNodes(graph, q2);
4304
+ nodes = fileSymbolNodes(graph, q);
5905
4305
  }
5906
4306
  if (nodes.length === 0 && isPhrase) {
5907
- nodes = reconstructedIdentifierNodes(graph, q2);
4307
+ nodes = reconstructedIdentifierNodes(graph, q);
5908
4308
  }
5909
4309
  if (nodes.length === 0) {
5910
- const tokens = queryTokens(q2);
4310
+ const tokens = queryTokens(q);
5911
4311
  if (tokens.length >= 2) nodes = multiTokenNodes(graph, tokens);
5912
4312
  }
5913
4313
  const symbolCap = isPhrase ? Math.max(1, Math.floor(limit / 3)) : limit;
@@ -5922,10 +4322,10 @@ async function searchSymbols(graph, root, query, limit) {
5922
4322
  const textHits = [];
5923
4323
  let truncatedScan = false;
5924
4324
  let totalTextMatches;
5925
- const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q2);
4325
+ const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q);
5926
4326
  if (spare > 0 && !skipScan) {
5927
4327
  const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
5928
- const scan = await scanFiles(root, q2, spare, seen, textHits, isPhrase);
4328
+ const scan = await scanFiles(root, q, spare, seen, textHits, isPhrase);
5929
4329
  truncatedScan = scan.truncated;
5930
4330
  if (isPhrase) totalTextMatches = scan.total;
5931
4331
  }
@@ -5946,25 +4346,25 @@ async function searchSymbols(graph, root, query, limit) {
5946
4346
  }
5947
4347
  return result;
5948
4348
  }
5949
- function fileSymbolNodes(graph, q2) {
5950
- if (!/[/\\]/.test(q2) && !/\.\w+$/.test(q2)) return [];
5951
- const norm = q2.replaceAll("\\", "/").replace(/^\.\//, "");
4349
+ function fileSymbolNodes(graph, q) {
4350
+ if (!/[/\\]/.test(q) && !/\.\w+$/.test(q)) return [];
4351
+ const norm = q.replaceAll("\\", "/").replace(/^\.\//, "");
5952
4352
  const inFile = graph.nodes.filter(
5953
4353
  (n) => n.kind !== "file" && (n.file === norm || n.file.endsWith(`/${norm}`))
5954
4354
  );
5955
4355
  return [...inFile].sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName));
5956
4356
  }
5957
- function hasExactSymbolMatch(nodes, q2) {
4357
+ function hasExactSymbolMatch(nodes, q) {
5958
4358
  if (nodes.length === 0) return false;
5959
- if (/^(.*):(\d+)$/.test(q2)) return true;
5960
- const lower = q2.toLowerCase();
5961
- return nodes.some((n) => n.id === q2 || n.qualifiedName.toLowerCase() === lower || n.name.toLowerCase() === lower);
4359
+ if (/^(.*):(\d+)$/.test(q)) return true;
4360
+ const lower = q.toLowerCase();
4361
+ return nodes.some((n) => n.id === q || n.qualifiedName.toLowerCase() === lower || n.name.toLowerCase() === lower);
5962
4362
  }
5963
- function queryTokens(q2) {
5964
- return [...new Set(q2.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2))];
4363
+ function queryTokens(q) {
4364
+ return [...new Set(q.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2))];
5965
4365
  }
5966
- function reconstructedIdentifierNodes(graph, q2) {
5967
- const words = q2.trim().split(/\s+/).filter(Boolean);
4366
+ function reconstructedIdentifierNodes(graph, q) {
4367
+ const words = q.trim().split(/\s+/).filter(Boolean);
5968
4368
  if (words.length < 2) return [];
5969
4369
  const concat = findNodes(graph, words.join(""));
5970
4370
  if (concat.length) return concat.filter((n) => n.kind !== "file");
@@ -6030,13 +4430,13 @@ function walkCandidates(root) {
6030
4430
  const dir = stack.pop();
6031
4431
  let entries;
6032
4432
  try {
6033
- entries = fs17.readdirSync(dir, { withFileTypes: true });
4433
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
6034
4434
  } catch {
6035
4435
  continue;
6036
4436
  }
6037
4437
  for (const e of entries) {
6038
4438
  if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
6039
- const abs = path17.join(dir, e.name);
4439
+ const abs = path2.join(dir, e.name);
6040
4440
  if (e.isDirectory()) {
6041
4441
  stack.push(abs);
6042
4442
  continue;
@@ -6046,7 +4446,7 @@ function walkCandidates(root) {
6046
4446
  truncated = true;
6047
4447
  continue;
6048
4448
  }
6049
- files.push(toPosix2(path17.relative(root, abs)));
4449
+ files.push(toPosix2(path2.relative(root, abs)));
6050
4450
  }
6051
4451
  }
6052
4452
  files.sort();
@@ -6077,11 +4477,11 @@ function ripgrepCandidates(root, needle) {
6077
4477
  if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
6078
4478
  return { files, truncated: false };
6079
4479
  }
6080
- function parseRgFileList(stdout, sep10 = path17.sep) {
4480
+ function parseRgFileList(stdout, sep9 = path2.sep) {
6081
4481
  const seen = /* @__PURE__ */ new Set();
6082
4482
  for (const raw of stdout.split("\n")) {
6083
4483
  if (!raw) continue;
6084
- const posix3 = sep10 === "\\" ? raw.replaceAll("\\", "/") : raw;
4484
+ const posix3 = sep9 === "\\" ? raw.replaceAll("\\", "/") : raw;
6085
4485
  const rel2 = posix3.startsWith("./") ? posix3.slice(2) : posix3;
6086
4486
  if (!rel2) continue;
6087
4487
  const segs = rel2.split("/");
@@ -6114,7 +4514,7 @@ function readyEmbedder(local) {
6114
4514
  async function retrieve(graph, question, budget, ctx) {
6115
4515
  let mode = "lexical";
6116
4516
  try {
6117
- const q2 = await withTimeout(
4517
+ const q = await withTimeout(
6118
4518
  (async () => {
6119
4519
  const embedder = await readyEmbedder(ctx.local);
6120
4520
  if (!embedder) return null;
@@ -6126,7 +4526,7 @@ async function retrieve(graph, question, budget, ctx) {
6126
4526
  SEMANTIC_BUDGET_MS,
6127
4527
  "semantic path over budget"
6128
4528
  );
6129
- if (q2) return { q: q2, mode };
4529
+ if (q) return { q, mode };
6130
4530
  } catch {
6131
4531
  }
6132
4532
  return { q: queryGraph(graph, question, { budget }), mode: "lexical" };
@@ -6175,10 +4575,10 @@ var TOOLS = [
6175
4575
  const question = String(args.question ?? args.query ?? "");
6176
4576
  if (!question) return { error: "bad_request", message: "question is required" };
6177
4577
  const budget = numOr(args.budget, 1500);
6178
- const { q: q2, mode } = await retrieve(graph, question, budget, ctx);
4578
+ const { q, mode } = await retrieve(graph, question, budget, ctx);
6179
4579
  const rawScope = String(args.scope ?? "").trim();
6180
4580
  const scope = rawScope === "." || rawScope === "./" ? "" : rawScope.replace(/^\.\//, "");
6181
- const scoped = scope ? q2.matches.filter((m) => m.node.file.startsWith(scope)) : q2.matches;
4581
+ const scoped = scope ? q.matches.filter((m) => m.node.file.startsWith(scope)) : q.matches;
6182
4582
  const detailed = isDetailed(args);
6183
4583
  let topImpact = null;
6184
4584
  const top = scoped[0]?.node;
@@ -6196,7 +4596,7 @@ var TOOLS = [
6196
4596
  mode,
6197
4597
  // The fact-annotated context block is detail: concise callers navigate
6198
4598
  // by the ranked matches and fetch nodes on demand (plan P2).
6199
- ...detailed ? { context: q2.context } : {},
4599
+ ...detailed ? { context: q.context } : {},
6200
4600
  matches: shown.map((m) => ({ name: m.node.qualifiedName, kind: m.node.kind, file: m.node.file, line: m.node.span.start, score: m.score })),
6201
4601
  // A neutral availability flag, not a directive to keep querying: a
6202
4602
  // retrieval tool that coaches "narrow the question / see more" nudges the
@@ -6975,12 +5375,12 @@ function detectAssistants(root, opts = {}) {
6975
5375
  const which = opts.which ?? whichOnPath;
6976
5376
  const found = [];
6977
5377
  for (const assistant of ASSISTANTS) {
6978
- const repoHit = (assistant.markers ?? []).find((m) => fs17.existsSync(path17.join(root, m)));
5378
+ const repoHit = (assistant.markers ?? []).find((m) => fs18.existsSync(path2.join(root, m)));
6979
5379
  if (repoHit) {
6980
5380
  found.push({ assistant, via: "repo", marker: repoHit });
6981
5381
  continue;
6982
5382
  }
6983
- const homeHit = (assistant.homeMarkers ?? []).find((m) => fs17.existsSync(path17.join(home, m)));
5383
+ const homeHit = (assistant.homeMarkers ?? []).find((m) => fs18.existsSync(path2.join(home, m)));
6984
5384
  if (homeHit) {
6985
5385
  found.push({ assistant, via: "home", marker: homeHit });
6986
5386
  continue;
@@ -7012,19 +5412,19 @@ function installAssistant(a, opts) {
7012
5412
  const skipped = [];
7013
5413
  let note;
7014
5414
  if (a.skill) {
7015
- writeFileEnsured(path17.join(opts.root, a.skill), skillMarkdown(a.id));
5415
+ writeFileEnsured(path2.join(opts.root, a.skill), skillMarkdown(a.id));
7016
5416
  wrote.push(a.skill);
7017
5417
  }
7018
5418
  if (a.mcp) {
7019
5419
  const launch = opts.launch ?? detectServeLaunch();
7020
- upsertMcp(path17.join(opts.root, a.mcp.file), a.mcp, launch);
5420
+ upsertMcp(path2.join(opts.root, a.mcp.file), a.mcp, launch);
7021
5421
  wrote.push(a.mcp.file);
7022
5422
  note = launch.note;
7023
5423
  } else {
7024
5424
  skipped.push("mcp (host-specific setup)");
7025
5425
  }
7026
5426
  if (a.nudge && opts.hook !== false) {
7027
- writeNudge(path17.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
5427
+ writeNudge(path2.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
7028
5428
  wrote.push(a.nudge.file);
7029
5429
  } else if (a.nudge) {
7030
5430
  skipped.push("nudge (--no-hook)");
@@ -7034,41 +5434,41 @@ function installAssistant(a, opts) {
7034
5434
  function uninstallAssistant(a, root, purge) {
7035
5435
  const removed = [];
7036
5436
  if (a.mcp) {
7037
- const file = path17.join(root, a.mcp.file);
5437
+ const file = path2.join(root, a.mcp.file);
7038
5438
  if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
7039
5439
  }
7040
5440
  if (a.nudge) {
7041
- const file = path17.join(root, a.nudge.file);
5441
+ const file = path2.join(root, a.nudge.file);
7042
5442
  if (a.nudge.kind === "block") {
7043
5443
  if (removeBlock(file)) removed.push(a.nudge.file);
7044
- } else if (fs17.existsSync(file)) {
7045
- fs17.rmSync(file);
5444
+ } else if (fs18.existsSync(file)) {
5445
+ fs18.rmSync(file);
7046
5446
  removed.push(a.nudge.file);
7047
5447
  }
7048
5448
  }
7049
5449
  if (purge && a.skill) {
7050
- const file = path17.join(root, a.skill);
7051
- if (fs17.existsSync(file)) {
7052
- fs17.rmSync(file);
5450
+ const file = path2.join(root, a.skill);
5451
+ if (fs18.existsSync(file)) {
5452
+ fs18.rmSync(file);
7053
5453
  removed.push(a.skill);
7054
5454
  }
7055
5455
  }
7056
5456
  return removed;
7057
5457
  }
7058
5458
  function writeFileEnsured(file, content) {
7059
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
7060
- fs17.writeFileSync(file, content);
5459
+ fs18.mkdirSync(path2.dirname(file), { recursive: true });
5460
+ fs18.writeFileSync(file, content);
7061
5461
  }
7062
5462
  function writeNavigationConfig(root) {
7063
5463
  ensureVibgrateGitignore(root);
7064
- const rel2 = path17.join(".vibgrate", "mcp-navigation.json");
5464
+ const rel2 = path2.join(".vibgrate", "mcp-navigation.json");
7065
5465
  const doc = {
7066
5466
  _readme: "Deferred-loading config for agents embedding `vg serve` via the Claude API (defer_loading + tool-search). The hot navigation core stays in context; the rest load on demand \u2014 ~350-450 schema tokens/step vs ~1,881 for the full set. Hosts without defer_loading ignore this and serve the whole (already optimized) tool set. See docs/graph/VG-NAVIGATION-PROFILE.md.",
7067
5467
  hot_core: [...HOT_TOOLS],
7068
5468
  deferred: deferredToolNames(),
7069
5469
  toolset: navigationToolsetConfig()
7070
5470
  };
7071
- writeFileEnsured(path17.join(root, rel2), `${JSON.stringify(doc, null, 2)}
5471
+ writeFileEnsured(path2.join(root, rel2), `${JSON.stringify(doc, null, 2)}
7072
5472
  `);
7073
5473
  return rel2;
7074
5474
  }
@@ -7083,7 +5483,7 @@ function upsertMcp(file, target, launch) {
7083
5483
  `);
7084
5484
  }
7085
5485
  function removeMcp(file, target) {
7086
- if (!fs17.existsSync(file)) return false;
5486
+ if (!fs18.existsSync(file)) return false;
7087
5487
  const config = readJson(file);
7088
5488
  const bag = config[target.key];
7089
5489
  if (!bag || !(bag.vg !== void 0)) return false;
@@ -7102,8 +5502,8 @@ function refreshInstalledInstructions(root, smallRepo) {
7102
5502
  };
7103
5503
  for (const a of ASSISTANTS) {
7104
5504
  if (a.skill) {
7105
- const file = path17.join(root, a.skill);
7106
- if (fs17.existsSync(file)) {
5505
+ const file = path2.join(root, a.skill);
5506
+ if (fs18.existsSync(file)) {
7107
5507
  const text = readTextSafe(file);
7108
5508
  const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
7109
5509
  if (from !== null) {
@@ -7113,8 +5513,8 @@ function refreshInstalledInstructions(root, smallRepo) {
7113
5513
  }
7114
5514
  }
7115
5515
  if (a.nudge) {
7116
- const file = path17.join(root, a.nudge.file);
7117
- if (!fs17.existsSync(file)) continue;
5516
+ const file = path2.join(root, a.nudge.file);
5517
+ if (!fs18.existsSync(file)) continue;
7118
5518
  const text = readTextSafe(file);
7119
5519
  if (text === null) continue;
7120
5520
  if (a.nudge.kind === "block") {
@@ -7138,7 +5538,7 @@ function refreshInstalledInstructions(root, smallRepo) {
7138
5538
  }
7139
5539
  function readTextSafe(file) {
7140
5540
  try {
7141
- return fs17.readFileSync(file, "utf8");
5541
+ return fs18.readFileSync(file, "utf8");
7142
5542
  } catch {
7143
5543
  return null;
7144
5544
  }
@@ -7157,8 +5557,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
7157
5557
  upsertBlock(file, nudgeMarkdown(smallRepo, client));
7158
5558
  }
7159
5559
  function upsertBlock(file, block) {
7160
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
7161
- let existing = fs17.existsSync(file) ? fs17.readFileSync(file, "utf8") : "";
5560
+ fs18.mkdirSync(path2.dirname(file), { recursive: true });
5561
+ let existing = fs18.existsSync(file) ? fs18.readFileSync(file, "utf8") : "";
7162
5562
  const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
7163
5563
  if (re.test(existing)) existing = existing.replace(re, block);
7164
5564
  else existing = existing.length ? `${existing.replace(/\s*$/, "")}
@@ -7166,20 +5566,20 @@ function upsertBlock(file, block) {
7166
5566
  ${block}
7167
5567
  ` : `${block}
7168
5568
  `;
7169
- fs17.writeFileSync(file, existing);
5569
+ fs18.writeFileSync(file, existing);
7170
5570
  }
7171
5571
  function removeBlock(file) {
7172
- if (!fs17.existsSync(file)) return false;
7173
- const existing = fs17.readFileSync(file, "utf8");
5572
+ if (!fs18.existsSync(file)) return false;
5573
+ const existing = fs18.readFileSync(file, "utf8");
7174
5574
  const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
7175
5575
  if (!re.test(existing)) return false;
7176
- fs17.writeFileSync(file, existing.replace(re, "\n"));
5576
+ fs18.writeFileSync(file, existing.replace(re, "\n"));
7177
5577
  return true;
7178
5578
  }
7179
5579
  function readJson(file) {
7180
- if (!fs17.existsSync(file)) return {};
5580
+ if (!fs18.existsSync(file)) return {};
7181
5581
  try {
7182
- const parsed = JSON.parse(fs17.readFileSync(file, "utf8"));
5582
+ const parsed = JSON.parse(fs18.readFileSync(file, "utf8"));
7183
5583
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7184
5584
  return parsed;
7185
5585
  }
@@ -7199,7 +5599,7 @@ function escapeRe(s) {
7199
5599
  }
7200
5600
  var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
7201
5601
  function refreshLockPath(root) {
7202
- return path17.join(cacheDir(root), "refresh.lock");
5602
+ return path2.join(cacheDir(root), "refresh.lock");
7203
5603
  }
7204
5604
  async function refreshIfStale(root, opts = {}) {
7205
5605
  const probe = probeFreshness(root);
@@ -7230,8 +5630,8 @@ async function refreshIfStale(root, opts = {}) {
7230
5630
  const dir = vibgrateDir(root);
7231
5631
  writeArtifacts(result.graph, {
7232
5632
  root,
7233
- report: fs17.existsSync(path17.join(dir, "GRAPH_REPORT.md")),
7234
- html: fs17.existsSync(path17.join(dir, "graph.html"))
5633
+ report: fs18.existsSync(path2.join(dir, "GRAPH_REPORT.md")),
5634
+ html: fs18.existsSync(path2.join(dir, "graph.html"))
7235
5635
  });
7236
5636
  }
7237
5637
  writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
@@ -7250,583 +5650,7 @@ async function refreshIfStale(root, opts = {}) {
7250
5650
  releaseLock(lock);
7251
5651
  }
7252
5652
  }
7253
- var LEDGER = "savings.jsonl";
7254
- var PER_FILE_TOKENS = 400;
7255
- var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node", "search_symbols"]);
7256
- var CLI_TOOL_ALIASES = {
7257
- ask: "query_graph",
7258
- show: "get_node",
7259
- impact: "impact_of",
7260
- path: "find_path",
7261
- hubs: "list_hubs",
7262
- areas: "list_areas",
7263
- tree: "tree"
7264
- };
7265
- function sanitizeClient(name) {
7266
- if (typeof name !== "string") return "unknown";
7267
- const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
7268
- return cleaned || "unknown";
7269
- }
7270
- function recordCliCall(root, entry, now) {
7271
- recordSaving(
7272
- root,
7273
- {
7274
- tool: entry.tool,
7275
- source: "cli",
7276
- client: sanitizeClient(entry.client),
7277
- outcome: entry.outcome,
7278
- vgTokens: entry.vgTokens ?? 0,
7279
- baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
7280
- },
7281
- now
7282
- );
7283
- }
7284
- function savingsLedgerPath(root) {
7285
- return path17.join(cacheDir(root), LEDGER);
7286
- }
7287
- function ledgerPath(root) {
7288
- return savingsLedgerPath(root);
7289
- }
7290
- function savingsRecorded(root) {
7291
- return fs17.existsSync(ledgerPath(root));
7292
- }
7293
- function clearSavings(root) {
7294
- const file = ledgerPath(root);
7295
- const existed = fs17.existsSync(file);
7296
- fs17.rmSync(file, { force: true });
7297
- fs17.rmSync(path17.join(cacheDir(root), "stats-share.json"), { force: true });
7298
- return existed;
7299
- }
7300
- function recordSaving(root, entry, now) {
7301
- try {
7302
- fs17.mkdirSync(cacheDir(root), { recursive: true });
7303
- const line = JSON.stringify({ ts: now, ...entry });
7304
- fs17.appendFileSync(ledgerPath(root), line + "\n");
7305
- } catch {
7306
- }
7307
- }
7308
- var DEFAULT_RATE_PER_M = 3;
7309
- var DEFAULT_RATE_LABEL = "input @ $3/1M";
7310
- function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
7311
- const file = ledgerPath(root);
7312
- const cutoff = now - days * 24 * 60 * 60 * 1e3;
7313
- let queries = 0;
7314
- let vgTokens = 0;
7315
- let baselineTokens = 0;
7316
- if (fs17.existsSync(file)) {
7317
- for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
7318
- if (!line.trim()) continue;
7319
- try {
7320
- const e = JSON.parse(line);
7321
- if (e.ts < cutoff) continue;
7322
- if (!SAVINGS_TOOLS.has(e.tool)) continue;
7323
- queries++;
7324
- vgTokens += e.vgTokens;
7325
- baselineTokens += e.baselineTokens;
7326
- } catch {
7327
- }
7328
- }
7329
- }
7330
- const estCostVg = vgTokens / 1e6 * ratePerM;
7331
- const estCostBaseline = baselineTokens / 1e6 * ratePerM;
7332
- return {
7333
- enabled: savingsRecorded(root),
7334
- days,
7335
- queries,
7336
- vgTokens,
7337
- baselineTokens,
7338
- ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
7339
- estCostVg: round22(estCostVg),
7340
- estCostBaseline: round22(estCostBaseline),
7341
- saved: round22(estCostBaseline - estCostVg),
7342
- rateLabel: DEFAULT_RATE_LABEL
7343
- };
7344
- }
7345
- function round22(x) {
7346
- return Math.round(x * 100) / 100;
7347
- }
7348
- function toDimensionStats(byKey) {
7349
- return [...byKey.entries()].map(([key, r]) => {
7350
- const calls = r.complete + r.partial + r.miss;
7351
- return {
7352
- key,
7353
- calls,
7354
- complete: r.complete,
7355
- partial: r.partial,
7356
- miss: r.miss,
7357
- successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
7358
- };
7359
- }).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
7360
- }
7361
- function readUsage(root, days, now) {
7362
- const file = ledgerPath(root);
7363
- const cutoff = now - days * 24 * 60 * 60 * 1e3;
7364
- const byTool = /* @__PURE__ */ new Map();
7365
- const bySource = /* @__PURE__ */ new Map();
7366
- const byClient = /* @__PURE__ */ new Map();
7367
- const msByTool = /* @__PURE__ */ new Map();
7368
- const bump2 = (m, key, outcome) => {
7369
- const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
7370
- row[outcome]++;
7371
- m.set(key, row);
7372
- };
7373
- if (fs17.existsSync(file)) {
7374
- for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
7375
- if (!line.trim()) continue;
7376
- try {
7377
- const e = JSON.parse(line);
7378
- if (e.ts < cutoff) continue;
7379
- const outcome = e.outcome ?? "complete";
7380
- bump2(byTool, e.tool, outcome);
7381
- bump2(bySource, e.source ?? "mcp", outcome);
7382
- bump2(byClient, e.client ?? "unknown", outcome);
7383
- if (typeof e.ms === "number" && e.ms >= 0) {
7384
- const timing = msByTool.get(e.tool) ?? { sum: 0, n: 0 };
7385
- timing.sum += e.ms;
7386
- timing.n++;
7387
- msByTool.set(e.tool, timing);
7388
- }
7389
- } catch {
7390
- }
7391
- }
7392
- }
7393
- const commands = toDimensionStats(byTool).map((d) => {
7394
- const timing = msByTool.get(d.key);
7395
- return {
7396
- tool: d.key,
7397
- calls: d.calls,
7398
- complete: d.complete,
7399
- partial: d.partial,
7400
- miss: d.miss,
7401
- successPct: d.successPct,
7402
- avgMs: timing && timing.n > 0 ? Math.round(timing.sum / timing.n) : null
7403
- };
7404
- });
7405
- const totals = commands.reduce(
7406
- (t, c) => ({
7407
- calls: t.calls + c.calls,
7408
- complete: t.complete + c.complete,
7409
- partial: t.partial + c.partial,
7410
- miss: t.miss + c.miss
7411
- }),
7412
- { calls: 0, complete: 0, partial: 0, miss: 0 }
7413
- );
7414
- const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
7415
- return {
7416
- enabled: savingsRecorded(root),
7417
- days,
7418
- commands,
7419
- sources: toDimensionStats(bySource),
7420
- clients: toDimensionStats(byClient),
7421
- totals,
7422
- avgSuccessPct
7423
- };
7424
- }
7425
- var PROBE_INTERVAL_MS = 2e3;
7426
- var MAX_PROBE_INTERVAL_MS = 3e4;
7427
- var PROBE_DUTY_FACTOR = 20;
7428
- var REFRESH_BUDGET_MS = 5e3;
7429
- var FAILURE_COOLDOWN_MS = 6e4;
7430
- var GraphSource = class {
7431
- constructor(graphPath, refresh = false, tuning = {}) {
7432
- this.graphPath = graphPath;
7433
- this.refresh = refresh;
7434
- this.tuning = tuning;
7435
- this.root = path17.dirname(path17.dirname(graphPath));
7436
- }
7437
- graphPath;
7438
- refresh;
7439
- tuning;
7440
- cachedMtimeMs = -1;
7441
- cached = null;
7442
- root;
7443
- lastProbeAt = 0;
7444
- failedUntil = 0;
7445
- inflight = null;
7446
- /** Self-tuned: grows with measured probe cost so huge repos aren't penalized. */
7447
- probeIntervalMs = PROBE_INTERVAL_MS;
7448
- /** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */
7449
- async get() {
7450
- if (this.refresh) await this.maybeRefresh();
7451
- const stat = fs17.statSync(this.graphPath);
7452
- if (stat.mtimeMs !== this.cachedMtimeMs || !this.cached) {
7453
- this.cached = parseGraph(fs17.readFileSync(this.graphPath, "utf8"));
7454
- this.cachedMtimeMs = stat.mtimeMs;
7455
- }
7456
- return this.cached;
7457
- }
7458
- /**
7459
- * Debounced, single-flight refresh. Never throws — a refresh problem must
7460
- * degrade to "answer from the current map", not break the tool call.
7461
- */
7462
- async maybeRefresh() {
7463
- const now = Date.now();
7464
- if (!this.inflight) {
7465
- const interval = this.tuning.probeIntervalMs ?? this.probeIntervalMs;
7466
- if (now < this.failedUntil || now - this.lastProbeAt < interval) return;
7467
- this.lastProbeAt = now;
7468
- this.inflight = refreshIfStale(this.root).then((r) => {
7469
- if (r.status === "error") this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
7470
- if (r.status === "fresh" || r.status === "no-snapshot" || r.status === "locked") {
7471
- const cost = Date.now() - now;
7472
- this.probeIntervalMs = Math.min(
7473
- MAX_PROBE_INTERVAL_MS,
7474
- Math.max(PROBE_INTERVAL_MS, cost * PROBE_DUTY_FACTOR)
7475
- );
7476
- }
7477
- }).catch(() => {
7478
- this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
7479
- }).finally(() => {
7480
- this.inflight = null;
7481
- });
7482
- }
7483
- await Promise.race([this.inflight, sleep(this.tuning.refreshBudgetMs ?? REFRESH_BUDGET_MS)]);
7484
- }
7485
- };
7486
- function createServer(source, opts = {}) {
7487
- const { savings = false, shareStats = false, local = false, dedup = false, stats } = opts;
7488
- const record = savings || shareStats;
7489
- const root = path17.dirname(path17.dirname(source.graphPath));
7490
- const seen = /* @__PURE__ */ new Set();
7491
- const server = new Server(
7492
- { name: "vg", version: VERSION },
7493
- {
7494
- capabilities: { tools: {} },
7495
- // Routing guidance once at the server level (hosts that surface
7496
- // `instructions` get it at zero per-step schema cost): the flashlight
7497
- // vs the map.
7498
- instructions: 'vg is a code map. Use search_symbols to find a known name or literal string fast \u2014 a multi-word/quoted phrase runs a complete literal sweep and reports totalTextMatches, so reach for it instead of grep even for plain-string "find every occurrence" lookups. Use orient/query_graph for meaning: symptoms, relationships, and what-breaks-if. Responses are concise by default; pass response_format:"detailed" only when a node proves load-bearing. Navigate as little as possible: one good search/query usually locates the code. As soon as you have the file and line, read that file and make the edit \u2014 do not call further graph tools unless the edit fails or the match was wrong. For how-do-I-use-this-library questions call resolve_library once, then library_docs with the returned targetId and a focused query: the docs are official and matched to the version THIS project has installed (drift-annotated) \u2014 prefer them over web search or training-data recall when they conflict. Skip them for language built-ins or APIs already shown in context. If two library_docs calls have not surfaced the section you need, read the package source under node_modules instead of searching again.'
7499
- }
7500
- );
7501
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
7502
- tools: TOOLS.map((t) => ({
7503
- name: t.name,
7504
- description: t.description,
7505
- inputSchema: t.inputSchema,
7506
- annotations: { readOnlyHint: true, openWorldHint: false }
7507
- }))
7508
- }));
7509
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
7510
- const tool = TOOLS.find((t) => t.name === request.params.name);
7511
- if (!tool) {
7512
- return errorResult(`unknown tool "${request.params.name}"`);
7513
- }
7514
- let graph;
7515
- try {
7516
- graph = await source.get();
7517
- } catch {
7518
- return errorResult(
7519
- "no code map found. Run `vg` in the project to build .vibgrate/graph.json, then retry."
7520
- );
7521
- }
7522
- const startedAt = Date.now();
7523
- try {
7524
- const args = request.params.arguments ?? {};
7525
- const result = await tool.handler(graph, args, { root, local, dedup, seen });
7526
- const ms = Date.now() - startedAt;
7527
- stats?.record({ ...measureCall(tool.name, result), client: sanitizeClient(detectClient(server)), ms });
7528
- if (record) recordUsage(root, tool.name, result, detectClient(server), ms);
7529
- return renderToolResult(result);
7530
- } catch (err) {
7531
- stats?.record({
7532
- tool: tool.name,
7533
- client: sanitizeClient(detectClient(server)),
7534
- outcome: "miss",
7535
- ms: Date.now() - startedAt,
7536
- vgTokens: 0,
7537
- baselineTokens: 0
7538
- });
7539
- return errorResult(`tool "${tool.name}" failed: ${err.message}`);
7540
- }
7541
- });
7542
- return server;
7543
- }
7544
- async function serveStdio(graphPath, opts = {}) {
7545
- const source = new GraphSource(graphPath, opts.refresh !== false);
7546
- const server = createServer(source, opts);
7547
- warmEmbedderInBackground(opts.local);
7548
- await server.connect(new StdioServerTransport());
7549
- }
7550
- function sleep(ms) {
7551
- return new Promise((resolve12) => {
7552
- const timer = setTimeout(resolve12, ms);
7553
- timer.unref?.();
7554
- });
7555
- }
7556
- function recordUsage(root, tool, result, client, ms) {
7557
- recordSaving(
7558
- root,
7559
- { ...measureCall(tool, result), source: "mcp", client: sanitizeClient(client), ...ms !== void 0 ? { ms } : {} },
7560
- Date.now()
7561
- );
7562
- }
7563
- function measureCall(tool, result) {
7564
- const outcome = classifyOutcome(result);
7565
- let vgTokens = 0;
7566
- let baselineTokens = 0;
7567
- if (SAVINGS_TOOLS.has(tool) && result && typeof result === "object") {
7568
- vgTokens = countTokens(renderedText(renderToolResult(result)));
7569
- baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
7570
- }
7571
- return { tool, outcome, vgTokens, baselineTokens };
7572
- }
7573
- function detectClient(server) {
7574
- try {
7575
- const info = server.getClientVersion?.();
7576
- return typeof info?.name === "string" ? info.name : void 0;
7577
- } catch {
7578
- return void 0;
7579
- }
7580
- }
7581
- function classifyOutcome(result) {
7582
- if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
7583
- if (!result || typeof result !== "object") return "miss";
7584
- const r = result;
7585
- if (typeof r.error === "string" && r.error) return "miss";
7586
- if (r.connected === false) return "miss";
7587
- if (Array.isArray(r.matches) && r.matches.length === 0) return "miss";
7588
- return isPartial(r) ? "partial" : "complete";
7589
- }
7590
- function isPartial(r) {
7591
- if (r.moreAvailable === true) return true;
7592
- if (r._truncated && typeof r._truncated === "object") return true;
7593
- for (const [key, value] of Object.entries(r)) {
7594
- if (typeof value !== "number" || !key.endsWith("Total")) continue;
7595
- const base = key.slice(0, -"Total".length);
7596
- const shown = Array.isArray(r[base]) ? r[base].length : 0;
7597
- if (value > shown) return true;
7598
- }
7599
- return false;
7600
- }
7601
- function renderedText(rendered) {
7602
- const block = rendered.content?.find((b) => b.type === "text");
7603
- return block && "text" in block && typeof block.text === "string" ? block.text : "";
7604
- }
7605
- function referencedFiles(result) {
7606
- const r = result;
7607
- const files = /* @__PURE__ */ new Set();
7608
- if (typeof r.file === "string" && r.file) files.add(r.file);
7609
- for (const m of asArray(r.matches)) {
7610
- const f = m.file;
7611
- if (typeof f === "string" && f) files.add(f);
7612
- }
7613
- for (const name of [...asArray(r.calls), ...asArray(r.calledBy)]) {
7614
- if (typeof name !== "string") continue;
7615
- const i = name.indexOf(":");
7616
- if (i > 0) files.add(name.slice(0, i));
7617
- }
7618
- return files;
7619
- }
7620
- function asArray(v) {
7621
- return Array.isArray(v) ? v : [];
7622
- }
7623
- function errorResult(message) {
7624
- return { content: [{ type: "text", text: message }], isError: true };
7625
- }
7626
-
7627
- // src/engine/export.ts
7628
- function formatForExt(ext) {
7629
- switch (ext.toLowerCase()) {
7630
- case ".json":
7631
- return "json";
7632
- case ".ndjson":
7633
- return "ndjson";
7634
- case ".graphml":
7635
- return "graphml";
7636
- case ".dot":
7637
- case ".gv":
7638
- return "dot";
7639
- case ".cypher":
7640
- return "cypher";
7641
- case ".sql":
7642
- return "sql";
7643
- case ".md":
7644
- return "md";
7645
- case ".html":
7646
- return "html";
7647
- default:
7648
- return null;
7649
- }
7650
- }
7651
- function exportGraph(format, ctx) {
7652
- switch (format) {
7653
- case "json":
7654
- return serializeGraph(ctx.graph);
7655
- case "md":
7656
- return renderReport(ctx.graph);
7657
- case "html":
7658
- return renderHtml(ctx.graph);
7659
- case "ndjson":
7660
- return ndjson(ctx.graph);
7661
- case "graphml":
7662
- return graphml(ctx.graph);
7663
- case "dot":
7664
- return dot(ctx.graph);
7665
- case "cypher":
7666
- return cypher(ctx.graph);
7667
- case "sql":
7668
- return sql(ctx);
7669
- case "cyclonedx":
7670
- return cyclonedx(ctx);
7671
- case "spdx":
7672
- return spdx(ctx);
7673
- }
7674
- }
7675
- function ndjson(graph) {
7676
- const rows = graph.facts && graph.facts.length ? graph.facts : graph.nodes;
7677
- return rows.map((r) => JSON.stringify(r)).join("\n") + "\n";
7678
- }
7679
- function esc2(s) {
7680
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7681
- }
7682
- function graphml(graph) {
7683
- const lines = [];
7684
- lines.push('<?xml version="1.0" encoding="UTF-8"?>');
7685
- lines.push('<graphml xmlns="http://graphml.graphdrawing.org/xmlns">');
7686
- lines.push(' <key id="name" for="node" attr.name="name" attr.type="string"/>');
7687
- lines.push(' <key id="kind" for="node" attr.name="kind" attr.type="string"/>');
7688
- lines.push(' <key id="file" for="node" attr.name="file" attr.type="string"/>');
7689
- lines.push(' <key id="ekind" for="edge" attr.name="kind" attr.type="string"/>');
7690
- lines.push(' <graph edgedefault="directed">');
7691
- for (const n of graph.nodes) {
7692
- lines.push(` <node id="${esc2(n.id)}"><data key="name">${esc2(n.qualifiedName)}</data><data key="kind">${esc2(n.kind)}</data><data key="file">${esc2(n.file)}</data></node>`);
7693
- }
7694
- for (const e of graph.edges) {
7695
- lines.push(` <edge source="${esc2(e.src)}" target="${esc2(e.dst)}"><data key="ekind">${esc2(e.kind)}</data></edge>`);
7696
- }
7697
- lines.push(" </graph>");
7698
- lines.push("</graphml>");
7699
- return lines.join("\n") + "\n";
7700
- }
7701
- function dot(graph) {
7702
- const lines = ["digraph vg {"];
7703
- for (const n of graph.nodes) lines.push(` "${n.id}" [label="${dotEsc(n.qualifiedName)}"];`);
7704
- for (const e of graph.edges) lines.push(` "${e.src}" -> "${e.dst}" [label="${e.kind}"];`);
7705
- lines.push("}");
7706
- return lines.join("\n") + "\n";
7707
- }
7708
- function dotEsc(s) {
7709
- return s.replace(/"/g, '\\"');
7710
- }
7711
- function cypher(graph) {
7712
- const lines = [];
7713
- for (const n of graph.nodes) {
7714
- lines.push(
7715
- `CREATE (:\`${cypherLabel(n.kind)}\` {id:${q(n.id)}, name:${q(n.qualifiedName)}, file:${q(n.file)}});`
7716
- );
7717
- }
7718
- for (const e of graph.edges) {
7719
- lines.push(
7720
- `MATCH (a {id:${q(e.src)}}),(b {id:${q(e.dst)}}) CREATE (a)-[:\`${cypherLabel(e.kind)}\`]->(b);`
7721
- );
7722
- }
7723
- return lines.join("\n") + "\n";
7724
- }
7725
- function cypherLabel(s) {
7726
- return s.replace(/[^A-Za-z0-9_]/g, "_");
7727
- }
7728
- function q(s) {
7729
- return JSON.stringify(s);
7730
- }
7731
- function sql(ctx) {
7732
- const graph = ctx.graph;
7733
- const lines = [];
7734
- lines.push("BEGIN;");
7735
- lines.push("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);");
7736
- const meta = [
7737
- ["schemaVersion", graph.schemaVersion],
7738
- ["generatedAt", ctx.generatedAt],
7739
- ["corpusHash", graph.provenance.corpusHash],
7740
- ["tool", graph.provenance.tool],
7741
- ["toolVersion", graph.provenance.version],
7742
- ["resolver", graph.provenance.resolver.join(",")],
7743
- ["fingerprint", graph.provenance.toolchain?.fingerprint ?? null]
7744
- ];
7745
- for (const [k, v] of meta) lines.push(`INSERT INTO meta VALUES (${sqlStr(k)}, ${sqlStr(v)});`);
7746
- lines.push(
7747
- "CREATE TABLE IF NOT EXISTS nodes (id TEXT PRIMARY KEY, kind TEXT, name TEXT, qualified_name TEXT, file TEXT, span_start INTEGER, span_end INTEGER, lang TEXT, visibility TEXT, signature TEXT, importance REAL, area INTEGER, is_hub INTEGER, tested INTEGER, coverage REAL);"
7748
- );
7749
- for (const n of graph.nodes) {
7750
- lines.push(
7751
- `INSERT INTO nodes VALUES (${sqlStr(n.id)}, ${sqlStr(n.kind)}, ${sqlStr(n.name)}, ${sqlStr(n.qualifiedName)}, ${sqlStr(n.file)}, ${sqlNum(n.span.start)}, ${sqlNum(n.span.end)}, ${sqlStr(n.lang)}, ${sqlStr(n.visibility)}, ${sqlStr(n.signature)}, ${sqlNum(n.importance)}, ${sqlNum(n.area)}, ${sqlBool(n.isHub)}, ${sqlBool(n.tested)}, ${sqlNum(n.coverage)});`
7752
- );
7753
- }
7754
- lines.push(
7755
- 'CREATE TABLE IF NOT EXISTS edges (id TEXT PRIMARY KEY, kind TEXT, src TEXT, dst TEXT, resolution TEXT, confidence REAL, epistemic TEXT, surprise REAL, "count" INTEGER);'
7756
- );
7757
- for (const e of graph.edges) {
7758
- lines.push(
7759
- `INSERT INTO edges VALUES (${sqlStr(e.id)}, ${sqlStr(e.kind)}, ${sqlStr(e.src)}, ${sqlStr(e.dst)}, ${sqlStr(e.resolution)}, ${sqlNum(e.confidence)}, ${sqlStr(e.epistemic)}, ${sqlNum(e.surprise)}, ${sqlNum(e.count)});`
7760
- );
7761
- }
7762
- lines.push("CREATE TABLE IF NOT EXISTS areas (id INTEGER PRIMARY KEY, label TEXT, size INTEGER, cohesion REAL, external_edges INTEGER);");
7763
- lines.push("CREATE TABLE IF NOT EXISTS area_members (area_id INTEGER, node_id TEXT);");
7764
- for (const a of graph.areas) {
7765
- lines.push(`INSERT INTO areas VALUES (${sqlNum(a.id)}, ${sqlStr(a.label)}, ${sqlNum(a.size)}, ${sqlNum(a.cohesion)}, ${sqlNum(a.externalEdges)});`);
7766
- for (const m of a.members) lines.push(`INSERT INTO area_members VALUES (${sqlNum(a.id)}, ${sqlStr(m)});`);
7767
- }
7768
- if (graph.facts && graph.facts.length) {
7769
- lines.push("CREATE TABLE IF NOT EXISTS facts (id TEXT PRIMARY KEY, kind TEXT, predicate_json TEXT, derived_by TEXT, confidence TEXT);");
7770
- lines.push("CREATE TABLE IF NOT EXISTS fact_subjects (fact_id TEXT, node_id TEXT);");
7771
- lines.push("CREATE TABLE IF NOT EXISTS fact_evidence (fact_id TEXT, file TEXT, span_start INTEGER, span_end INTEGER);");
7772
- for (const f of graph.facts) {
7773
- lines.push(`INSERT INTO facts VALUES (${sqlStr(f.id)}, ${sqlStr(f.kind)}, ${sqlStr(JSON.stringify(f.predicate))}, ${sqlStr(f.derivedBy)}, ${sqlStr(f.confidence)});`);
7774
- for (const s of f.subjectIds) lines.push(`INSERT INTO fact_subjects VALUES (${sqlStr(f.id)}, ${sqlStr(s)});`);
7775
- for (const ev of f.evidence) lines.push(`INSERT INTO fact_evidence VALUES (${sqlStr(f.id)}, ${sqlStr(ev.file)}, ${sqlNum(ev.span.start)}, ${sqlNum(ev.span.end)});`);
7776
- }
7777
- }
7778
- lines.push("COMMIT;");
7779
- return lines.join("\n") + "\n";
7780
- }
7781
- function sqlStr(v) {
7782
- if (v == null) return "NULL";
7783
- return `'${v.replace(/'/g, "''")}'`;
7784
- }
7785
- function sqlNum(v) {
7786
- return v == null || !Number.isFinite(v) ? "NULL" : String(v);
7787
- }
7788
- function sqlBool(v) {
7789
- return v == null ? "NULL" : v ? "1" : "0";
7790
- }
7791
- function cyclonedx(ctx) {
7792
- const components = [];
7793
- for (const d of ctx.deps ?? []) {
7794
- components.push({
7795
- type: "library",
7796
- name: d.name,
7797
- version: d.installed ?? d.declared,
7798
- purl: d.ecosystem === "npm" ? `pkg:npm/${d.name}@${d.installed ?? ""}` : void 0
7799
- });
7800
- }
7801
- for (const m of ctx.models ?? []) {
7802
- components.push({ type: "machine-learning-model", name: m.name, properties: [{ name: "vg:runtime", value: m.runtime }] });
7803
- }
7804
- const bom = {
7805
- bomFormat: "CycloneDX",
7806
- specVersion: "1.6",
7807
- metadata: { timestamp: ctx.generatedAt, tools: [{ name: "vg", version: ctx.graph.provenance.version }] },
7808
- components
7809
- };
7810
- return JSON.stringify(bom, null, 2) + "\n";
7811
- }
7812
- function spdx(ctx) {
7813
- const packages = (ctx.deps ?? []).map((d) => ({
7814
- SPDXID: `SPDXRef-Package-${cypherLabel(d.name)}`,
7815
- name: d.name,
7816
- versionInfo: d.installed ?? d.declared,
7817
- downloadLocation: "NOASSERTION"
7818
- }));
7819
- const doc = {
7820
- spdxVersion: "SPDX-2.3",
7821
- dataLicense: "CC0-1.0",
7822
- SPDXID: "SPDXRef-DOCUMENT",
7823
- name: "vg-sbom",
7824
- creationInfo: { created: ctx.generatedAt, creators: [`Tool: vg-${ctx.graph.provenance.version}`] },
7825
- packages
7826
- };
7827
- return JSON.stringify(doc, null, 2) + "\n";
7828
- }
7829
5653
 
7830
- export { ASSISTANTS, CLI_TOOL_ALIASES, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SAVINGS_TOOLS, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, SMALL_REPO_FILES, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearSavings, clearStoredCredentials, cosine, countPending, countTokens, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectAssistants, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, epistemicBreakdown, exportGraph, fetchHostedDocsCached, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, homeCredentialsPath, hostedBase, identifierParts, impactOf, indexFor, ingestHostForRegionId, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, projectCredentialsPath, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, readStoredCredentials, readUsage, recordCliCall, recordSaving, refreshIfStale, refreshInstalledInstructions, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, sanitizeClient, saveCatalog, savingsLedgerPath, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
7831
- //# sourceMappingURL=chunk-3DPZQS2Q.js.map
7832
- //# sourceMappingURL=chunk-3DPZQS2Q.js.map
5654
+ export { ASSISTANTS, CliError, ExitCode, FREE_PACK, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, SMALL_REPO_FILES, TOOLS, UsageError, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, clearStoredCredentials, countTokens, coveringTests, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectAssistants, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, dsnCommand, ensureGitignored, epistemicBreakdown, fetchHostedDocsCached, findGitRoot, gitignoreEntryForCredentials, groundGraph, hasDrift, homeCredentialsPath, hostedBase, ingestHostForRegionId, installAssistant, isTestFile, loadCoverage, loadGraph, loadSnapshot, parseDsn, parseJsonc, probeFreshness, projectCredentialsPath, publishPrivateLibrary, pushCommand, readScanArtifact, readStoredCredentials, refreshIfStale, refreshInstalledInstructions, relativeResolver, renderHtml, renderReport, renderToolResult, resolveCliInvocation, resolveDsn, resolveIngestHost, resolveLimits, scipEdges, selectForBudget, shortestPath, symbolsFromApi, testsToRun, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, warmEmbedderInBackground, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
5655
+ //# sourceMappingURL=chunk-VXT6LUGP.js.map
5656
+ //# sourceMappingURL=chunk-VXT6LUGP.js.map