@vibgrate/cli 2026.721.3 → 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 (65) 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-37G4TINM.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-5PLKADD6.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-JLRF5TFA.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-IKJBELUV.js → chunk-PY3DNX5H.js} +4 -29
  32. package/dist/chunk-PY3DNX5H.js.map +1 -0
  33. package/dist/{chunk-ELUFCQDR.js → chunk-VXT6LUGP.js} +240 -2519
  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 +3 -1
  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.js +2 -1
  51. package/dist/parse-worker.js.map +1 -1
  52. package/dist/session-POJUDFPH.js +6 -0
  53. package/dist/session-POJUDFPH.js.map +1 -0
  54. package/dist/session-store-W5RZZHA3.js +3 -0
  55. package/dist/session-store-W5RZZHA3.js.map +1 -0
  56. package/dist/stream-json-Q2JGWPHB.js +65 -0
  57. package/dist/stream-json-Q2JGWPHB.js.map +1 -0
  58. package/dist/ui-HOVOPWMX.js +4 -0
  59. package/dist/ui-HOVOPWMX.js.map +1 -0
  60. package/package.json +1 -1
  61. package/dist/baseline-37G4TINM.js +0 -6
  62. package/dist/chunk-5PLKADD6.js.map +0 -1
  63. package/dist/chunk-ELUFCQDR.js.map +0 -1
  64. package/dist/chunk-IKJBELUV.js.map +0 -1
  65. package/dist/chunk-JLRF5TFA.js.map +0 -1
@@ -1,8 +1,12 @@
1
- import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-IKJBELUV.js';
2
- import { writeTextFile, pathExists, readJsonFile, loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-JLRF5TFA.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 },
@@ -1663,8 +1637,8 @@ function resolve4(parses, resolver) {
1663
1637
  for (const imp of p.imports) {
1664
1638
  namespaces.add(imp.source);
1665
1639
  if (IMPORT_NAMES_A_TYPE.has(p.lang)) {
1666
- const sep10 = Math.max(imp.source.lastIndexOf("."), imp.source.lastIndexOf("\\"));
1667
- if (sep10 > 0) namespaces.add(imp.source.slice(0, sep10));
1640
+ const sep9 = Math.max(imp.source.lastIndexOf("."), imp.source.lastIndexOf("\\"));
1641
+ if (sep9 > 0) namespaces.add(imp.source.slice(0, sep9));
1668
1642
  }
1669
1643
  }
1670
1644
  for (const ns of namespaces) for (const rel2 of filesByNamespace.get(ns) ?? []) if (rel2 !== p.rel) reach.add(rel2);
@@ -2147,50 +2121,7 @@ function enclosing3(nodes, line) {
2147
2121
  return best;
2148
2122
  }
2149
2123
  function normalize2(p) {
2150
- return path17.resolve(p).split(path17.sep).join("/");
2151
- }
2152
- var CACHE_VERSION = "vg-parse-cache/3";
2153
- function cacheDir(root) {
2154
- return path17.join(root, ".vibgrate", "cache");
2155
- }
2156
- function cachePath(root) {
2157
- return path17.join(cacheDir(root), "parse-cache.json");
2158
- }
2159
- function loadCache(root, opts) {
2160
- const file = cachePath(root);
2161
- let data = {
2162
- version: CACHE_VERSION,
2163
- toolVersion: opts.toolVersion,
2164
- grammars: opts.grammars,
2165
- entries: {}
2166
- };
2167
- if (!opts.disabled && fs17.existsSync(file)) {
2168
- try {
2169
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2170
- if (loaded.version === CACHE_VERSION && loaded.toolVersion === opts.toolVersion && loaded.grammars === opts.grammars && loaded.entries) {
2171
- data = loaded;
2172
- }
2173
- } catch {
2174
- }
2175
- }
2176
- return {
2177
- get(rel2, hash) {
2178
- const entry = data.entries[rel2];
2179
- return entry && entry.hash === hash ? entry.parse : void 0;
2180
- },
2181
- set(rel2, parse) {
2182
- data.entries[rel2] = { hash: parse.hash, parse };
2183
- },
2184
- prune(currentRels) {
2185
- for (const rel2 of Object.keys(data.entries)) {
2186
- if (!currentRels.has(rel2)) delete data.entries[rel2];
2187
- }
2188
- },
2189
- save() {
2190
- fs17.mkdirSync(cacheDir(root), { recursive: true });
2191
- fs17.writeFileSync(file, stableStringify(data, 0));
2192
- }
2193
- };
2124
+ return path2.resolve(p).split(path2.sep).join("/");
2194
2125
  }
2195
2126
 
2196
2127
  // src/engine/epistemic.ts
@@ -2220,7 +2151,7 @@ function epistemicBreakdown(edges) {
2220
2151
  // src/engine/build.ts
2221
2152
  async function buildGraph(options) {
2222
2153
  const start = nowMs();
2223
- const root = path17.resolve(options.root);
2154
+ const root = path2.resolve(options.root);
2224
2155
  const files = discover({
2225
2156
  root,
2226
2157
  only: options.only,
@@ -2248,7 +2179,7 @@ async function buildGraph(options) {
2248
2179
  for (const file of files) {
2249
2180
  let stat;
2250
2181
  try {
2251
- stat = fs17.statSync(file.abs);
2182
+ stat = fs18.statSync(file.abs);
2252
2183
  } catch {
2253
2184
  continue;
2254
2185
  }
@@ -2266,7 +2197,7 @@ async function buildGraph(options) {
2266
2197
  }
2267
2198
  let hash = "";
2268
2199
  try {
2269
- hash = hashBytes(fs17.readFileSync(file.abs));
2200
+ hash = hashBytes(fs18.readFileSync(file.abs));
2270
2201
  } catch {
2271
2202
  continue;
2272
2203
  }
@@ -2358,7 +2289,7 @@ async function buildGraph(options) {
2358
2289
  toolchain
2359
2290
  },
2360
2291
  meta: {
2361
- root: path17.basename(root) === "" ? "." : ".",
2292
+ root: path2.basename(root) === "" ? "." : ".",
2362
2293
  languages,
2363
2294
  counts: {
2364
2295
  nodes: analysis.nodes.length,
@@ -2404,14 +2335,14 @@ function toRepoRel(p) {
2404
2335
  function loadScipIndex(root, explicit) {
2405
2336
  const candidates = [
2406
2337
  explicit,
2407
- path17.join(root, "index.scip"),
2408
- path17.join(root, ".vibgrate", "index.scip")
2338
+ path2.join(root, "index.scip"),
2339
+ path2.join(root, ".vibgrate", "index.scip")
2409
2340
  ].filter((p) => Boolean(p));
2410
2341
  for (const file of candidates) {
2411
- const abs = path17.isAbsolute(file) ? file : path17.resolve(root, file);
2412
- if (!fs17.existsSync(abs)) continue;
2342
+ const abs = path2.isAbsolute(file) ? file : path2.resolve(root, file);
2343
+ if (!fs18.existsSync(abs)) continue;
2413
2344
  try {
2414
- const index = decodeScipIndex(new Uint8Array(fs17.readFileSync(abs)));
2345
+ const index = decodeScipIndex(new Uint8Array(fs18.readFileSync(abs)));
2415
2346
  if (index.documents.length) {
2416
2347
  return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
2417
2348
  }
@@ -2572,7 +2503,7 @@ function esc(s) {
2572
2503
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2573
2504
  }
2574
2505
  function vibgrateDir(root) {
2575
- return path17.join(root, ".vibgrate");
2506
+ return path2.join(root, ".vibgrate");
2576
2507
  }
2577
2508
  var DEFAULT_GITIGNORE = [
2578
2509
  "# Created once by vg \u2014 local graph artifacts stay out of git by default.",
@@ -2588,51 +2519,51 @@ var DEFAULT_GITIGNORE = [
2588
2519
  "mcp-navigation.json"
2589
2520
  ];
2590
2521
  function ensureVibgrateGitignore(root) {
2591
- const file = path17.join(vibgrateDir(root), ".gitignore");
2592
- if (fs17.existsSync(file)) return;
2593
- fs17.mkdirSync(vibgrateDir(root), { recursive: true });
2594
- 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")}
2595
2526
  `);
2596
2527
  }
2597
2528
  function defaultGraphPath(root) {
2598
- return path17.join(vibgrateDir(root), "graph.json");
2529
+ return path2.join(vibgrateDir(root), "graph.json");
2599
2530
  }
2600
2531
  function writeArtifacts(graph, options) {
2601
2532
  const dir = vibgrateDir(options.root);
2602
- fs17.mkdirSync(dir, { recursive: true });
2533
+ fs18.mkdirSync(dir, { recursive: true });
2603
2534
  ensureVibgrateGitignore(options.root);
2604
2535
  const graphPath = options.graphPath ?? defaultGraphPath(options.root);
2605
- fs17.mkdirSync(path17.dirname(graphPath), { recursive: true });
2536
+ fs18.mkdirSync(path2.dirname(graphPath), { recursive: true });
2606
2537
  const tmp = `${graphPath}.${process.pid}.tmp`;
2607
2538
  try {
2608
- fs17.writeFileSync(tmp, serializeGraph(graph));
2609
- fs17.renameSync(tmp, graphPath);
2539
+ fs18.writeFileSync(tmp, serializeGraph(graph));
2540
+ fs18.renameSync(tmp, graphPath);
2610
2541
  } catch (err) {
2611
- fs17.rmSync(tmp, { force: true });
2542
+ fs18.rmSync(tmp, { force: true });
2612
2543
  throw err;
2613
2544
  }
2614
2545
  const written = { graphPath };
2615
2546
  if (options.report !== false) {
2616
- const reportPath = path17.join(dir, "GRAPH_REPORT.md");
2617
- fs17.writeFileSync(reportPath, renderReport(graph));
2547
+ const reportPath = path2.join(dir, "GRAPH_REPORT.md");
2548
+ fs18.writeFileSync(reportPath, renderReport(graph));
2618
2549
  written.reportPath = reportPath;
2619
2550
  }
2620
2551
  if (options.html !== false) {
2621
- const htmlPath = path17.join(dir, "graph.html");
2622
- fs17.writeFileSync(htmlPath, renderHtml(graph));
2552
+ const htmlPath = path2.join(dir, "graph.html");
2553
+ fs18.writeFileSync(htmlPath, renderHtml(graph));
2623
2554
  written.htmlPath = htmlPath;
2624
2555
  }
2625
2556
  if (graph.facts && graph.facts.length) {
2626
- const factsPath = path17.join(dir, "facts.jsonl");
2627
- 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");
2628
2559
  written.factsPath = factsPath;
2629
2560
  }
2630
2561
  return written;
2631
2562
  }
2632
2563
  function loadGraph(root, graphPath) {
2633
2564
  const file = graphPath ?? defaultGraphPath(root);
2634
- if (!fs17.existsSync(file)) return null;
2635
- return parseGraph(fs17.readFileSync(file, "utf8"));
2565
+ if (!fs18.existsSync(file)) return null;
2566
+ return parseGraph(fs18.readFileSync(file, "utf8"));
2636
2567
  }
2637
2568
 
2638
2569
  // src/engine/verify.ts
@@ -2698,361 +2629,23 @@ var CliError = class extends Error {
2698
2629
  function usageError(message) {
2699
2630
  return new CliError(message, ExitCode.USAGE_ERROR);
2700
2631
  }
2701
- var DEFAULT_LOCK_STALE_MS = 15 * 60 * 1e3;
2702
- function isProcessAlive(pid) {
2703
- try {
2704
- process.kill(pid, 0);
2705
- return true;
2706
- } catch (e) {
2707
- return e.code === "EPERM";
2708
- }
2709
- }
2710
- function lockIsStale(file, staleMs) {
2711
- try {
2712
- const { pid, at } = JSON.parse(fs17.readFileSync(file, "utf8"));
2713
- if (typeof at === "number" && Date.now() - at > staleMs) return true;
2714
- if (typeof pid === "number" && !isProcessAlive(pid)) return true;
2715
- return false;
2716
- } catch {
2717
- return true;
2718
- }
2719
- }
2720
- function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
2721
- const write = () => {
2722
- try {
2723
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
2724
- const fd = fs17.openSync(file, "wx");
2725
- fs17.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
2726
- fs17.closeSync(fd);
2727
- return true;
2728
- } catch {
2729
- return false;
2730
- }
2731
- };
2732
- if (write()) return true;
2733
- if (lockIsStale(file, staleMs)) {
2734
- try {
2735
- fs17.rmSync(file, { force: true });
2736
- } catch {
2737
- }
2738
- return write();
2739
- }
2740
- return false;
2741
- }
2742
- function releaseLock(file) {
2743
- try {
2744
- fs17.rmSync(file, { force: true });
2745
- } catch {
2746
- }
2747
- }
2748
-
2749
- // src/engine/embeddings.ts
2750
- function resolveEmbedModel(model) {
2751
- return model ?? "bge-small-en-v1.5";
2752
- }
2753
- function modelCacheDir() {
2754
- const base = process.env.XDG_CACHE_HOME || path17.join(os.homedir(), ".cache");
2755
- return path17.join(base, "vibgrate", "models");
2756
- }
2757
- function modelReadyMarker(modelId) {
2758
- return path17.join(modelCacheDir(), `.ready-${safe(modelId)}`);
2759
- }
2760
- function isModelReady(modelId = resolveEmbedModel()) {
2761
- return fs17.existsSync(modelReadyMarker(modelId));
2762
- }
2763
- function unavailableMessage(reason) {
2764
- switch (reason) {
2765
- case "no-permission":
2766
- 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.)`;
2767
- case "download-failed":
2768
- 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.`;
2769
- case "not-installed":
2770
- return `the optional semantic backend isn't available here \u2014 using fast lexical search.`;
2771
- case "init-failed":
2772
- default:
2773
- return `the semantic model couldn't load \u2014 using fast lexical search.`;
2774
- }
2775
- }
2776
- function modelCacheInfo(modelId = resolveEmbedModel()) {
2777
- const dir = modelCacheDir();
2778
- return { dir, present: isModelReady(modelId) || fs17.existsSync(dir), bytes: dirSize(dir) };
2779
- }
2780
- function clearModelCache() {
2781
- const dir = modelCacheDir();
2782
- const bytes = dirSize(dir);
2783
- try {
2784
- fs17.rmSync(dir, { recursive: true, force: true });
2785
- } catch {
2786
- }
2787
- return bytes;
2788
- }
2789
- function dirSize(dir) {
2790
- let total = 0;
2791
- let entries;
2792
- try {
2793
- entries = fs17.readdirSync(dir, { withFileTypes: true });
2794
- } catch {
2795
- return 0;
2796
- }
2797
- for (const e of entries) {
2798
- const p = path17.join(dir, e.name);
2799
- if (e.isDirectory()) total += dirSize(p);
2800
- else {
2801
- try {
2802
- total += fs17.statSync(p).size;
2803
- } catch {
2804
- }
2805
- }
2806
- }
2807
- return total;
2808
- }
2809
- function isPermissionError(e) {
2810
- const code = e?.code;
2811
- return code === "EACCES" || code === "EPERM" || code === "EROFS";
2812
- }
2813
- function embeddingsCached(root, modelId) {
2814
- return fs17.existsSync(path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
2815
- }
2816
- async function loadEmbedder(options = {}) {
2817
- if (options.local) return null;
2818
- const modelId = resolveEmbedModel(options.model);
2819
- if (options.noDownload && !isModelReady(modelId)) return null;
2820
- const fail = (reason) => {
2821
- options.onUnavailable?.(reason);
2822
- return null;
2823
- };
2824
- const mod = await importEmbedBackend();
2825
- if (!mod?.FlagEmbedding) return fail("not-installed");
2826
- const cache = modelCacheDir();
2827
- try {
2828
- fs17.mkdirSync(cache, { recursive: true });
2829
- } catch (e) {
2830
- return fail(isPermissionError(e) ? "no-permission" : "init-failed");
2831
- }
2832
- try {
2833
- const model = await withTimeout(
2834
- mod.FlagEmbedding.init({
2835
- model: mapModel(mod, modelId),
2836
- cacheDir: cache,
2837
- showDownloadProgress: options.showDownloadProgress ?? false
2838
- }),
2839
- embedInitTimeoutMs(),
2840
- "embedding model init timed out"
2841
- );
2842
- try {
2843
- fs17.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
2844
- } catch {
2845
- }
2846
- return {
2847
- id: modelId,
2848
- async embed(texts) {
2849
- const out = [];
2850
- for await (const batch of model.embed(texts, 64)) {
2851
- for (const v of batch) out.push(Array.from(v));
2852
- }
2853
- return out;
2854
- },
2855
- async embedQuery(text) {
2856
- const v = await model.queryEmbed(text);
2857
- return Array.from(v);
2858
- }
2859
- };
2860
- } catch (e) {
2861
- return fail(isPermissionError(e) ? "no-permission" : "download-failed");
2862
- }
2863
- }
2864
- async function importEmbedBackend() {
2865
- const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;
2866
- if (hostDir) {
2867
- try {
2868
- const req = createRequire(path17.join(hostDir, "package.json"));
2869
- const mod = await import(pathToFileURL(req.resolve("fastembed")).href);
2870
- const resolved = mod?.FlagEmbedding ? mod : mod?.default;
2871
- if (resolved?.FlagEmbedding) return resolved;
2872
- } catch {
2873
- }
2874
- }
2875
- return await import('fastembed').catch(() => null);
2876
- }
2877
- function embedInitTimeoutMs() {
2878
- const n = Number(process.env.VG_EMBED_TIMEOUT_MS);
2879
- return Number.isFinite(n) && n > 0 ? n : 6e4;
2880
- }
2881
- async function withTimeout(promise, ms, message) {
2882
- let timer;
2883
- try {
2884
- return await Promise.race([
2885
- promise,
2886
- new Promise((_, reject) => {
2887
- timer = setTimeout(() => reject(new Error(message)), ms);
2888
- timer.unref?.();
2889
- })
2890
- ]);
2891
- } finally {
2892
- clearTimeout(timer);
2893
- }
2894
- }
2895
- function mapModel(mod, id) {
2896
- const m = mod.EmbeddingModel ?? {};
2897
- if (/bge-small/i.test(id)) return m.BGESmallENV15 ?? m.BGESmallEN ?? id;
2898
- if (/bge-base/i.test(id)) return m.BGEBaseENV15 ?? id;
2899
- if (/all-minilm/i.test(id)) return m.AllMiniLML6V2 ?? id;
2900
- return id;
2901
- }
2902
- var PATH_NOISE = /* @__PURE__ */ new Set([
2903
- "src",
2904
- "app",
2905
- "apps",
2906
- "lib",
2907
- "libs",
2908
- "dist",
2909
- "build",
2910
- "out",
2911
- "index",
2912
- "main",
2913
- "packages",
2914
- "pkg",
2915
- "test",
2916
- "tests",
2917
- "__tests__",
2918
- "spec",
2919
- "node_modules",
2920
- "internal",
2921
- "com",
2922
- "example",
2923
- "demo",
2924
- "java",
2925
- "net",
2926
- "org",
2927
- "www"
2928
- ]);
2929
- function pathContext(file) {
2930
- const parts = file.split("/");
2931
- const stem = (parts.pop() ?? "").replace(/\.[^.]+$/, "");
2932
- const dirs = parts.filter((p) => p && !PATH_NOISE.has(p.toLowerCase()));
2933
- return [...dirs.slice(-2), stem].filter(Boolean).join(" ");
2934
- }
2935
- function nodeEmbedText(node, areaLabel) {
2936
- return [node.qualifiedName, node.kind, node.signature ?? "", node.doc ?? "", pathContext(node.file), areaLabel ?? ""].filter(Boolean).join(" ");
2937
- }
2938
- function cosine(a, b) {
2939
- let dot2 = 0;
2940
- let na = 0;
2941
- let nb = 0;
2942
- const n = Math.min(a.length, b.length);
2943
- for (let i = 0; i < n; i++) {
2944
- dot2 += a[i] * b[i];
2945
- na += a[i] * a[i];
2946
- nb += b[i] * b[i];
2947
- }
2948
- if (na === 0 || nb === 0) return 0;
2949
- return dot2 / (Math.sqrt(na) * Math.sqrt(nb));
2950
- }
2951
- var EMBED_CHUNK = 256;
2952
- var CACHE_WRITE_INTERVAL_MS = 1500;
2953
- function vectorCachePath(root, modelId) {
2954
- return path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
2955
- }
2956
- function countPending(graph, root, modelId) {
2957
- const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
2958
- const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
2959
- let pending = 0;
2960
- for (const n of graph.nodes) {
2961
- if (n.kind === "file" || n.kind === "external") continue;
2962
- const h = hashString(nodeEmbedText(n, areaLabel.get(n.area)));
2963
- const cached2 = entries[n.id];
2964
- if (!cached2 || cached2.hash !== h) pending++;
2965
- }
2966
- return pending;
2967
- }
2968
- function readCacheEntries(file, modelId) {
2969
- if (!fs17.existsSync(file)) return {};
2970
- try {
2971
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2972
- if (loaded.model === modelId && loaded.entries) return loaded.entries;
2973
- } catch {
2974
- }
2975
- return {};
2976
- }
2977
- async function getNodeEmbeddings(graph, embedder, root, onProgress) {
2978
- const file = vectorCachePath(root, embedder.id);
2979
- let cache = { model: embedder.id, entries: {} };
2980
- if (fs17.existsSync(file)) {
2981
- try {
2982
- const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
2983
- if (loaded.model === embedder.id && loaded.entries) cache = loaded;
2984
- } catch {
2985
- }
2986
- }
2987
- const areaLabel = new Map(graph.areas.map((a) => [a.id, a.label]));
2988
- const targets = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external");
2989
- const toEmbed = [];
2990
- const vectors = /* @__PURE__ */ new Map();
2991
- for (const n of targets) {
2992
- const text = nodeEmbedText(n, areaLabel.get(n.area));
2993
- const h = hashString(text);
2994
- const cached2 = cache.entries[n.id];
2995
- if (cached2 && cached2.hash === h) vectors.set(n.id, cached2.vec);
2996
- else toEmbed.push({ id: n.id, text, hash: h });
2997
- }
2998
- const persist = () => {
2999
- try {
3000
- fs17.mkdirSync(cacheDir(root), { recursive: true });
3001
- const tmp = `${file}.tmp.${process.pid}`;
3002
- fs17.writeFileSync(tmp, JSON.stringify(cache));
3003
- fs17.renameSync(tmp, file);
3004
- } catch {
3005
- }
3006
- };
3007
- if (toEmbed.length) {
3008
- const lock = `${file}.lock`;
3009
- if (!acquireLock(lock)) return vectors;
3010
- try {
3011
- onProgress?.(0, toEmbed.length);
3012
- let lastWrite = Date.now();
3013
- for (let i = 0; i < toEmbed.length; i += EMBED_CHUNK) {
3014
- const slice = toEmbed.slice(i, i + EMBED_CHUNK);
3015
- const vecs = await embedder.embed(slice.map((t) => t.text));
3016
- slice.forEach((t, j) => {
3017
- const vec = vecs[j] ?? [];
3018
- vectors.set(t.id, vec);
3019
- cache.entries[t.id] = { hash: t.hash, vec };
3020
- });
3021
- onProgress?.(Math.min(i + EMBED_CHUNK, toEmbed.length), toEmbed.length);
3022
- if (Date.now() - lastWrite >= CACHE_WRITE_INTERVAL_MS) {
3023
- persist();
3024
- lastWrite = Date.now();
3025
- }
3026
- }
3027
- const live = new Set(targets.map((n) => n.id));
3028
- for (const id of Object.keys(cache.entries)) if (!live.has(id)) delete cache.entries[id];
3029
- persist();
3030
- } finally {
3031
- releaseLock(lock);
3032
- }
3033
- }
3034
- return vectors;
3035
- }
3036
- function safe(id) {
3037
- return id.replace(/[^a-z0-9.-]+/gi, "_");
3038
- }
3039
2632
  var SNAPSHOT_VERSION = "vg-freshness/1";
3040
2633
  function snapshotPath(root) {
3041
- return path17.join(cacheDir(root), "freshness.json");
2634
+ return path2.join(cacheDir(root), "freshness.json");
3042
2635
  }
3043
2636
  function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
3044
2637
  const files = {};
3045
2638
  for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
3046
2639
  const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
3047
2640
  try {
3048
- fs17.mkdirSync(cacheDir(root), { recursive: true });
3049
- fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
2641
+ fs18.mkdirSync(cacheDir(root), { recursive: true });
2642
+ fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
3050
2643
  } catch {
3051
2644
  }
3052
2645
  }
3053
2646
  function loadSnapshot(root) {
3054
2647
  try {
3055
- const loaded = JSON.parse(fs17.readFileSync(snapshotPath(root), "utf8"));
2648
+ const loaded = JSON.parse(fs18.readFileSync(snapshotPath(root), "utf8"));
3056
2649
  if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
3057
2650
  return { ...loaded, scope: loaded.scope ?? {} };
3058
2651
  }
@@ -3088,7 +2681,7 @@ function probeFreshness(root) {
3088
2681
  }
3089
2682
  let stat;
3090
2683
  try {
3091
- stat = fs17.statSync(file.abs);
2684
+ stat = fs18.statSync(file.abs);
3092
2685
  } catch {
3093
2686
  drift.removed.push(file.rel);
3094
2687
  continue;
@@ -3096,7 +2689,7 @@ function probeFreshness(root) {
3096
2689
  if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
3097
2690
  let hash = "";
3098
2691
  try {
3099
- hash = hashBytes(fs17.readFileSync(file.abs));
2692
+ hash = hashBytes(fs18.readFileSync(file.abs));
3100
2693
  } catch {
3101
2694
  drift.removed.push(file.rel);
3102
2695
  continue;
@@ -3113,7 +2706,7 @@ function probeFreshness(root) {
3113
2706
  }
3114
2707
  if (absorbed && !hasDrift(drift)) {
3115
2708
  try {
3116
- fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
2709
+ fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
3117
2710
  } catch {
3118
2711
  }
3119
2712
  }
@@ -3136,348 +2729,6 @@ function pruneScope(scope) {
3136
2729
  if (scope.grammarsDir) out.grammarsDir = scope.grammarsDir;
3137
2730
  return out;
3138
2731
  }
3139
-
3140
- // src/engine/relations.ts
3141
- var GraphIndex = class {
3142
- constructor(graph) {
3143
- this.graph = graph;
3144
- for (const n of graph.nodes) this.nodeById.set(n.id, n);
3145
- for (const e of graph.edges) {
3146
- push2(this.outById, e.src, e);
3147
- push2(this.inById, e.dst, e);
3148
- }
3149
- }
3150
- graph;
3151
- nodeById = /* @__PURE__ */ new Map();
3152
- outById = /* @__PURE__ */ new Map();
3153
- inById = /* @__PURE__ */ new Map();
3154
- out(id, kind) {
3155
- const all = this.outById.get(id) ?? [];
3156
- return kind ? all.filter((e) => e.kind === kind) : all;
3157
- }
3158
- in(id, kind) {
3159
- const all = this.inById.get(id) ?? [];
3160
- return kind ? all.filter((e) => e.kind === kind) : all;
3161
- }
3162
- node(id) {
3163
- return this.nodeById.get(id);
3164
- }
3165
- /**
3166
- * Resolved nodes called by `id` — invocations (`call`) plus structural
3167
- * dependency references (`references`, e.g. a constructor-injected field's
3168
- * type), since both represent real usage a caller/impact question cares
3169
- * about. `references` is otherwise only emitted by the precise SCIP/tsc
3170
- * rungs and (for Java DI wiring) the heuristic rung — never a guess.
3171
- */
3172
- callees(id) {
3173
- return this.resolveTargets(this.out(id).filter(isUsageEdge), "dst");
3174
- }
3175
- /** Resolved nodes that call or structurally reference `id`. */
3176
- callers(id) {
3177
- return this.resolveTargets(this.in(id).filter(isUsageEdge), "src");
3178
- }
3179
- resolveTargets(edges, end) {
3180
- const out = [];
3181
- for (const e of edges) {
3182
- const node = this.nodeById.get(e[end]);
3183
- if (node) out.push({ edge: e, node });
3184
- }
3185
- return out;
3186
- }
3187
- };
3188
- var USAGE_KINDS = /* @__PURE__ */ new Set(["call", "references"]);
3189
- function isUsageEdge(e) {
3190
- return USAGE_KINDS.has(e.kind);
3191
- }
3192
- function push2(map, key, value) {
3193
- const list = map.get(key);
3194
- if (list) list.push(value);
3195
- else map.set(key, [value]);
3196
- }
3197
- var INDEX_CACHE = /* @__PURE__ */ new WeakMap();
3198
- function indexFor(graph) {
3199
- let idx = INDEX_CACHE.get(graph);
3200
- if (!idx) {
3201
- idx = new GraphIndex(graph);
3202
- INDEX_CACHE.set(graph, idx);
3203
- }
3204
- return idx;
3205
- }
3206
-
3207
- // src/engine/query.ts
3208
- var STOPWORDS = /* @__PURE__ */ new Set([
3209
- "the",
3210
- "a",
3211
- "an",
3212
- "is",
3213
- "are",
3214
- "was",
3215
- "were",
3216
- "be",
3217
- "to",
3218
- "of",
3219
- "in",
3220
- "on",
3221
- "for",
3222
- "and",
3223
- "or",
3224
- "where",
3225
- "what",
3226
- "which",
3227
- "how",
3228
- "do",
3229
- "does",
3230
- "did",
3231
- "i",
3232
- "we",
3233
- "it",
3234
- "this",
3235
- "that",
3236
- "with",
3237
- "when",
3238
- "who",
3239
- "why",
3240
- "can",
3241
- "should",
3242
- "my",
3243
- "our",
3244
- "you",
3245
- "your",
3246
- "from",
3247
- "by",
3248
- "at",
3249
- "as",
3250
- // Discovery-question scaffolding: words a caller uses to FRAME the ask
3251
- // ("find the code responsible for X", "I need to modify X, where do I
3252
- // start?", "explain how X works in this codebase") rather than to name the
3253
- // target. Left in, these compete on equal footing with the real identifier
3254
- // terms and can outrank it outright — e.g. "find the code responsible for
3255
- // deleteAsync" let "find" alone drag in every FindByIdAsync/FindAll method
3256
- // in the repo, none of them the target (VG-LOCATE-FAILURE-ANALYSIS.md).
3257
- "find",
3258
- "code",
3259
- "responsible",
3260
- "need",
3261
- "modify",
3262
- "me",
3263
- "implementation",
3264
- "explain",
3265
- "works",
3266
- "codebase",
3267
- "contains",
3268
- "file"
3269
- ]);
3270
- function queryGraph(graph, question, options = {}) {
3271
- const budget = options.budget ?? 2e3;
3272
- const limit = options.limit ?? 12;
3273
- const terms = tokenize(question);
3274
- const weightOf = termWeights(graph, terms);
3275
- const index = indexFor(graph);
3276
- const scored = [];
3277
- for (const node of graph.nodes) {
3278
- if (node.kind === "file" || node.kind === "external") continue;
3279
- const { score, why } = scoreNode(node, terms, weightOf);
3280
- if (score > 0) scored.push({ node, score: round2(score * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
3281
- }
3282
- scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
3283
- const seeds = scored.slice(0, limit);
3284
- const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget);
3285
- return { question, matches: seeds, context, tokensEstimate };
3286
- }
3287
- async function queryGraphSemantic(graph, question, options) {
3288
- const budget = options.budget ?? 2e3;
3289
- const limit = options.limit ?? 12;
3290
- const terms = tokenize(question);
3291
- const weightOf = termWeights(graph, terms);
3292
- const index = indexFor(graph);
3293
- const queryVec = await options.embedder.embedQuery(question);
3294
- const lexRaw = /* @__PURE__ */ new Map();
3295
- let lexMax = 0;
3296
- const whyById = /* @__PURE__ */ new Map();
3297
- for (const node of graph.nodes) {
3298
- if (node.kind === "file" || node.kind === "external") continue;
3299
- const { score, why } = scoreNode(node, terms, weightOf);
3300
- lexRaw.set(node.id, score);
3301
- whyById.set(node.id, why);
3302
- if (score > lexMax) lexMax = score;
3303
- }
3304
- const scored = [];
3305
- for (const node of graph.nodes) {
3306
- if (node.kind === "file" || node.kind === "external") continue;
3307
- const lexNorm = lexMax > 0 ? (lexRaw.get(node.id) ?? 0) / lexMax : 0;
3308
- const vec = options.nodeVectors.get(node.id);
3309
- const sem = vec ? Math.max(0, cosine(queryVec, vec)) : 0;
3310
- const hybrid = 0.5 * lexNorm + 0.5 * sem;
3311
- if (hybrid <= 0) continue;
3312
- const lexWhy = whyById.get(node.id);
3313
- const why = lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : "weak match");
3314
- scored.push({ node, score: round2(hybrid * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
3315
- }
3316
- scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
3317
- const seeds = scored.slice(0, limit);
3318
- const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget);
3319
- return { question, matches: seeds, context, tokensEstimate };
3320
- }
3321
- function tokenize(q2) {
3322
- return [
3323
- ...new Set(
3324
- q2.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((t) => t.length >= 1 && !STOPWORDS.has(t))
3325
- )
3326
- ];
3327
- }
3328
- function scoreNode(node, terms, weightOf = () => 1) {
3329
- let score = 0;
3330
- const hits = [];
3331
- const name = node.name.toLowerCase();
3332
- const qn = node.qualifiedName.toLowerCase();
3333
- const file = node.file.toLowerCase();
3334
- const nameParts = identifierParts(node.name);
3335
- for (const t of terms) {
3336
- const w = weightOf(t);
3337
- if (name === t) {
3338
- score += 10 * w;
3339
- hits.push(t);
3340
- } else if (nameParts.has(t)) {
3341
- score += 6 * w;
3342
- hits.push(t);
3343
- } else if (name.includes(t)) {
3344
- score += 4 * w;
3345
- hits.push(t);
3346
- } else if (qn.includes(t)) {
3347
- score += 3 * w;
3348
- hits.push(t);
3349
- } else if (fuzzyPartMatch(t, nameParts)) {
3350
- score += 2 * w;
3351
- hits.push(`~${t}`);
3352
- } else if (file.includes(t)) {
3353
- score += 1 * w;
3354
- hits.push(t);
3355
- }
3356
- }
3357
- return { score, why: hits.length ? `matched: ${hits.join(", ")}` : "" };
3358
- }
3359
- var IMPORTANCE_WEIGHT = 0.4;
3360
- function termWeights(graph, terms) {
3361
- if (terms.length === 0) return () => 1;
3362
- const df = /* @__PURE__ */ new Map();
3363
- let n = 0;
3364
- for (const node of graph.nodes) {
3365
- if (node.kind === "file" || node.kind === "external") continue;
3366
- n++;
3367
- const name = node.name.toLowerCase();
3368
- const parts = identifierParts(node.name);
3369
- for (const t of terms) {
3370
- if (parts.has(t) || name.includes(t)) df.set(t, (df.get(t) ?? 0) + 1);
3371
- }
3372
- }
3373
- const w = /* @__PURE__ */ new Map();
3374
- for (const t of terms) {
3375
- const idf = Math.log((n + 1) / ((df.get(t) ?? 0) + 1)) + 1;
3376
- w.set(t, Math.max(0.5, Math.min(8, idf)));
3377
- }
3378
- return (t) => w.get(t) ?? 1;
3379
- }
3380
- function identifierParts(name) {
3381
- return new Set(
3382
- name.split(/[^\p{L}\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/u).filter(Boolean).map((s) => s.toLowerCase())
3383
- );
3384
- }
3385
- function fuzzyPartMatch(term, parts) {
3386
- for (const part of parts) {
3387
- const shared = sharedPrefixLen(term, part);
3388
- if (shared >= 5 && shared >= 0.6 * Math.min(term.length, part.length)) return true;
3389
- }
3390
- return false;
3391
- }
3392
- function sharedPrefixLen(a, b) {
3393
- const n = Math.min(a.length, b.length);
3394
- let i = 0;
3395
- while (i < n && a[i] === b[i]) i++;
3396
- return i;
3397
- }
3398
- function buildContext(graph, index, question, seeds, budget) {
3399
- const lines = [];
3400
- lines.push(`# Context for: ${question}`);
3401
- lines.push("");
3402
- if (seeds.length === 0) {
3403
- lines.push("_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._");
3404
- const text2 = lines.join("\n");
3405
- return { context: text2, tokensEstimate: estimateTokens(text2) };
3406
- }
3407
- for (const { node, why } of seeds) {
3408
- const block = [];
3409
- block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);
3410
- if (node.signature) block.push("`" + node.signature + "`");
3411
- const callees = index.callees(node.id).map((x) => x.node.qualifiedName);
3412
- const callers = index.callers(node.id).map((x) => x.node.qualifiedName);
3413
- if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(", ")}`);
3414
- if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(", ")}`);
3415
- block.push(`importance ${node.importance.toFixed(3)} \xB7 area #${node.area}${node.isHub ? " \xB7 hub" : ""} \xB7 ${why}`);
3416
- block.push("");
3417
- const candidate = lines.concat(block).join("\n");
3418
- if (estimateTokens(candidate) > budget && lines.length > 2) break;
3419
- lines.push(...block);
3420
- }
3421
- const text = lines.join("\n");
3422
- return { context: text, tokensEstimate: estimateTokens(text) };
3423
- }
3424
- function unique(xs) {
3425
- return [...new Set(xs)];
3426
- }
3427
- function estimateTokens(text) {
3428
- return Math.ceil(text.length / 4);
3429
- }
3430
- function round2(x) {
3431
- return Math.round(x * 1e6) / 1e6;
3432
- }
3433
-
3434
- // src/engine/lookup.ts
3435
- function findNodes(graph, query) {
3436
- const q2 = query.trim();
3437
- if (!q2) return [];
3438
- const byId = graph.nodes.filter((n) => n.id === q2);
3439
- if (byId.length) return byId;
3440
- const fileLine = /^(.*):(\d+)$/.exec(q2);
3441
- if (fileLine) {
3442
- const file = fileLine[1];
3443
- const line = Number(fileLine[2]);
3444
- 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));
3445
- if (spanning.length) return spanning;
3446
- }
3447
- if (/[*?]/.test(q2)) {
3448
- const re = globToRegExp(q2);
3449
- return rank(graph.nodes.filter((n) => re.test(n.qualifiedName) || re.test(n.name) || re.test(n.file)));
3450
- }
3451
- const byQn = graph.nodes.filter((n) => n.qualifiedName === q2);
3452
- if (byQn.length) return rank(byQn);
3453
- const byName = graph.nodes.filter((n) => n.name === q2);
3454
- if (byName.length) return rank(byName);
3455
- const lower = q2.toLowerCase();
3456
- const ci = graph.nodes.filter(
3457
- (n) => n.qualifiedName.toLowerCase() === lower || n.name.toLowerCase() === lower
3458
- );
3459
- if (ci.length) return rank(ci);
3460
- return rank(graph.nodes.filter((n) => n.qualifiedName.toLowerCase().includes(lower))).slice(0, 25);
3461
- }
3462
- function resolveOne(graph, query, pick) {
3463
- const candidates = findNodes(graph, query);
3464
- if (candidates.length === 0) return { candidates };
3465
- if (pick && pick >= 1 && pick <= candidates.length) return { node: candidates[pick - 1], candidates };
3466
- if (candidates.length === 1) return { node: candidates[0], candidates };
3467
- return { candidates };
3468
- }
3469
- function nodeById(graph, id) {
3470
- return graph.nodes.find((n) => n.id === id);
3471
- }
3472
- function rank(nodes) {
3473
- return [...nodes].sort(
3474
- (a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)
3475
- );
3476
- }
3477
- function globToRegExp(glob) {
3478
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3479
- return new RegExp(`^${escaped}$`, "i");
3480
- }
3481
2732
  function buildGraphologyGraph(nodes, edges) {
3482
2733
  const g = new Graph({ type: "directed", multi: true, allowSelfLoops: true });
3483
2734
  for (const n of nodes) {
@@ -3501,57 +2752,6 @@ function shortestPath(graph, srcId, dstId) {
3501
2752
  if (reverse) return { ids: reverse.slice().reverse(), direction: "reverse" };
3502
2753
  return null;
3503
2754
  }
3504
-
3505
- // src/engine/impact.ts
3506
- var DEPEND_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
3507
- function impactOf(graph, rootId, opts = {}) {
3508
- const maxDepth = Math.max(1, opts.depth ?? 4);
3509
- const index = indexFor(graph);
3510
- const root = index.node(rootId);
3511
- const affected = /* @__PURE__ */ new Map();
3512
- let minEdgeConfidence = 1;
3513
- let frontier = [{ id: rootId, conf: 1 }];
3514
- for (let depth = 1; depth <= maxDepth; depth++) {
3515
- const nextFrontier = [];
3516
- for (const { id, conf } of frontier) {
3517
- for (const e of index.in(id)) {
3518
- if (!DEPEND_EDGES.has(e.kind)) continue;
3519
- minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);
3520
- const dependentId = e.src;
3521
- if (dependentId === rootId || affected.has(dependentId)) continue;
3522
- const node = index.node(dependentId);
3523
- if (!node) continue;
3524
- const conf2 = round3(conf * e.confidence * 0.9);
3525
- affected.set(dependentId, {
3526
- id: node.id,
3527
- name: node.qualifiedName,
3528
- kind: node.kind,
3529
- file: node.file,
3530
- line: node.span.start,
3531
- depth,
3532
- confidence: conf2
3533
- });
3534
- nextFrontier.push({ id: dependentId, conf: conf2 });
3535
- }
3536
- }
3537
- frontier = nextFrontier;
3538
- if (frontier.length === 0) break;
3539
- }
3540
- const items = [...affected.values()].sort(
3541
- (a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name)
3542
- );
3543
- return {
3544
- root: { id: rootId, name: root?.qualifiedName ?? rootId },
3545
- depth: maxDepth,
3546
- affected: items,
3547
- direct: items.filter((i) => i.depth === 1).length,
3548
- transitive: items.filter((i) => i.depth > 1).length,
3549
- minEdgeConfidence: round3(minEdgeConfidence)
3550
- };
3551
- }
3552
- function round3(x) {
3553
- return Math.round(x * 1e6) / 1e6;
3554
- }
3555
2755
  function coveringTests(graph, node, index) {
3556
2756
  const idx = index ?? indexFor(graph);
3557
2757
  const byId = idx.nodeById;
@@ -3587,10 +2787,10 @@ function testsToRun(graph, rootId, depth = 4) {
3587
2787
  };
3588
2788
  }
3589
2789
  function detectRunner(root, lang) {
3590
- const pkgPath = path17.join(root, "package.json");
3591
- if (fs17.existsSync(pkgPath)) {
2790
+ const pkgPath = path2.join(root, "package.json");
2791
+ if (fs18.existsSync(pkgPath)) {
3592
2792
  try {
3593
- const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf8"));
2793
+ const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf8"));
3594
2794
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
3595
2795
  if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
3596
2796
  if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
@@ -3598,10 +2798,10 @@ function detectRunner(root, lang) {
3598
2798
  } catch {
3599
2799
  }
3600
2800
  }
3601
- 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"))) {
3602
2802
  return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
3603
2803
  }
3604
- if (lang === "go" || fs17.existsSync(path17.join(root, "go.mod"))) {
2804
+ if (lang === "go" || fs18.existsSync(path2.join(root, "go.mod"))) {
3605
2805
  return { name: "go test", command: () => "go test ./..." };
3606
2806
  }
3607
2807
  if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
@@ -3610,469 +2810,12 @@ function detectRunner(root, lang) {
3610
2810
  if (lang === "rb") return { name: "rspec", command: (f) => `bundle exec rspec ${f.join(" ")}`.trim() };
3611
2811
  return { name: "tests", command: (f) => f.length ? f.join(" ") : "(no runner detected)" };
3612
2812
  }
3613
- function hasFile(root, re) {
3614
- try {
3615
- return fs17.readdirSync(root).some((f) => re.test(f));
3616
- } catch {
3617
- return false;
3618
- }
3619
- }
3620
- var ECOSYSTEMS = ["npm", "pypi", "go", "rust", "ruby", "php", "dotnet", "swift", "dart", "java"];
3621
- function inventory(root) {
3622
- const manifests = findManifests(root);
3623
- const records = [];
3624
- records.push(...npmDeps(manifests.npm));
3625
- records.push(...pypiDeps(manifests.pypi));
3626
- records.push(...goDeps(manifests.go));
3627
- records.push(...cargoDeps(manifests.rust));
3628
- records.push(...rubyDeps(manifests.ruby));
3629
- records.push(...phpDeps(manifests.php));
3630
- records.push(...dotnetDeps(manifests.dotnet));
3631
- records.push(...swiftDeps(manifests.swift));
3632
- records.push(...dartDeps(manifests.dart));
3633
- records.push(...javaDeps(manifests.java));
3634
- const seen = /* @__PURE__ */ new Set();
3635
- const deduped = records.filter((r) => {
3636
- const key = `${r.ecosystem}\0${r.name}`;
3637
- if (seen.has(key)) return false;
3638
- seen.add(key);
3639
- return true;
3640
- });
3641
- deduped.sort((a, b) => a.ecosystem.localeCompare(b.ecosystem) || a.name.localeCompare(b.name));
3642
- const counts = { total: deduped.length };
3643
- for (const e of ECOSYSTEMS) counts[e] = 0;
3644
- for (const r of deduped) counts[r.ecosystem]++;
3645
- return { records: deduped, counts };
3646
- }
3647
- var SKIP_DIRS2 = /* @__PURE__ */ new Set([
3648
- "node_modules",
3649
- ".git",
3650
- "dist",
3651
- "build",
3652
- "out",
3653
- ".vibgrate",
3654
- "vendor",
3655
- ".venv",
3656
- "venv",
3657
- "env",
3658
- "__pycache__",
3659
- "target",
3660
- ".next",
3661
- ".nuxt",
3662
- "coverage",
3663
- ".cache"
3664
- ]);
3665
- var MANIFEST_BY_FILE = {
3666
- "package.json": "npm",
3667
- "requirements.txt": "pypi",
3668
- "pyproject.toml": "pypi",
3669
- "go.mod": "go",
3670
- "Cargo.toml": "rust",
3671
- Gemfile: "ruby",
3672
- "composer.json": "php",
3673
- "Package.swift": "swift",
3674
- "pubspec.yaml": "dart",
3675
- "pom.xml": "java",
3676
- "build.gradle": "java",
3677
- "build.gradle.kts": "java"
3678
- };
3679
- function findManifests(root) {
3680
- const set = Object.fromEntries(ECOSYSTEMS.map((e) => [e, []]));
3681
- const MAX_ENTRIES2 = 2e4;
3682
- let scanned = 0;
3683
- const walk2 = (dir, depth) => {
3684
- if (depth > 8 || scanned > MAX_ENTRIES2) return;
3685
- let entries;
3686
- try {
3687
- entries = fs17.readdirSync(dir, { withFileTypes: true });
3688
- } catch {
3689
- return;
3690
- }
3691
- for (const e of entries) {
3692
- scanned++;
3693
- if (scanned > MAX_ENTRIES2) break;
3694
- if (e.isDirectory()) {
3695
- if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(path17.join(dir, e.name), depth + 1);
3696
- continue;
3697
- }
3698
- const eco = MANIFEST_BY_FILE[e.name] ?? (/\.(cs|fs)proj$/i.test(e.name) ? "dotnet" : void 0);
3699
- if (eco) set[eco].push(path17.join(dir, e.name));
3700
- }
3701
- };
3702
- walk2(root, 0);
3703
- for (const e of ECOSYSTEMS) set[e].sort();
3704
- return set;
3705
- }
3706
- function npmDeps(files) {
3707
- const out = [];
3708
- for (const file of files) {
3709
- let pkg;
3710
- try {
3711
- pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
3712
- } catch {
3713
- continue;
3714
- }
3715
- const dir = path17.dirname(file);
3716
- const all = { ...pkg.dependencies, ...pkg.devDependencies };
3717
- for (const [name, declared] of Object.entries(all)) {
3718
- out.push({ name, ecosystem: "npm", declared, installed: installedNpmVersion(dir, name) });
3719
- }
3720
- }
3721
- return out;
3722
- }
3723
- function installedNpmVersion(dir, name) {
3724
- let cur = dir;
3725
- for (let i = 0; i < 12; i++) {
3726
- const p = path17.join(cur, "node_modules", name, "package.json");
3727
- try {
3728
- return JSON.parse(fs17.readFileSync(p, "utf8")).version;
3729
- } catch {
3730
- }
3731
- const parent = path17.dirname(cur);
3732
- if (parent === cur) break;
3733
- cur = parent;
3734
- }
3735
- return void 0;
3736
- }
3737
- function installedTreeVersion(root, ecosystem, name) {
3738
- if (ecosystem === "pypi") return installedPypiVersion(root, name);
3739
- if (ecosystem === "php") return installedPhpVersion(root, name);
3740
- return void 0;
3741
- }
3742
- function normalizePep503(name) {
3743
- return name.trim().toLowerCase().replace(/[-_.]+/g, "-");
3744
- }
3745
- function sitePackagesDirs(base) {
3746
- const out = [];
3747
- try {
3748
- for (const d of fs17.readdirSync(path17.join(base, "lib"))) {
3749
- if (d.startsWith("python")) out.push(path17.join(base, "lib", d, "site-packages"));
3750
- }
3751
- } catch {
3752
- }
3753
- out.push(path17.join(base, "Lib", "site-packages"));
3754
- return out;
3755
- }
3756
- function installedPypiVersion(root, name) {
3757
- const target = normalizePep503(name);
3758
- for (const venv of [".venv", "venv", "env", ".tox"]) {
3759
- for (const sp of sitePackagesDirs(path17.join(root, venv))) {
3760
- let entries;
3761
- try {
3762
- entries = fs17.readdirSync(sp);
3763
- } catch {
3764
- continue;
3765
- }
3766
- for (const entry of entries) {
3767
- const m = /^(.+?)-(\d[^-]*)\.dist-info$/.exec(entry);
3768
- if (m && normalizePep503(m[1]) === target) return m[2];
3769
- }
3770
- }
3771
- }
3772
- return void 0;
3773
- }
3774
- function installedPhpVersion(root, name) {
3775
- let data;
3776
- try {
3777
- data = JSON.parse(fs17.readFileSync(path17.join(root, "vendor", "composer", "installed.json"), "utf8"));
3778
- } catch {
3779
- return void 0;
3780
- }
3781
- const pkgs = Array.isArray(data) ? data : data.packages;
3782
- if (!Array.isArray(pkgs)) return void 0;
3783
- const p = pkgs.find((x) => x && typeof x === "object" && x.name === name);
3784
- return typeof p?.version === "string" ? p.version.replace(/^v/, "") : void 0;
3785
- }
3786
- function pypiDeps(files) {
3787
- const byName = /* @__PURE__ */ new Map();
3788
- const add3 = (name, declared) => {
3789
- const clean = name.trim().replace(/\[.*\]$/, "");
3790
- if (!clean || clean.toLowerCase() === "python") return;
3791
- if (!byName.has(clean)) byName.set(clean, { name: clean, ecosystem: "pypi", declared: declared.trim() || "*" });
3792
- };
3793
- for (const file of files) {
3794
- let text;
3795
- try {
3796
- text = fs17.readFileSync(file, "utf8");
3797
- } catch {
3798
- continue;
3799
- }
3800
- if (path17.basename(file) === "requirements.txt") {
3801
- for (const line of text.split("\n")) {
3802
- const t = line.trim();
3803
- if (!t || t.startsWith("#") || t.startsWith("-")) continue;
3804
- const m = /^([A-Za-z0-9._-]+(?:\[[^\]]*\])?)\s*([<>=!~).*]+.*)?$/.exec(t);
3805
- if (m) add3(m[1], m[2] ?? "*");
3806
- }
3807
- } else {
3808
- for (const dep of pep621Dependencies(text)) {
3809
- const m = /^([A-Za-z0-9._-]+(?:\[[^\]]*\])?)\s*(.*)$/.exec(dep.trim());
3810
- if (m) add3(m[1], m[2] ?? "*");
3811
- }
3812
- for (const [name, spec] of poetryDependencies(text)) add3(name, spec);
3813
- }
3814
- }
3815
- return [...byName.values()];
3816
- }
3817
- function pep621Dependencies(text) {
3818
- const section = sectionBody(text, "project");
3819
- if (!section) return [];
3820
- const start = /dependencies\s*=\s*\[/.exec(section);
3821
- if (!start) return [];
3822
- let depth = 0;
3823
- let inStr = false;
3824
- let strCh = "";
3825
- const out = [];
3826
- let cur = "";
3827
- for (let i = start.index + start[0].length - 1; i < section.length; i++) {
3828
- const ch = section[i];
3829
- if (inStr) {
3830
- if (ch === strCh) {
3831
- out.push(cur);
3832
- inStr = false;
3833
- } else cur += ch;
3834
- continue;
3835
- }
3836
- if (ch === '"' || ch === "'") {
3837
- inStr = true;
3838
- strCh = ch;
3839
- cur = "";
3840
- } else if (ch === "[") depth++;
3841
- else if (ch === "]") {
3842
- depth--;
3843
- if (depth === 0) break;
3844
- }
3845
- }
3846
- return out;
3847
- }
3848
- function poetryDependencies(text) {
3849
- const out = [];
3850
- const lines = text.split("\n");
3851
- let inDeps = false;
3852
- for (const raw of lines) {
3853
- const line = raw.trim();
3854
- if (line.startsWith("[")) {
3855
- inDeps = /^\[tool\.poetry(\.group\.[^.\]]+)?\.dependencies\]$/.test(line);
3856
- continue;
3857
- }
3858
- if (!inDeps || !line || line.startsWith("#")) continue;
3859
- const m = /^([A-Za-z0-9._-]+)\s*=\s*["']([^"']*)["']/.exec(line);
3860
- if (m) out.push([m[1], m[2] || "*"]);
3861
- }
3862
- return out;
3863
- }
3864
- function sectionBody(text, name) {
3865
- const lines = text.split("\n");
3866
- let capturing = false;
3867
- const body = [];
3868
- for (const raw of lines) {
3869
- const line = raw.trim();
3870
- if (/^\[/.test(line)) {
3871
- if (capturing) break;
3872
- capturing = line === `[${name}]`;
3873
- continue;
3874
- }
3875
- if (capturing) body.push(raw);
3876
- }
3877
- return capturing || body.length ? body.join("\n") : null;
3878
- }
3879
- function goDeps(files) {
3880
- const out = [];
3881
- for (const mod of files) {
3882
- let text;
3883
- try {
3884
- text = fs17.readFileSync(mod, "utf8");
3885
- } catch {
3886
- continue;
3887
- }
3888
- const re = /^\s*(?:require\s+)?([\w./-]+)\s+v(\S+)/gm;
3889
- let m;
3890
- while (m = re.exec(text)) {
3891
- if (m[1] === "go" || m[1] === "require" || m[1].startsWith("module")) continue;
3892
- out.push({ name: m[1], ecosystem: "go", declared: `v${m[2]}` });
3893
- }
3894
- }
3895
- return out;
3896
- }
3897
- function cargoDeps(files) {
3898
- const out = [];
3899
- for (const file of files) {
3900
- let text;
3901
- try {
3902
- text = fs17.readFileSync(file, "utf8");
3903
- } catch {
3904
- continue;
3905
- }
3906
- for (const table of ["dependencies", "dev-dependencies", "build-dependencies"]) {
3907
- const body = sectionBody(text, table);
3908
- if (!body) continue;
3909
- for (const raw of body.split("\n")) {
3910
- const line = raw.trim();
3911
- if (!line || line.startsWith("#")) continue;
3912
- const m = /^([A-Za-z0-9._-]+)\s*=\s*(.+)$/.exec(line);
3913
- if (!m) continue;
3914
- const rhs = m[2].trim();
3915
- let declared = "*";
3916
- if (rhs.startsWith('"')) {
3917
- const sm = /^"([^"]+)"/.exec(rhs);
3918
- if (sm) declared = sm[1];
3919
- } else if (rhs.startsWith("{")) {
3920
- const vm = /version\s*=\s*"([^"]+)"/.exec(rhs);
3921
- if (vm) declared = vm[1];
3922
- }
3923
- out.push({ name: m[1], ecosystem: "rust", declared });
3924
- }
3925
- }
3926
- }
3927
- return out;
3928
- }
3929
- function rubyDeps(files) {
3930
- const out = [];
3931
- for (const file of files) {
3932
- let text;
3933
- try {
3934
- text = fs17.readFileSync(file, "utf8");
3935
- } catch {
3936
- continue;
3937
- }
3938
- const re = /^\s*gem\s+["']([^"']+)["']\s*(?:,\s*["']([^"']+)["'])?/gm;
3939
- let m;
3940
- while (m = re.exec(text)) out.push({ name: m[1], ecosystem: "ruby", declared: m[2] ?? "*" });
3941
- }
3942
- return out;
3943
- }
3944
- function phpDeps(files) {
3945
- const out = [];
3946
- for (const file of files) {
3947
- let pkg;
3948
- try {
3949
- pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
3950
- } catch {
3951
- continue;
3952
- }
3953
- const all = { ...pkg.require, ...pkg["require-dev"] };
3954
- for (const [name, declared] of Object.entries(all)) {
3955
- if (!name.includes("/")) continue;
3956
- out.push({ name, ecosystem: "php", declared });
3957
- }
3958
- }
3959
- return out;
3960
- }
3961
- function dotnetDeps(files) {
3962
- const out = [];
3963
- for (const file of files) {
3964
- let text;
3965
- try {
3966
- text = fs17.readFileSync(file, "utf8");
3967
- } catch {
3968
- continue;
3969
- }
3970
- const tagRe = /<PackageReference\b[^>]*>/gi;
3971
- let m;
3972
- while (m = tagRe.exec(text)) {
3973
- const inc = /Include\s*=\s*"([^"]+)"/i.exec(m[0]);
3974
- if (!inc) continue;
3975
- const ver = /Version\s*=\s*"([^"]+)"/i.exec(m[0]);
3976
- out.push({ name: inc[1], ecosystem: "dotnet", declared: ver?.[1] ?? "*" });
3977
- }
3978
- }
3979
- return out;
3980
- }
3981
- function swiftDeps(files) {
3982
- const out = [];
3983
- for (const file of files) {
3984
- let text;
3985
- try {
3986
- text = fs17.readFileSync(file, "utf8");
3987
- } catch {
3988
- continue;
3989
- }
3990
- const re = /\.package\(/g;
3991
- let m;
3992
- while (m = re.exec(text)) {
3993
- const win = text.slice(m.index, m.index + 200);
3994
- const urlM = /url:\s*"([^"]+)"/.exec(win);
3995
- if (!urlM) continue;
3996
- const name = (urlM[1].replace(/\.git$/, "").split("/").filter(Boolean).pop() ?? urlM[1]).toLowerCase();
3997
- const verM = /"(\d+\.\d+(?:\.\d+)?)"/.exec(win.slice(urlM.index + urlM[0].length));
3998
- out.push({ name, ecosystem: "swift", declared: verM?.[1] ?? "*" });
3999
- }
4000
- }
4001
- return out;
4002
- }
4003
- function dartDeps(files) {
4004
- const out = [];
4005
- for (const file of files) {
4006
- let text;
4007
- try {
4008
- text = fs17.readFileSync(file, "utf8");
4009
- } catch {
4010
- continue;
4011
- }
4012
- let section = "";
4013
- for (const raw of text.split("\n")) {
4014
- const top = /^([a-z_]+):\s*$/.exec(raw);
4015
- if (top) {
4016
- section = top[1];
4017
- continue;
4018
- }
4019
- if (section !== "dependencies" && section !== "dev_dependencies") continue;
4020
- const m = /^ ([A-Za-z0-9_.]+):\s*(\S.*)?$/.exec(raw);
4021
- if (!m || m[1] === "flutter" || m[1] === "sdk") continue;
4022
- const declared = (m[2] ?? "").replace(/^["']|["']$/g, "").trim() || "*";
4023
- out.push({ name: m[1], ecosystem: "dart", declared });
4024
- }
4025
- }
4026
- return out;
4027
- }
4028
- function javaDeps(files) {
4029
- const out = [];
4030
- for (const file of files) {
4031
- let text;
4032
- try {
4033
- text = fs17.readFileSync(file, "utf8");
4034
- } catch {
4035
- continue;
4036
- }
4037
- if (path17.basename(file) === "pom.xml") {
4038
- for (const block of text.match(/<dependency>[\s\S]*?<\/dependency>/g) ?? []) {
4039
- const g = /<groupId>([^<]+)<\/groupId>/.exec(block);
4040
- const a = /<artifactId>([^<]+)<\/artifactId>/.exec(block);
4041
- if (!g || !a) continue;
4042
- const v = /<version>([^<]+)<\/version>/.exec(block);
4043
- const declared = v ? v[1].trim().startsWith("${") ? "*" : v[1].trim() : "*";
4044
- out.push({ name: `${g[1].trim()}:${a[1].trim()}`, ecosystem: "java", declared });
4045
- }
4046
- } else {
4047
- const re = /(?:implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|annotationProcessor)\s*[(]?\s*["']([\w.-]+):([\w.-]+):([^"']+)["']/g;
4048
- let m;
4049
- while (m = re.exec(text)) out.push({ name: `${m[1]}:${m[2]}`, ecosystem: "java", declared: m[3] });
4050
- }
4051
- }
4052
- return out;
4053
- }
4054
- async function enrichOnline(records, fetchImpl = globalThis.fetch) {
4055
- for (const r of records) {
4056
- if (r.ecosystem !== "npm" || !r.installed) continue;
4057
- try {
4058
- const res = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(r.name)}/latest`);
4059
- if (!res.ok) continue;
4060
- const data = await res.json();
4061
- r.latest = data.version;
4062
- r.drift = classify(r.installed, r.latest);
4063
- } catch {
4064
- r.drift = "unknown";
4065
- }
4066
- }
4067
- }
4068
- function classify(installed, latest) {
4069
- if (!latest) return "unknown";
4070
- const a = installed.split(".").map(Number);
4071
- const b = latest.split(".").map(Number);
4072
- if (a[0] !== b[0]) return "major";
4073
- if (a[1] !== b[1]) return "minor";
4074
- if (a[2] !== b[2]) return "patch";
4075
- 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
+ }
4076
2819
  }
4077
2820
  function discoverModels(home = os.homedir()) {
4078
2821
  return [...ollama(home), ...lmStudio(home), ...looseGguf(home)].sort(
@@ -4080,10 +2823,10 @@ function discoverModels(home = os.homedir()) {
4080
2823
  );
4081
2824
  }
4082
2825
  function ollama(home) {
4083
- const base = path17.join(home, ".ollama", "models", "manifests");
2826
+ const base = path2.join(home, ".ollama", "models", "manifests");
4084
2827
  const out = [];
4085
2828
  walk(base, 4, (file) => {
4086
- const rel2 = path17.relative(base, file).split(path17.sep);
2829
+ const rel2 = path2.relative(base, file).split(path2.sep);
4087
2830
  if (rel2.length >= 2) {
4088
2831
  const tag = rel2[rel2.length - 1];
4089
2832
  const model = rel2[rel2.length - 2];
@@ -4093,24 +2836,24 @@ function ollama(home) {
4093
2836
  return dedupe(out);
4094
2837
  }
4095
2838
  function lmStudio(home) {
4096
- 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")];
4097
2840
  const out = [];
4098
2841
  for (const base of bases) {
4099
2842
  walk(base, 5, (file) => {
4100
2843
  if (!file.endsWith(".gguf")) return;
4101
- const rel2 = path17.relative(base, file).replace(/\\/g, "/");
2844
+ const rel2 = path2.relative(base, file).replace(/\\/g, "/");
4102
2845
  out.push({ runtime: "lm-studio", name: rel2, path: file });
4103
2846
  });
4104
2847
  }
4105
2848
  return dedupe(out);
4106
2849
  }
4107
2850
  function looseGguf(home) {
4108
- const bases = [path17.join(home, "models"), path17.join(home, ".cache", "huggingface")];
2851
+ const bases = [path2.join(home, "models"), path2.join(home, ".cache", "huggingface")];
4109
2852
  const out = [];
4110
2853
  for (const base of bases) {
4111
2854
  walk(base, 4, (file) => {
4112
2855
  if (!file.endsWith(".gguf")) return;
4113
- out.push({ runtime: "gguf", name: path17.basename(file), path: file });
2856
+ out.push({ runtime: "gguf", name: path2.basename(file), path: file });
4114
2857
  });
4115
2858
  }
4116
2859
  return dedupe(out);
@@ -4119,12 +2862,12 @@ function walk(dir, depth, onFile) {
4119
2862
  if (depth < 0) return;
4120
2863
  let entries;
4121
2864
  try {
4122
- entries = fs17.readdirSync(dir, { withFileTypes: true });
2865
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
4123
2866
  } catch {
4124
2867
  return;
4125
2868
  }
4126
2869
  for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
4127
- const abs = path17.join(dir, e.name);
2870
+ const abs = path2.join(dir, e.name);
4128
2871
  if (e.isDirectory()) walk(abs, depth - 1, onFile);
4129
2872
  else if (e.isFile()) onFile(abs);
4130
2873
  }
@@ -4140,457 +2883,11 @@ function dedupe(models) {
4140
2883
  }
4141
2884
  return out;
4142
2885
  }
4143
- function lockfileVersion(root, ecosystem, name) {
4144
- if (ecosystem === "npm") return packageLockVersion(root, name) ?? yarnLockVersion(root, name);
4145
- if (ecosystem === "pypi") return pypiLockVersion(root, name);
4146
- if (ecosystem === "rust") return tomlPackageLock(root, "Cargo.lock", name, rustName);
4147
- if (ecosystem === "ruby") return gemfileLock(root, name);
4148
- if (ecosystem === "php") return composerLock(root, name);
4149
- if (ecosystem === "dotnet") return packagesLock(root, name);
4150
- if (ecosystem === "swift") return packageResolved(root, name);
4151
- if (ecosystem === "dart") return pubspecLock(root, name);
4152
- if (ecosystem === "java") return gradleLock(root, name);
4153
- return void 0;
4154
- }
4155
- function gradleLock(root, name) {
4156
- let text;
4157
- try {
4158
- text = fs17.readFileSync(path17.join(root, "gradle.lockfile"), "utf8");
4159
- } catch {
4160
- return void 0;
4161
- }
4162
- const m = new RegExp(`^${escapeRegExp(name)}:([^=\\s]+)=`, "m").exec(text);
4163
- return m ? m[1] : void 0;
4164
- }
4165
- function packageResolved(root, name) {
4166
- let data;
4167
- try {
4168
- data = JSON.parse(fs17.readFileSync(path17.join(root, "Package.resolved"), "utf8"));
4169
- } catch {
4170
- return void 0;
4171
- }
4172
- const pins = data.pins ?? data.object?.pins;
4173
- if (!Array.isArray(pins)) return void 0;
4174
- const target = name.toLowerCase();
4175
- for (const p of pins) {
4176
- const id = (p.identity ?? p.package ?? "").toLowerCase();
4177
- if (id === target && p.state?.version) return p.state.version;
4178
- }
4179
- return void 0;
4180
- }
4181
- function pubspecLock(root, name) {
4182
- let text;
4183
- try {
4184
- text = fs17.readFileSync(path17.join(root, "pubspec.lock"), "utf8");
4185
- } catch {
4186
- return void 0;
4187
- }
4188
- let inPkg = false;
4189
- for (const line of text.split("\n")) {
4190
- const key = /^ ([A-Za-z0-9_.]+):\s*$/.exec(line);
4191
- if (key) {
4192
- inPkg = key[1] === name;
4193
- continue;
4194
- }
4195
- if (inPkg) {
4196
- const vm = /^\s+version:\s*"?([^"\s]+)"?/.exec(line);
4197
- if (vm) return vm[1];
4198
- }
4199
- }
4200
- return void 0;
4201
- }
4202
- function escapeRegExp(s) {
4203
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4204
- }
4205
- function gemfileLock(root, name) {
4206
- let text;
4207
- try {
4208
- text = fs17.readFileSync(path17.join(root, "Gemfile.lock"), "utf8");
4209
- } catch {
4210
- return void 0;
4211
- }
4212
- const m = new RegExp(`^ ${escapeRegExp(name)} \\((\\d[^)]*)\\)`, "m").exec(text);
4213
- return m ? m[1] : void 0;
4214
- }
4215
- function composerLock(root, name) {
4216
- let data;
4217
- try {
4218
- data = JSON.parse(fs17.readFileSync(path17.join(root, "composer.lock"), "utf8"));
4219
- } catch {
4220
- return void 0;
4221
- }
4222
- for (const section of ["packages", "packages-dev"]) {
4223
- const arr = data[section];
4224
- if (!Array.isArray(arr)) continue;
4225
- const p = arr.find((x) => x && x.name === name);
4226
- if (p && typeof p.version === "string") return p.version.replace(/^v/, "");
4227
- }
4228
- return void 0;
4229
- }
4230
- function packagesLock(root, name) {
4231
- let data;
4232
- try {
4233
- data = JSON.parse(fs17.readFileSync(path17.join(root, "packages.lock.json"), "utf8"));
4234
- } catch {
4235
- return void 0;
4236
- }
4237
- const target = name.toLowerCase();
4238
- for (const fw of Object.values(data.dependencies ?? {})) {
4239
- if (!fw || typeof fw !== "object") continue;
4240
- for (const [id, v] of Object.entries(fw)) {
4241
- if (id.toLowerCase() === target && v && typeof v.resolved === "string") return v.resolved;
4242
- }
4243
- }
4244
- return void 0;
4245
- }
4246
- function pep503(name) {
4247
- return name.trim().toLowerCase().replace(/[-_.]+/g, "-");
4248
- }
4249
- function rustName(name) {
4250
- return name.trim().toLowerCase();
4251
- }
4252
- function pypiLockVersion(root, name) {
4253
- return tomlPackageLock(root, "poetry.lock", name, pep503) ?? tomlPackageLock(root, "uv.lock", name, pep503) ?? pipfileLock(root, name);
4254
- }
4255
- function tomlPackageLock(root, file, name, normalize3) {
4256
- let text;
4257
- try {
4258
- text = fs17.readFileSync(path17.join(root, file), "utf8");
4259
- } catch {
4260
- return void 0;
4261
- }
4262
- const target = normalize3(name);
4263
- for (const block of text.split(/\[\[package\]\]/)) {
4264
- const nm = /(?:^|\n)\s*name\s*=\s*"([^"]+)"/.exec(block);
4265
- if (nm && normalize3(nm[1]) === target) {
4266
- const ver = /(?:^|\n)\s*version\s*=\s*"([^"]+)"/.exec(block);
4267
- if (ver) return ver[1];
4268
- }
4269
- }
4270
- return void 0;
4271
- }
4272
- function pipfileLock(root, name) {
4273
- let data;
4274
- try {
4275
- data = JSON.parse(fs17.readFileSync(path17.join(root, "Pipfile.lock"), "utf8"));
4276
- } catch {
4277
- return void 0;
4278
- }
4279
- const target = pep503(name);
4280
- for (const section of ["default", "develop"]) {
4281
- const deps = data[section];
4282
- if (!deps || typeof deps !== "object") continue;
4283
- for (const [k, v] of Object.entries(deps)) {
4284
- if (pep503(k) === target && v && typeof v.version === "string") {
4285
- return v.version.replace(/^==/, "");
4286
- }
4287
- }
4288
- }
4289
- return void 0;
4290
- }
4291
- function packageLockVersion(root, name) {
4292
- let data;
4293
- try {
4294
- data = JSON.parse(fs17.readFileSync(path17.join(root, "package-lock.json"), "utf8"));
4295
- } catch {
4296
- return void 0;
4297
- }
4298
- const top = data.packages?.[`node_modules/${name}`]?.version;
4299
- if (typeof top === "string") return top;
4300
- const v1 = data.dependencies?.[name]?.version;
4301
- return typeof v1 === "string" ? v1 : void 0;
4302
- }
4303
- function yarnLockVersion(root, name) {
4304
- let text;
4305
- try {
4306
- text = fs17.readFileSync(path17.join(root, "yarn.lock"), "utf8");
4307
- } catch {
4308
- return void 0;
4309
- }
4310
- let inBlock = false;
4311
- for (const line of text.split("\n")) {
4312
- if (line && !/^\s/.test(line) && !line.startsWith("#")) {
4313
- inBlock = headerNamesPackage(line, name);
4314
- } else if (inBlock) {
4315
- const m = /^\s+version:?\s+"?([^"\s]+)"?/.exec(line);
4316
- if (m) return m[1];
4317
- }
4318
- }
4319
- return void 0;
4320
- }
4321
- function headerNamesPackage(header, name) {
4322
- return header.replace(/:\s*$/, "").split(",").some((spec) => {
4323
- const s = spec.trim().replace(/^"|"$/g, "");
4324
- const at = s.lastIndexOf("@");
4325
- if (at <= 0) return false;
4326
- const specName = s.slice(0, at);
4327
- return specName === name;
4328
- });
4329
- }
4330
-
4331
- // src/engine/lib.ts
4332
- var LIB_SCHEMA = "vg-lib/1.0";
4333
- function catalogPath(root) {
4334
- return path17.join(root, "vibgrate.lib.json");
4335
- }
4336
- function libDir(root) {
4337
- return path17.join(root, ".vibgrate", "lib");
4338
- }
4339
- function libId(name) {
4340
- return name.trim().toLowerCase().replace(/^@/, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
4341
- }
4342
- function loadCatalog(root) {
4343
- const file = catalogPath(root);
4344
- if (fs17.existsSync(file)) {
4345
- try {
4346
- const data = JSON.parse(fs17.readFileSync(file, "utf8"));
4347
- if (data.schemaVersion === LIB_SCHEMA && data.libraries) return data;
4348
- } catch {
4349
- }
4350
- }
4351
- return { schemaVersion: LIB_SCHEMA, libraries: {} };
4352
- }
4353
- function saveCatalog(root, catalog) {
4354
- fs17.writeFileSync(catalogPath(root), `${stableStringify(catalog, 2)}
4355
- `);
4356
- }
4357
- function resolveLib(catalog, name) {
4358
- const id = libId(name);
4359
- if (catalog.libraries[id]) return catalog.libraries[id];
4360
- const lower = name.toLowerCase();
4361
- const all = Object.values(catalog.libraries);
4362
- return all.find((e) => e.name.toLowerCase() === lower) ?? all.find((e) => e.id.includes(id) || e.name.toLowerCase().includes(lower));
4363
- }
4364
- function installedVersion(root, name) {
4365
- const rec = inventory(root).records.find((r) => r.name === name);
4366
- return rec?.installed ?? rec?.declared;
4367
- }
4368
- var stripRange = (v) => v?.replace(/^[v^~>=<\s]+/, "");
4369
- function resolveVersion(root, name) {
4370
- const rec = inventory(root).records.find((r) => r.name === name);
4371
- const declared = rec?.declared;
4372
- const installed = rec?.installed ?? (rec ? installedTreeVersion(root, rec.ecosystem, name) : void 0);
4373
- const lockfile = rec ? lockfileVersion(root, rec.ecosystem, name) : void 0;
4374
- let mismatch;
4375
- if (lockfile && installed && stripRange(lockfile) !== stripRange(installed)) {
4376
- mismatch = {
4377
- lockfile,
4378
- installed,
4379
- 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)`
4380
- };
4381
- }
4382
- const served = lockfile ?? installed ?? declared;
4383
- const source = lockfile ? "lockfile" : installed ? "installed" : declared ? "declared" : "unknown";
4384
- return { served, source, lockfile, installed, declared, mismatch };
4385
- }
4386
- function driftFor(root, entry) {
4387
- const installed = installedVersion(root, entry.name);
4388
- if (!installed || entry.version === "*") return { cataloged: entry.version, installed, drift: "unknown" };
4389
- const norm = (v) => v.replace(/^[v^~>=<\s]+/, "");
4390
- const a = norm(entry.version);
4391
- const b = norm(installed);
4392
- if (a === b) return { cataloged: entry.version, installed, drift: "current" };
4393
- return { cataloged: entry.version, installed, drift: cmp(a, b) < 0 ? "behind" : "ahead" };
4394
- }
4395
- function cmp(a, b) {
4396
- const pa = a.split(".").map(Number);
4397
- const pb = b.split(".").map(Number);
4398
- for (let i = 0; i < 3; i++) {
4399
- const d = (pa[i] || 0) - (pb[i] || 0);
4400
- if (d) return d;
4401
- }
4402
- return 0;
4403
- }
4404
- function parseGitSource(source) {
4405
- if (source.startsWith("git+")) return source.slice(4);
4406
- if (/^git@[^:]+:/.test(source)) return source;
4407
- if (/\.git\/?$/.test(source)) return source;
4408
- return null;
4409
- }
4410
- async function addLibrary(source, opts) {
4411
- const { root } = opts;
4412
- let content;
4413
- let type = "local";
4414
- const gitUrl = parseGitSource(source);
4415
- if (gitUrl) {
4416
- if (!opts.allowNetwork) {
4417
- throw new Error(`refusing to clone ${source} without --online (offline by default)`);
4418
- }
4419
- content = cloneAndReadGit(gitUrl);
4420
- type = "git";
4421
- } else if (/^https?:\/\//.test(source)) {
4422
- if (!opts.allowNetwork) {
4423
- throw new Error(`refusing to fetch ${source} without --online (offline by default)`);
4424
- }
4425
- const res = await (opts.fetchImpl ?? globalThis.fetch)(source);
4426
- if (!res.ok) throw new Error(`fetch failed (${res.status}) for ${source}`);
4427
- content = await res.text();
4428
- type = source.endsWith("llms.txt") ? "llms.txt" : source.endsWith(".json") ? "openapi" : "website";
4429
- } else {
4430
- const abs = path17.resolve(root, source);
4431
- if (!fs17.existsSync(abs)) throw new Error(`source not found: ${source}`);
4432
- content = readLocal(abs);
4433
- type = "local";
4434
- }
4435
- const name = opts.name ?? inferName(source);
4436
- const id = libId(name);
4437
- const version = opts.version ?? installedVersion(root, name) ?? "*";
4438
- fs17.mkdirSync(libDir(root), { recursive: true });
4439
- const docFileAbs = path17.join(libDir(root), `${id}.md`);
4440
- fs17.writeFileSync(docFileAbs, content);
4441
- const entry = {
4442
- id,
4443
- name,
4444
- version,
4445
- source: { type, location: source },
4446
- docFile: path17.relative(root, docFileAbs).split(path17.sep).join("/"),
4447
- docHash: hashString(content),
4448
- bytes: Buffer.byteLength(content, "utf8")
4449
- };
4450
- const catalog = loadCatalog(root);
4451
- catalog.libraries[id] = entry;
4452
- saveCatalog(root, catalog);
4453
- return entry;
4454
- }
4455
- function readDoc(root, entry) {
4456
- const abs = path17.resolve(root, entry.docFile);
4457
- return fs17.existsSync(abs) ? fs17.readFileSync(abs, "utf8") : "";
4458
- }
4459
- function npmPackageDir(root, name) {
4460
- let cur = root;
4461
- for (let i = 0; i < 12; i++) {
4462
- const dir = path17.join(cur, "node_modules", name);
4463
- if (fs17.existsSync(path17.join(dir, "package.json"))) return dir;
4464
- const parent = path17.dirname(cur);
4465
- if (parent === cur) break;
4466
- cur = parent;
4467
- }
4468
- return void 0;
4469
- }
4470
- function localPackageDocs(root, name) {
4471
- const dir = npmPackageDir(root, name);
4472
- if (!dir) return void 0;
4473
- const version = resolveVersion(root, name).served;
4474
- for (const f of ["llms.txt", "README.md", "README.mdx", "README", "readme.md"]) {
4475
- const p = path17.join(dir, f);
4476
- try {
4477
- const docs = fs17.readFileSync(p, "utf8");
4478
- if (docs.trim()) {
4479
- return { docs, version, source: path17.relative(root, p).split(path17.sep).join("/") };
4480
- }
4481
- } catch {
4482
- }
4483
- }
4484
- try {
4485
- const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
4486
- if (pkg.description?.trim()) {
4487
- return {
4488
- docs: `# ${name}
4489
-
4490
- ${pkg.description}`,
4491
- version: version ?? pkg.version,
4492
- source: path17.relative(root, path17.join(dir, "package.json")).split(path17.sep).join("/")
4493
- };
4494
- }
4495
- } catch {
4496
- }
4497
- return void 0;
4498
- }
4499
- function dtsEntry(dir) {
4500
- try {
4501
- const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
4502
- const rel2 = pkg.types ?? pkg.typings;
4503
- if (typeof rel2 === "string") {
4504
- const p = path17.join(dir, rel2);
4505
- if (fs17.existsSync(p)) return p;
4506
- }
4507
- } catch {
4508
- }
4509
- for (const c of ["index.d.ts", "dist/index.d.ts", "lib/index.d.ts", "types/index.d.ts"]) {
4510
- const p = path17.join(dir, c);
4511
- if (fs17.existsSync(p)) return p;
4512
- }
4513
- return void 0;
4514
- }
4515
- function extractDtsApi(src, maxDecls = 200) {
4516
- const out = [];
4517
- let depth = 0;
4518
- let capture = null;
4519
- for (const raw of src.split("\n")) {
4520
- const line = raw.trim();
4521
- if (capture === null && depth === 0 && /^export\b/.test(line) && !/^export\s+(\{|\*|default\b|type\s+\{)/.test(line)) {
4522
- capture = [];
4523
- }
4524
- if (capture) capture.push(raw);
4525
- for (const ch of raw) {
4526
- if (ch === "{") depth++;
4527
- else if (ch === "}") depth = Math.max(0, depth - 1);
4528
- }
4529
- if (capture && depth === 0 && /[;}]\s*$/.test(line)) {
4530
- out.push(capture.join("\n").trim());
4531
- capture = null;
4532
- if (out.length >= maxDecls) break;
4533
- }
4534
- }
4535
- return out;
4536
- }
4537
- function localApiSurface(root, name) {
4538
- const dir = npmPackageDir(root, name);
4539
- if (!dir) return void 0;
4540
- const dts = dtsEntry(dir);
4541
- if (!dts) return void 0;
4542
- let src;
4543
- try {
4544
- src = fs17.readFileSync(dts, "utf8");
4545
- } catch {
4546
- return void 0;
4547
- }
4548
- const decls = extractDtsApi(src);
4549
- return decls.length ? decls.join("\n\n") : void 0;
4550
- }
4551
- function cloneAndReadGit(url) {
4552
- const dir = fs17.mkdtempSync(path17.join(os.tmpdir(), "vg-lib-git-"));
4553
- try {
4554
- execFileSync("git", ["clone", "--depth", "1", "--quiet", url, dir], {
4555
- stdio: ["ignore", "ignore", "pipe"]
4556
- });
4557
- } catch (err) {
4558
- fs17.rmSync(dir, { recursive: true, force: true });
4559
- const detail = err instanceof Error && err.message ? `: ${err.message.split("\n")[0]}` : "";
4560
- throw new Error(`git clone failed for ${url}${detail}`);
4561
- }
4562
- try {
4563
- return readGitDocs(dir);
4564
- } finally {
4565
- fs17.rmSync(dir, { recursive: true, force: true });
4566
- }
4567
- }
4568
- function readGitDocs(dir) {
4569
- const docsDir = path17.join(dir, "docs");
4570
- if (fs17.existsSync(docsDir) && fs17.statSync(docsDir).isDirectory()) return readLocal(docsDir);
4571
- const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) => path17.join(dir, f)).find((p) => fs17.existsSync(p));
4572
- if (readme) return fs17.readFileSync(readme, "utf8");
4573
- return readLocal(dir);
4574
- }
4575
- function readLocal(abs) {
4576
- const stat = fs17.statSync(abs);
4577
- if (stat.isFile()) return fs17.readFileSync(abs, "utf8");
4578
- const parts = [];
4579
- const files = fs17.readdirSync(abs, { withFileTypes: true }).filter((e) => e.isFile() && /\.(md|mdx|txt|rst)$/i.test(e.name)).map((e) => e.name).sort();
4580
- for (const f of files) parts.push(`<!-- ${f} -->
4581
- ${fs17.readFileSync(path17.join(abs, f), "utf8")}`);
4582
- return parts.join("\n\n");
4583
- }
4584
- function inferName(source) {
4585
- const cleaned = source.replace(/^git\+/, "").replace(/\.git\/?$/i, "");
4586
- const base = cleaned.replace(/\/+$/, "").split(/[/:]/).pop() ?? cleaned;
4587
- return base.replace(/\.(md|mdx|txt|rst|json)$/i, "").replace(/^llms$/, "docs");
4588
- }
4589
2886
  function findGitRoot(startDir = process.cwd()) {
4590
- let dir = path17.resolve(startDir);
2887
+ let dir = path2.resolve(startDir);
4591
2888
  for (; ; ) {
4592
- if (fs17.existsSync(path17.join(dir, ".git"))) return dir;
4593
- const parent = path17.dirname(dir);
2889
+ if (fs18.existsSync(path2.join(dir, ".git"))) return dir;
2890
+ const parent = path2.dirname(dir);
4594
2891
  if (parent === dir) return null;
4595
2892
  dir = parent;
4596
2893
  }
@@ -4601,19 +2898,19 @@ function normalizeEntry(line) {
4601
2898
  function ensureGitignored(entry, startDir = process.cwd()) {
4602
2899
  const root = findGitRoot(startDir);
4603
2900
  if (!root) return { status: "not-a-repo", entry };
4604
- const gitignorePath = path17.join(root, ".gitignore");
2901
+ const gitignorePath = path2.join(root, ".gitignore");
4605
2902
  const target = normalizeEntry(entry);
4606
2903
  let existing = "";
4607
2904
  let fileExisted = true;
4608
2905
  try {
4609
- existing = fs17.readFileSync(gitignorePath, "utf8");
2906
+ existing = fs18.readFileSync(gitignorePath, "utf8");
4610
2907
  } catch {
4611
2908
  fileExisted = false;
4612
2909
  }
4613
2910
  const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
4614
2911
  if (alreadyPresent) return { status: "present", entry, gitignorePath };
4615
2912
  const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
4616
- fs17.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
2913
+ fs18.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
4617
2914
  `, "utf8");
4618
2915
  return { status: fileExisted ? "added" : "created", entry, gitignorePath };
4619
2916
  }
@@ -4767,14 +3064,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
4767
3064
  console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
4768
3065
  }
4769
3066
  if (opts.write) {
4770
- const writePath = path17.resolve(opts.write);
3067
+ const writePath = path2.resolve(opts.write);
4771
3068
  await writeTextFile(writePath, dsn + "\n");
4772
3069
  console.log("");
4773
3070
  console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
4774
- const root = findGitRoot(path17.dirname(writePath));
4775
- 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("/") : "";
4776
3073
  if (root && rel2 && !rel2.startsWith("..")) {
4777
- const res = ensureGitignored(rel2, path17.dirname(writePath));
3074
+ const res = ensureGitignored(rel2, path2.dirname(writePath));
4778
3075
  if (res.status === "created") {
4779
3076
  console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
4780
3077
  } else if (res.status === "added") {
@@ -4792,30 +3089,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
4792
3089
  var STORE_DIRNAME = ".vibgrate";
4793
3090
  var STORE_FILENAME = "credentials.json";
4794
3091
  function homeCredentialsPath() {
4795
- return path17.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
3092
+ return path2.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
4796
3093
  }
4797
3094
  function projectCredentialsPath(cwd = process.cwd()) {
4798
3095
  const root = findGitRoot(cwd) ?? cwd;
4799
- return path17.join(root, STORE_DIRNAME, STORE_FILENAME);
3096
+ return path2.join(root, STORE_DIRNAME, STORE_FILENAME);
4800
3097
  }
4801
3098
  function credentialsPath(opts = {}) {
4802
3099
  const override = process.env.VIBGRATE_CREDENTIALS?.trim();
4803
- if (override) return path17.resolve(override);
3100
+ if (override) return path2.resolve(override);
4804
3101
  const local = projectCredentialsPath(opts.cwd);
4805
3102
  if (opts.local) return local;
4806
- if (fs17.existsSync(local)) return local;
3103
+ if (fs18.existsSync(local)) return local;
4807
3104
  return homeCredentialsPath();
4808
3105
  }
4809
3106
  function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
4810
- const rel2 = path17.relative(repoRoot, credsFile);
4811
- if (rel2 && !rel2.startsWith("..") && !path17.isAbsolute(rel2)) {
4812
- 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("/");
4813
3110
  }
4814
3111
  return `${STORE_DIRNAME}/${STORE_FILENAME}`;
4815
3112
  }
4816
3113
  function readStoredCredentials(opts = {}) {
4817
3114
  try {
4818
- const raw = fs17.readFileSync(credentialsPath(opts), "utf8");
3115
+ const raw = fs18.readFileSync(credentialsPath(opts), "utf8");
4819
3116
  const parsed = JSON.parse(raw);
4820
3117
  return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
4821
3118
  } catch {
@@ -4824,16 +3121,16 @@ function readStoredCredentials(opts = {}) {
4824
3121
  }
4825
3122
  function writeStoredCredentials(creds, opts = {}) {
4826
3123
  const file = credentialsPath(opts);
4827
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
4828
- 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 });
4829
3126
  try {
4830
- fs17.chmodSync(file, 384);
3127
+ fs18.chmodSync(file, 384);
4831
3128
  } catch {
4832
3129
  }
4833
3130
  }
4834
3131
  function clearStoredCredentials(opts = {}) {
4835
3132
  try {
4836
- fs17.rmSync(credentialsPath(opts));
3133
+ fs18.rmSync(credentialsPath(opts));
4837
3134
  return true;
4838
3135
  } catch {
4839
3136
  return false;
@@ -5204,7 +3501,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
5204
3501
  if (opts.strict) process.exit(1);
5205
3502
  return;
5206
3503
  }
5207
- const filePath = path17.resolve(opts.file);
3504
+ const filePath = path2.resolve(opts.file);
5208
3505
  if (!await pathExists(filePath)) {
5209
3506
  console.error(chalk.red(`Scan artifact not found: ${filePath}`));
5210
3507
  console.error(chalk.dim('Run "vibgrate scan" first.'));
@@ -5267,8 +3564,8 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
5267
3564
  });
5268
3565
  function readScanArtifact(root) {
5269
3566
  try {
5270
- const file = path17.join(root, ".vibgrate", "scan_result.json");
5271
- 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"));
5272
3569
  } catch {
5273
3570
  return null;
5274
3571
  }
@@ -5373,7 +3670,7 @@ var DEFAULT_SELECTION_WEIGHTS = {
5373
3670
  };
5374
3671
 
5375
3672
  // src/engine/select.ts
5376
- 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"]);
5377
3674
  var USAGE_RE = /\b(usage|example|examples|quick ?start|getting started|api|guide|how to|reference|tutorial|basic)\b/i;
5378
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;
5379
3676
  function splitSections(md) {
@@ -5420,9 +3717,9 @@ function chunkBody(body) {
5420
3717
  function stripNoise(body) {
5421
3718
  return body.split("\n").filter((l) => !/^\s*(?:!\[[^\]]*\]\([^)]*\)\s*)+$/.test(l)).join("\n").trim();
5422
3719
  }
5423
- function tokenizeQuery(q2) {
5424
- if (!q2) return [];
5425
- 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)))];
5426
3723
  }
5427
3724
  function symbolsFromApi(api) {
5428
3725
  if (!api) return [];
@@ -5654,11 +3951,11 @@ async function publishPrivateLibrary(req, opts = {}) {
5654
3951
  }
5655
3952
 
5656
3953
  // src/engine/hosted-cache.ts
5657
- var CACHE_VERSION2 = "vg-hosted-docs/1";
3954
+ var CACHE_VERSION = "vg-hosted-docs/1";
5658
3955
  var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
5659
3956
  var MAX_ENTRIES = 64;
5660
- function cachePath2(root) {
5661
- return path17.join(cacheDir(root), "hosted-docs.json");
3957
+ function cachePath(root) {
3958
+ return path2.join(cacheDir(root), "hosted-docs.json");
5662
3959
  }
5663
3960
  function hostedDocsCacheKey(req, opts = {}) {
5664
3961
  const material = JSON.stringify([
@@ -5674,11 +3971,11 @@ function hostedDocsCacheKey(req, opts = {}) {
5674
3971
  }
5675
3972
  function loadFile(root) {
5676
3973
  try {
5677
- const parsed = JSON.parse(fs17.readFileSync(cachePath2(root), "utf8"));
5678
- 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;
5679
3976
  } catch {
5680
3977
  }
5681
- return { version: CACHE_VERSION2, entries: {} };
3978
+ return { version: CACHE_VERSION, entries: {} };
5682
3979
  }
5683
3980
  function saveFile(root, file) {
5684
3981
  try {
@@ -5688,8 +3985,8 @@ function saveFile(root, file) {
5688
3985
  delete file.entries[k];
5689
3986
  }
5690
3987
  }
5691
- fs17.mkdirSync(cacheDir(root), { recursive: true });
5692
- fs17.writeFileSync(cachePath2(root), JSON.stringify(file));
3988
+ fs18.mkdirSync(cacheDir(root), { recursive: true });
3989
+ fs18.writeFileSync(cachePath(root), JSON.stringify(file));
5693
3990
  } catch {
5694
3991
  }
5695
3992
  }
@@ -5814,7 +4111,7 @@ function measure(root, files) {
5814
4111
  for (const rel2 of files) {
5815
4112
  let size;
5816
4113
  try {
5817
- size = fs17.statSync(path17.join(root, rel2)).size;
4114
+ size = fs18.statSync(path2.join(root, rel2)).size;
5818
4115
  } catch {
5819
4116
  continue;
5820
4117
  }
@@ -5837,9 +4134,9 @@ function matchLines(text, needleLower) {
5837
4134
  }
5838
4135
  function readCandidate(abs, knownSize) {
5839
4136
  try {
5840
- const size = knownSize ?? fs17.statSync(abs).size;
4137
+ const size = knownSize ?? fs18.statSync(abs).size;
5841
4138
  if (size > MAX_FILE_BYTES) return null;
5842
- const text = fs17.readFileSync(abs, "utf8");
4139
+ const text = fs18.readFileSync(abs, "utf8");
5843
4140
  return text.includes(NUL) ? null : text;
5844
4141
  } catch {
5845
4142
  return null;
@@ -5850,7 +4147,7 @@ function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
5850
4147
  let total = 0;
5851
4148
  let truncated = false;
5852
4149
  for (const rel2 of files) {
5853
- const text = readCandidate(path17.join(root, rel2), sizes?.get(rel2));
4150
+ const text = readCandidate(path2.join(root, rel2), sizes?.get(rel2));
5854
4151
  if (text === null) continue;
5855
4152
  for (const h of matchLines(text, needleLower)) {
5856
4153
  total++;
@@ -5917,12 +4214,12 @@ var WORKER_SOURCE = [
5917
4214
  "parentPort.postMessage({ hits, total, truncated });"
5918
4215
  ].join("\n");
5919
4216
  function runWorker(root, files, needleLower) {
5920
- return new Promise((resolve12, reject) => {
4217
+ return new Promise((resolve11, reject) => {
5921
4218
  const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
5922
4219
  let settled = false;
5923
4220
  w.once("message", (m) => {
5924
4221
  settled = true;
5925
- resolve12(m);
4222
+ resolve11(m);
5926
4223
  });
5927
4224
  w.once("error", reject);
5928
4225
  w.once("exit", (code) => {
@@ -5991,26 +4288,26 @@ var BINARY_EXTS = /* @__PURE__ */ new Set([
5991
4288
  ".pdf"
5992
4289
  ]);
5993
4290
  function hasBinaryExt(rel2) {
5994
- return BINARY_EXTS.has(path17.extname(rel2).toLowerCase());
4291
+ return BINARY_EXTS.has(path2.extname(rel2).toLowerCase());
5995
4292
  }
5996
4293
  function toPosix2(rel2) {
5997
- return path17.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
4294
+ return path2.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
5998
4295
  }
5999
4296
  async function searchSymbols(graph, root, query, limit) {
6000
- let q2 = query.trim();
6001
- if (!q2) return { matches: [], moreAvailable: false, hint: "query is required" };
6002
- const quoted = /^(["'`])(.+)\1$/.exec(q2);
6003
- if (quoted && quoted[2].trim()) q2 = quoted[2].trim();
6004
- const isPhrase = Boolean(quoted) || /\s/.test(q2) || /[/\\]/.test(q2) && !q2.includes(":") && !/[*?]/.test(q2);
6005
- 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");
6006
4303
  if (nodes.length === 0) {
6007
- nodes = fileSymbolNodes(graph, q2);
4304
+ nodes = fileSymbolNodes(graph, q);
6008
4305
  }
6009
4306
  if (nodes.length === 0 && isPhrase) {
6010
- nodes = reconstructedIdentifierNodes(graph, q2);
4307
+ nodes = reconstructedIdentifierNodes(graph, q);
6011
4308
  }
6012
4309
  if (nodes.length === 0) {
6013
- const tokens = queryTokens(q2);
4310
+ const tokens = queryTokens(q);
6014
4311
  if (tokens.length >= 2) nodes = multiTokenNodes(graph, tokens);
6015
4312
  }
6016
4313
  const symbolCap = isPhrase ? Math.max(1, Math.floor(limit / 3)) : limit;
@@ -6025,10 +4322,10 @@ async function searchSymbols(graph, root, query, limit) {
6025
4322
  const textHits = [];
6026
4323
  let truncatedScan = false;
6027
4324
  let totalTextMatches;
6028
- const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q2);
4325
+ const skipScan = !isPhrase && hasExactSymbolMatch(nodes, q);
6029
4326
  if (spare > 0 && !skipScan) {
6030
4327
  const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
6031
- const scan = await scanFiles(root, q2, spare, seen, textHits, isPhrase);
4328
+ const scan = await scanFiles(root, q, spare, seen, textHits, isPhrase);
6032
4329
  truncatedScan = scan.truncated;
6033
4330
  if (isPhrase) totalTextMatches = scan.total;
6034
4331
  }
@@ -6049,25 +4346,25 @@ async function searchSymbols(graph, root, query, limit) {
6049
4346
  }
6050
4347
  return result;
6051
4348
  }
6052
- function fileSymbolNodes(graph, q2) {
6053
- if (!/[/\\]/.test(q2) && !/\.\w+$/.test(q2)) return [];
6054
- const norm = q2.replaceAll("\\", "/").replace(/^\.\//, "");
4349
+ function fileSymbolNodes(graph, q) {
4350
+ if (!/[/\\]/.test(q) && !/\.\w+$/.test(q)) return [];
4351
+ const norm = q.replaceAll("\\", "/").replace(/^\.\//, "");
6055
4352
  const inFile = graph.nodes.filter(
6056
4353
  (n) => n.kind !== "file" && (n.file === norm || n.file.endsWith(`/${norm}`))
6057
4354
  );
6058
4355
  return [...inFile].sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName));
6059
4356
  }
6060
- function hasExactSymbolMatch(nodes, q2) {
4357
+ function hasExactSymbolMatch(nodes, q) {
6061
4358
  if (nodes.length === 0) return false;
6062
- if (/^(.*):(\d+)$/.test(q2)) return true;
6063
- const lower = q2.toLowerCase();
6064
- 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);
6065
4362
  }
6066
- function queryTokens(q2) {
6067
- 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))];
6068
4365
  }
6069
- function reconstructedIdentifierNodes(graph, q2) {
6070
- const words = q2.trim().split(/\s+/).filter(Boolean);
4366
+ function reconstructedIdentifierNodes(graph, q) {
4367
+ const words = q.trim().split(/\s+/).filter(Boolean);
6071
4368
  if (words.length < 2) return [];
6072
4369
  const concat = findNodes(graph, words.join(""));
6073
4370
  if (concat.length) return concat.filter((n) => n.kind !== "file");
@@ -6133,13 +4430,13 @@ function walkCandidates(root) {
6133
4430
  const dir = stack.pop();
6134
4431
  let entries;
6135
4432
  try {
6136
- entries = fs17.readdirSync(dir, { withFileTypes: true });
4433
+ entries = fs18.readdirSync(dir, { withFileTypes: true });
6137
4434
  } catch {
6138
4435
  continue;
6139
4436
  }
6140
4437
  for (const e of entries) {
6141
4438
  if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
6142
- const abs = path17.join(dir, e.name);
4439
+ const abs = path2.join(dir, e.name);
6143
4440
  if (e.isDirectory()) {
6144
4441
  stack.push(abs);
6145
4442
  continue;
@@ -6149,7 +4446,7 @@ function walkCandidates(root) {
6149
4446
  truncated = true;
6150
4447
  continue;
6151
4448
  }
6152
- files.push(toPosix2(path17.relative(root, abs)));
4449
+ files.push(toPosix2(path2.relative(root, abs)));
6153
4450
  }
6154
4451
  }
6155
4452
  files.sort();
@@ -6180,11 +4477,11 @@ function ripgrepCandidates(root, needle) {
6180
4477
  if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
6181
4478
  return { files, truncated: false };
6182
4479
  }
6183
- function parseRgFileList(stdout, sep10 = path17.sep) {
4480
+ function parseRgFileList(stdout, sep9 = path2.sep) {
6184
4481
  const seen = /* @__PURE__ */ new Set();
6185
4482
  for (const raw of stdout.split("\n")) {
6186
4483
  if (!raw) continue;
6187
- const posix3 = sep10 === "\\" ? raw.replaceAll("\\", "/") : raw;
4484
+ const posix3 = sep9 === "\\" ? raw.replaceAll("\\", "/") : raw;
6188
4485
  const rel2 = posix3.startsWith("./") ? posix3.slice(2) : posix3;
6189
4486
  if (!rel2) continue;
6190
4487
  const segs = rel2.split("/");
@@ -6217,7 +4514,7 @@ function readyEmbedder(local) {
6217
4514
  async function retrieve(graph, question, budget, ctx) {
6218
4515
  let mode = "lexical";
6219
4516
  try {
6220
- const q2 = await withTimeout(
4517
+ const q = await withTimeout(
6221
4518
  (async () => {
6222
4519
  const embedder = await readyEmbedder(ctx.local);
6223
4520
  if (!embedder) return null;
@@ -6229,7 +4526,7 @@ async function retrieve(graph, question, budget, ctx) {
6229
4526
  SEMANTIC_BUDGET_MS,
6230
4527
  "semantic path over budget"
6231
4528
  );
6232
- if (q2) return { q: q2, mode };
4529
+ if (q) return { q, mode };
6233
4530
  } catch {
6234
4531
  }
6235
4532
  return { q: queryGraph(graph, question, { budget }), mode: "lexical" };
@@ -6278,10 +4575,10 @@ var TOOLS = [
6278
4575
  const question = String(args.question ?? args.query ?? "");
6279
4576
  if (!question) return { error: "bad_request", message: "question is required" };
6280
4577
  const budget = numOr(args.budget, 1500);
6281
- const { q: q2, mode } = await retrieve(graph, question, budget, ctx);
4578
+ const { q, mode } = await retrieve(graph, question, budget, ctx);
6282
4579
  const rawScope = String(args.scope ?? "").trim();
6283
4580
  const scope = rawScope === "." || rawScope === "./" ? "" : rawScope.replace(/^\.\//, "");
6284
- 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;
6285
4582
  const detailed = isDetailed(args);
6286
4583
  let topImpact = null;
6287
4584
  const top = scoped[0]?.node;
@@ -6299,7 +4596,7 @@ var TOOLS = [
6299
4596
  mode,
6300
4597
  // The fact-annotated context block is detail: concise callers navigate
6301
4598
  // by the ranked matches and fetch nodes on demand (plan P2).
6302
- ...detailed ? { context: q2.context } : {},
4599
+ ...detailed ? { context: q.context } : {},
6303
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 })),
6304
4601
  // A neutral availability flag, not a directive to keep querying: a
6305
4602
  // retrieval tool that coaches "narrow the question / see more" nudges the
@@ -7078,12 +5375,12 @@ function detectAssistants(root, opts = {}) {
7078
5375
  const which = opts.which ?? whichOnPath;
7079
5376
  const found = [];
7080
5377
  for (const assistant of ASSISTANTS) {
7081
- 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)));
7082
5379
  if (repoHit) {
7083
5380
  found.push({ assistant, via: "repo", marker: repoHit });
7084
5381
  continue;
7085
5382
  }
7086
- 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)));
7087
5384
  if (homeHit) {
7088
5385
  found.push({ assistant, via: "home", marker: homeHit });
7089
5386
  continue;
@@ -7115,19 +5412,19 @@ function installAssistant(a, opts) {
7115
5412
  const skipped = [];
7116
5413
  let note;
7117
5414
  if (a.skill) {
7118
- writeFileEnsured(path17.join(opts.root, a.skill), skillMarkdown(a.id));
5415
+ writeFileEnsured(path2.join(opts.root, a.skill), skillMarkdown(a.id));
7119
5416
  wrote.push(a.skill);
7120
5417
  }
7121
5418
  if (a.mcp) {
7122
5419
  const launch = opts.launch ?? detectServeLaunch();
7123
- upsertMcp(path17.join(opts.root, a.mcp.file), a.mcp, launch);
5420
+ upsertMcp(path2.join(opts.root, a.mcp.file), a.mcp, launch);
7124
5421
  wrote.push(a.mcp.file);
7125
5422
  note = launch.note;
7126
5423
  } else {
7127
5424
  skipped.push("mcp (host-specific setup)");
7128
5425
  }
7129
5426
  if (a.nudge && opts.hook !== false) {
7130
- 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);
7131
5428
  wrote.push(a.nudge.file);
7132
5429
  } else if (a.nudge) {
7133
5430
  skipped.push("nudge (--no-hook)");
@@ -7137,41 +5434,41 @@ function installAssistant(a, opts) {
7137
5434
  function uninstallAssistant(a, root, purge) {
7138
5435
  const removed = [];
7139
5436
  if (a.mcp) {
7140
- const file = path17.join(root, a.mcp.file);
5437
+ const file = path2.join(root, a.mcp.file);
7141
5438
  if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
7142
5439
  }
7143
5440
  if (a.nudge) {
7144
- const file = path17.join(root, a.nudge.file);
5441
+ const file = path2.join(root, a.nudge.file);
7145
5442
  if (a.nudge.kind === "block") {
7146
5443
  if (removeBlock(file)) removed.push(a.nudge.file);
7147
- } else if (fs17.existsSync(file)) {
7148
- fs17.rmSync(file);
5444
+ } else if (fs18.existsSync(file)) {
5445
+ fs18.rmSync(file);
7149
5446
  removed.push(a.nudge.file);
7150
5447
  }
7151
5448
  }
7152
5449
  if (purge && a.skill) {
7153
- const file = path17.join(root, a.skill);
7154
- if (fs17.existsSync(file)) {
7155
- fs17.rmSync(file);
5450
+ const file = path2.join(root, a.skill);
5451
+ if (fs18.existsSync(file)) {
5452
+ fs18.rmSync(file);
7156
5453
  removed.push(a.skill);
7157
5454
  }
7158
5455
  }
7159
5456
  return removed;
7160
5457
  }
7161
5458
  function writeFileEnsured(file, content) {
7162
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
7163
- fs17.writeFileSync(file, content);
5459
+ fs18.mkdirSync(path2.dirname(file), { recursive: true });
5460
+ fs18.writeFileSync(file, content);
7164
5461
  }
7165
5462
  function writeNavigationConfig(root) {
7166
5463
  ensureVibgrateGitignore(root);
7167
- const rel2 = path17.join(".vibgrate", "mcp-navigation.json");
5464
+ const rel2 = path2.join(".vibgrate", "mcp-navigation.json");
7168
5465
  const doc = {
7169
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.",
7170
5467
  hot_core: [...HOT_TOOLS],
7171
5468
  deferred: deferredToolNames(),
7172
5469
  toolset: navigationToolsetConfig()
7173
5470
  };
7174
- writeFileEnsured(path17.join(root, rel2), `${JSON.stringify(doc, null, 2)}
5471
+ writeFileEnsured(path2.join(root, rel2), `${JSON.stringify(doc, null, 2)}
7175
5472
  `);
7176
5473
  return rel2;
7177
5474
  }
@@ -7186,7 +5483,7 @@ function upsertMcp(file, target, launch) {
7186
5483
  `);
7187
5484
  }
7188
5485
  function removeMcp(file, target) {
7189
- if (!fs17.existsSync(file)) return false;
5486
+ if (!fs18.existsSync(file)) return false;
7190
5487
  const config = readJson(file);
7191
5488
  const bag = config[target.key];
7192
5489
  if (!bag || !(bag.vg !== void 0)) return false;
@@ -7205,8 +5502,8 @@ function refreshInstalledInstructions(root, smallRepo) {
7205
5502
  };
7206
5503
  for (const a of ASSISTANTS) {
7207
5504
  if (a.skill) {
7208
- const file = path17.join(root, a.skill);
7209
- if (fs17.existsSync(file)) {
5505
+ const file = path2.join(root, a.skill);
5506
+ if (fs18.existsSync(file)) {
7210
5507
  const text = readTextSafe(file);
7211
5508
  const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
7212
5509
  if (from !== null) {
@@ -7216,8 +5513,8 @@ function refreshInstalledInstructions(root, smallRepo) {
7216
5513
  }
7217
5514
  }
7218
5515
  if (a.nudge) {
7219
- const file = path17.join(root, a.nudge.file);
7220
- if (!fs17.existsSync(file)) continue;
5516
+ const file = path2.join(root, a.nudge.file);
5517
+ if (!fs18.existsSync(file)) continue;
7221
5518
  const text = readTextSafe(file);
7222
5519
  if (text === null) continue;
7223
5520
  if (a.nudge.kind === "block") {
@@ -7241,7 +5538,7 @@ function refreshInstalledInstructions(root, smallRepo) {
7241
5538
  }
7242
5539
  function readTextSafe(file) {
7243
5540
  try {
7244
- return fs17.readFileSync(file, "utf8");
5541
+ return fs18.readFileSync(file, "utf8");
7245
5542
  } catch {
7246
5543
  return null;
7247
5544
  }
@@ -7260,8 +5557,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
7260
5557
  upsertBlock(file, nudgeMarkdown(smallRepo, client));
7261
5558
  }
7262
5559
  function upsertBlock(file, block) {
7263
- fs17.mkdirSync(path17.dirname(file), { recursive: true });
7264
- 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") : "";
7265
5562
  const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
7266
5563
  if (re.test(existing)) existing = existing.replace(re, block);
7267
5564
  else existing = existing.length ? `${existing.replace(/\s*$/, "")}
@@ -7269,20 +5566,20 @@ function upsertBlock(file, block) {
7269
5566
  ${block}
7270
5567
  ` : `${block}
7271
5568
  `;
7272
- fs17.writeFileSync(file, existing);
5569
+ fs18.writeFileSync(file, existing);
7273
5570
  }
7274
5571
  function removeBlock(file) {
7275
- if (!fs17.existsSync(file)) return false;
7276
- const existing = fs17.readFileSync(file, "utf8");
5572
+ if (!fs18.existsSync(file)) return false;
5573
+ const existing = fs18.readFileSync(file, "utf8");
7277
5574
  const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
7278
5575
  if (!re.test(existing)) return false;
7279
- fs17.writeFileSync(file, existing.replace(re, "\n"));
5576
+ fs18.writeFileSync(file, existing.replace(re, "\n"));
7280
5577
  return true;
7281
5578
  }
7282
5579
  function readJson(file) {
7283
- if (!fs17.existsSync(file)) return {};
5580
+ if (!fs18.existsSync(file)) return {};
7284
5581
  try {
7285
- const parsed = JSON.parse(fs17.readFileSync(file, "utf8"));
5582
+ const parsed = JSON.parse(fs18.readFileSync(file, "utf8"));
7286
5583
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7287
5584
  return parsed;
7288
5585
  }
@@ -7302,7 +5599,7 @@ function escapeRe(s) {
7302
5599
  }
7303
5600
  var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
7304
5601
  function refreshLockPath(root) {
7305
- return path17.join(cacheDir(root), "refresh.lock");
5602
+ return path2.join(cacheDir(root), "refresh.lock");
7306
5603
  }
7307
5604
  async function refreshIfStale(root, opts = {}) {
7308
5605
  const probe = probeFreshness(root);
@@ -7333,8 +5630,8 @@ async function refreshIfStale(root, opts = {}) {
7333
5630
  const dir = vibgrateDir(root);
7334
5631
  writeArtifacts(result.graph, {
7335
5632
  root,
7336
- report: fs17.existsSync(path17.join(dir, "GRAPH_REPORT.md")),
7337
- 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"))
7338
5635
  });
7339
5636
  }
7340
5637
  writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
@@ -7353,583 +5650,7 @@ async function refreshIfStale(root, opts = {}) {
7353
5650
  releaseLock(lock);
7354
5651
  }
7355
5652
  }
7356
- var LEDGER = "savings.jsonl";
7357
- var PER_FILE_TOKENS = 400;
7358
- var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node", "search_symbols"]);
7359
- var CLI_TOOL_ALIASES = {
7360
- ask: "query_graph",
7361
- show: "get_node",
7362
- impact: "impact_of",
7363
- path: "find_path",
7364
- hubs: "list_hubs",
7365
- areas: "list_areas",
7366
- tree: "tree"
7367
- };
7368
- function sanitizeClient(name) {
7369
- if (typeof name !== "string") return "unknown";
7370
- const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
7371
- return cleaned || "unknown";
7372
- }
7373
- function recordCliCall(root, entry, now) {
7374
- recordSaving(
7375
- root,
7376
- {
7377
- tool: entry.tool,
7378
- source: "cli",
7379
- client: sanitizeClient(entry.client),
7380
- outcome: entry.outcome,
7381
- vgTokens: entry.vgTokens ?? 0,
7382
- baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
7383
- },
7384
- now
7385
- );
7386
- }
7387
- function savingsLedgerPath(root) {
7388
- return path17.join(cacheDir(root), LEDGER);
7389
- }
7390
- function ledgerPath(root) {
7391
- return savingsLedgerPath(root);
7392
- }
7393
- function savingsRecorded(root) {
7394
- return fs17.existsSync(ledgerPath(root));
7395
- }
7396
- function clearSavings(root) {
7397
- const file = ledgerPath(root);
7398
- const existed = fs17.existsSync(file);
7399
- fs17.rmSync(file, { force: true });
7400
- fs17.rmSync(path17.join(cacheDir(root), "stats-share.json"), { force: true });
7401
- return existed;
7402
- }
7403
- function recordSaving(root, entry, now) {
7404
- try {
7405
- fs17.mkdirSync(cacheDir(root), { recursive: true });
7406
- const line = JSON.stringify({ ts: now, ...entry });
7407
- fs17.appendFileSync(ledgerPath(root), line + "\n");
7408
- } catch {
7409
- }
7410
- }
7411
- var DEFAULT_RATE_PER_M = 3;
7412
- var DEFAULT_RATE_LABEL = "input @ $3/1M";
7413
- function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
7414
- const file = ledgerPath(root);
7415
- const cutoff = now - days * 24 * 60 * 60 * 1e3;
7416
- let queries = 0;
7417
- let vgTokens = 0;
7418
- let baselineTokens = 0;
7419
- if (fs17.existsSync(file)) {
7420
- for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
7421
- if (!line.trim()) continue;
7422
- try {
7423
- const e = JSON.parse(line);
7424
- if (e.ts < cutoff) continue;
7425
- if (!SAVINGS_TOOLS.has(e.tool)) continue;
7426
- queries++;
7427
- vgTokens += e.vgTokens;
7428
- baselineTokens += e.baselineTokens;
7429
- } catch {
7430
- }
7431
- }
7432
- }
7433
- const estCostVg = vgTokens / 1e6 * ratePerM;
7434
- const estCostBaseline = baselineTokens / 1e6 * ratePerM;
7435
- return {
7436
- enabled: savingsRecorded(root),
7437
- days,
7438
- queries,
7439
- vgTokens,
7440
- baselineTokens,
7441
- ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
7442
- estCostVg: round22(estCostVg),
7443
- estCostBaseline: round22(estCostBaseline),
7444
- saved: round22(estCostBaseline - estCostVg),
7445
- rateLabel: DEFAULT_RATE_LABEL
7446
- };
7447
- }
7448
- function round22(x) {
7449
- return Math.round(x * 100) / 100;
7450
- }
7451
- function toDimensionStats(byKey) {
7452
- return [...byKey.entries()].map(([key, r]) => {
7453
- const calls = r.complete + r.partial + r.miss;
7454
- return {
7455
- key,
7456
- calls,
7457
- complete: r.complete,
7458
- partial: r.partial,
7459
- miss: r.miss,
7460
- successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
7461
- };
7462
- }).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
7463
- }
7464
- function readUsage(root, days, now) {
7465
- const file = ledgerPath(root);
7466
- const cutoff = now - days * 24 * 60 * 60 * 1e3;
7467
- const byTool = /* @__PURE__ */ new Map();
7468
- const bySource = /* @__PURE__ */ new Map();
7469
- const byClient = /* @__PURE__ */ new Map();
7470
- const msByTool = /* @__PURE__ */ new Map();
7471
- const bump2 = (m, key, outcome) => {
7472
- const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
7473
- row[outcome]++;
7474
- m.set(key, row);
7475
- };
7476
- if (fs17.existsSync(file)) {
7477
- for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
7478
- if (!line.trim()) continue;
7479
- try {
7480
- const e = JSON.parse(line);
7481
- if (e.ts < cutoff) continue;
7482
- const outcome = e.outcome ?? "complete";
7483
- bump2(byTool, e.tool, outcome);
7484
- bump2(bySource, e.source ?? "mcp", outcome);
7485
- bump2(byClient, e.client ?? "unknown", outcome);
7486
- if (typeof e.ms === "number" && e.ms >= 0) {
7487
- const timing = msByTool.get(e.tool) ?? { sum: 0, n: 0 };
7488
- timing.sum += e.ms;
7489
- timing.n++;
7490
- msByTool.set(e.tool, timing);
7491
- }
7492
- } catch {
7493
- }
7494
- }
7495
- }
7496
- const commands = toDimensionStats(byTool).map((d) => {
7497
- const timing = msByTool.get(d.key);
7498
- return {
7499
- tool: d.key,
7500
- calls: d.calls,
7501
- complete: d.complete,
7502
- partial: d.partial,
7503
- miss: d.miss,
7504
- successPct: d.successPct,
7505
- avgMs: timing && timing.n > 0 ? Math.round(timing.sum / timing.n) : null
7506
- };
7507
- });
7508
- const totals = commands.reduce(
7509
- (t, c) => ({
7510
- calls: t.calls + c.calls,
7511
- complete: t.complete + c.complete,
7512
- partial: t.partial + c.partial,
7513
- miss: t.miss + c.miss
7514
- }),
7515
- { calls: 0, complete: 0, partial: 0, miss: 0 }
7516
- );
7517
- const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
7518
- return {
7519
- enabled: savingsRecorded(root),
7520
- days,
7521
- commands,
7522
- sources: toDimensionStats(bySource),
7523
- clients: toDimensionStats(byClient),
7524
- totals,
7525
- avgSuccessPct
7526
- };
7527
- }
7528
- var PROBE_INTERVAL_MS = 2e3;
7529
- var MAX_PROBE_INTERVAL_MS = 3e4;
7530
- var PROBE_DUTY_FACTOR = 20;
7531
- var REFRESH_BUDGET_MS = 5e3;
7532
- var FAILURE_COOLDOWN_MS = 6e4;
7533
- var GraphSource = class {
7534
- constructor(graphPath, refresh = false, tuning = {}) {
7535
- this.graphPath = graphPath;
7536
- this.refresh = refresh;
7537
- this.tuning = tuning;
7538
- this.root = path17.dirname(path17.dirname(graphPath));
7539
- }
7540
- graphPath;
7541
- refresh;
7542
- tuning;
7543
- cachedMtimeMs = -1;
7544
- cached = null;
7545
- root;
7546
- lastProbeAt = 0;
7547
- failedUntil = 0;
7548
- inflight = null;
7549
- /** Self-tuned: grows with measured probe cost so huge repos aren't penalized. */
7550
- probeIntervalMs = PROBE_INTERVAL_MS;
7551
- /** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */
7552
- async get() {
7553
- if (this.refresh) await this.maybeRefresh();
7554
- const stat = fs17.statSync(this.graphPath);
7555
- if (stat.mtimeMs !== this.cachedMtimeMs || !this.cached) {
7556
- this.cached = parseGraph(fs17.readFileSync(this.graphPath, "utf8"));
7557
- this.cachedMtimeMs = stat.mtimeMs;
7558
- }
7559
- return this.cached;
7560
- }
7561
- /**
7562
- * Debounced, single-flight refresh. Never throws — a refresh problem must
7563
- * degrade to "answer from the current map", not break the tool call.
7564
- */
7565
- async maybeRefresh() {
7566
- const now = Date.now();
7567
- if (!this.inflight) {
7568
- const interval = this.tuning.probeIntervalMs ?? this.probeIntervalMs;
7569
- if (now < this.failedUntil || now - this.lastProbeAt < interval) return;
7570
- this.lastProbeAt = now;
7571
- this.inflight = refreshIfStale(this.root).then((r) => {
7572
- if (r.status === "error") this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
7573
- if (r.status === "fresh" || r.status === "no-snapshot" || r.status === "locked") {
7574
- const cost = Date.now() - now;
7575
- this.probeIntervalMs = Math.min(
7576
- MAX_PROBE_INTERVAL_MS,
7577
- Math.max(PROBE_INTERVAL_MS, cost * PROBE_DUTY_FACTOR)
7578
- );
7579
- }
7580
- }).catch(() => {
7581
- this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
7582
- }).finally(() => {
7583
- this.inflight = null;
7584
- });
7585
- }
7586
- await Promise.race([this.inflight, sleep(this.tuning.refreshBudgetMs ?? REFRESH_BUDGET_MS)]);
7587
- }
7588
- };
7589
- function createServer(source, opts = {}) {
7590
- const { savings = false, shareStats = false, local = false, dedup = false, stats } = opts;
7591
- const record = savings || shareStats;
7592
- const root = path17.dirname(path17.dirname(source.graphPath));
7593
- const seen = /* @__PURE__ */ new Set();
7594
- const server = new Server(
7595
- { name: "vg", version: VERSION },
7596
- {
7597
- capabilities: { tools: {} },
7598
- // Routing guidance once at the server level (hosts that surface
7599
- // `instructions` get it at zero per-step schema cost): the flashlight
7600
- // vs the map.
7601
- 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.'
7602
- }
7603
- );
7604
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
7605
- tools: TOOLS.map((t) => ({
7606
- name: t.name,
7607
- description: t.description,
7608
- inputSchema: t.inputSchema,
7609
- annotations: { readOnlyHint: true, openWorldHint: false }
7610
- }))
7611
- }));
7612
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
7613
- const tool = TOOLS.find((t) => t.name === request.params.name);
7614
- if (!tool) {
7615
- return errorResult(`unknown tool "${request.params.name}"`);
7616
- }
7617
- let graph;
7618
- try {
7619
- graph = await source.get();
7620
- } catch {
7621
- return errorResult(
7622
- "no code map found. Run `vg` in the project to build .vibgrate/graph.json, then retry."
7623
- );
7624
- }
7625
- const startedAt = Date.now();
7626
- try {
7627
- const args = request.params.arguments ?? {};
7628
- const result = await tool.handler(graph, args, { root, local, dedup, seen });
7629
- const ms = Date.now() - startedAt;
7630
- stats?.record({ ...measureCall(tool.name, result), client: sanitizeClient(detectClient(server)), ms });
7631
- if (record) recordUsage(root, tool.name, result, detectClient(server), ms);
7632
- return renderToolResult(result);
7633
- } catch (err) {
7634
- stats?.record({
7635
- tool: tool.name,
7636
- client: sanitizeClient(detectClient(server)),
7637
- outcome: "miss",
7638
- ms: Date.now() - startedAt,
7639
- vgTokens: 0,
7640
- baselineTokens: 0
7641
- });
7642
- return errorResult(`tool "${tool.name}" failed: ${err.message}`);
7643
- }
7644
- });
7645
- return server;
7646
- }
7647
- async function serveStdio(graphPath, opts = {}) {
7648
- const source = new GraphSource(graphPath, opts.refresh !== false);
7649
- const server = createServer(source, opts);
7650
- warmEmbedderInBackground(opts.local);
7651
- await server.connect(new StdioServerTransport());
7652
- }
7653
- function sleep(ms) {
7654
- return new Promise((resolve12) => {
7655
- const timer = setTimeout(resolve12, ms);
7656
- timer.unref?.();
7657
- });
7658
- }
7659
- function recordUsage(root, tool, result, client, ms) {
7660
- recordSaving(
7661
- root,
7662
- { ...measureCall(tool, result), source: "mcp", client: sanitizeClient(client), ...ms !== void 0 ? { ms } : {} },
7663
- Date.now()
7664
- );
7665
- }
7666
- function measureCall(tool, result) {
7667
- const outcome = classifyOutcome(result);
7668
- let vgTokens = 0;
7669
- let baselineTokens = 0;
7670
- if (SAVINGS_TOOLS.has(tool) && result && typeof result === "object") {
7671
- vgTokens = countTokens(renderedText(renderToolResult(result)));
7672
- baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
7673
- }
7674
- return { tool, outcome, vgTokens, baselineTokens };
7675
- }
7676
- function detectClient(server) {
7677
- try {
7678
- const info = server.getClientVersion?.();
7679
- return typeof info?.name === "string" ? info.name : void 0;
7680
- } catch {
7681
- return void 0;
7682
- }
7683
- }
7684
- function classifyOutcome(result) {
7685
- if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
7686
- if (!result || typeof result !== "object") return "miss";
7687
- const r = result;
7688
- if (typeof r.error === "string" && r.error) return "miss";
7689
- if (r.connected === false) return "miss";
7690
- if (Array.isArray(r.matches) && r.matches.length === 0) return "miss";
7691
- return isPartial(r) ? "partial" : "complete";
7692
- }
7693
- function isPartial(r) {
7694
- if (r.moreAvailable === true) return true;
7695
- if (r._truncated && typeof r._truncated === "object") return true;
7696
- for (const [key, value] of Object.entries(r)) {
7697
- if (typeof value !== "number" || !key.endsWith("Total")) continue;
7698
- const base = key.slice(0, -"Total".length);
7699
- const shown = Array.isArray(r[base]) ? r[base].length : 0;
7700
- if (value > shown) return true;
7701
- }
7702
- return false;
7703
- }
7704
- function renderedText(rendered) {
7705
- const block = rendered.content?.find((b) => b.type === "text");
7706
- return block && "text" in block && typeof block.text === "string" ? block.text : "";
7707
- }
7708
- function referencedFiles(result) {
7709
- const r = result;
7710
- const files = /* @__PURE__ */ new Set();
7711
- if (typeof r.file === "string" && r.file) files.add(r.file);
7712
- for (const m of asArray(r.matches)) {
7713
- const f = m.file;
7714
- if (typeof f === "string" && f) files.add(f);
7715
- }
7716
- for (const name of [...asArray(r.calls), ...asArray(r.calledBy)]) {
7717
- if (typeof name !== "string") continue;
7718
- const i = name.indexOf(":");
7719
- if (i > 0) files.add(name.slice(0, i));
7720
- }
7721
- return files;
7722
- }
7723
- function asArray(v) {
7724
- return Array.isArray(v) ? v : [];
7725
- }
7726
- function errorResult(message) {
7727
- return { content: [{ type: "text", text: message }], isError: true };
7728
- }
7729
-
7730
- // src/engine/export.ts
7731
- function formatForExt(ext) {
7732
- switch (ext.toLowerCase()) {
7733
- case ".json":
7734
- return "json";
7735
- case ".ndjson":
7736
- return "ndjson";
7737
- case ".graphml":
7738
- return "graphml";
7739
- case ".dot":
7740
- case ".gv":
7741
- return "dot";
7742
- case ".cypher":
7743
- return "cypher";
7744
- case ".sql":
7745
- return "sql";
7746
- case ".md":
7747
- return "md";
7748
- case ".html":
7749
- return "html";
7750
- default:
7751
- return null;
7752
- }
7753
- }
7754
- function exportGraph(format, ctx) {
7755
- switch (format) {
7756
- case "json":
7757
- return serializeGraph(ctx.graph);
7758
- case "md":
7759
- return renderReport(ctx.graph);
7760
- case "html":
7761
- return renderHtml(ctx.graph);
7762
- case "ndjson":
7763
- return ndjson(ctx.graph);
7764
- case "graphml":
7765
- return graphml(ctx.graph);
7766
- case "dot":
7767
- return dot(ctx.graph);
7768
- case "cypher":
7769
- return cypher(ctx.graph);
7770
- case "sql":
7771
- return sql(ctx);
7772
- case "cyclonedx":
7773
- return cyclonedx(ctx);
7774
- case "spdx":
7775
- return spdx(ctx);
7776
- }
7777
- }
7778
- function ndjson(graph) {
7779
- const rows = graph.facts && graph.facts.length ? graph.facts : graph.nodes;
7780
- return rows.map((r) => JSON.stringify(r)).join("\n") + "\n";
7781
- }
7782
- function esc2(s) {
7783
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
7784
- }
7785
- function graphml(graph) {
7786
- const lines = [];
7787
- lines.push('<?xml version="1.0" encoding="UTF-8"?>');
7788
- lines.push('<graphml xmlns="http://graphml.graphdrawing.org/xmlns">');
7789
- lines.push(' <key id="name" for="node" attr.name="name" attr.type="string"/>');
7790
- lines.push(' <key id="kind" for="node" attr.name="kind" attr.type="string"/>');
7791
- lines.push(' <key id="file" for="node" attr.name="file" attr.type="string"/>');
7792
- lines.push(' <key id="ekind" for="edge" attr.name="kind" attr.type="string"/>');
7793
- lines.push(' <graph edgedefault="directed">');
7794
- for (const n of graph.nodes) {
7795
- 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>`);
7796
- }
7797
- for (const e of graph.edges) {
7798
- lines.push(` <edge source="${esc2(e.src)}" target="${esc2(e.dst)}"><data key="ekind">${esc2(e.kind)}</data></edge>`);
7799
- }
7800
- lines.push(" </graph>");
7801
- lines.push("</graphml>");
7802
- return lines.join("\n") + "\n";
7803
- }
7804
- function dot(graph) {
7805
- const lines = ["digraph vg {"];
7806
- for (const n of graph.nodes) lines.push(` "${n.id}" [label="${dotEsc(n.qualifiedName)}"];`);
7807
- for (const e of graph.edges) lines.push(` "${e.src}" -> "${e.dst}" [label="${e.kind}"];`);
7808
- lines.push("}");
7809
- return lines.join("\n") + "\n";
7810
- }
7811
- function dotEsc(s) {
7812
- return s.replace(/"/g, '\\"');
7813
- }
7814
- function cypher(graph) {
7815
- const lines = [];
7816
- for (const n of graph.nodes) {
7817
- lines.push(
7818
- `CREATE (:\`${cypherLabel(n.kind)}\` {id:${q(n.id)}, name:${q(n.qualifiedName)}, file:${q(n.file)}});`
7819
- );
7820
- }
7821
- for (const e of graph.edges) {
7822
- lines.push(
7823
- `MATCH (a {id:${q(e.src)}}),(b {id:${q(e.dst)}}) CREATE (a)-[:\`${cypherLabel(e.kind)}\`]->(b);`
7824
- );
7825
- }
7826
- return lines.join("\n") + "\n";
7827
- }
7828
- function cypherLabel(s) {
7829
- return s.replace(/[^A-Za-z0-9_]/g, "_");
7830
- }
7831
- function q(s) {
7832
- return JSON.stringify(s);
7833
- }
7834
- function sql(ctx) {
7835
- const graph = ctx.graph;
7836
- const lines = [];
7837
- lines.push("BEGIN;");
7838
- lines.push("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);");
7839
- const meta = [
7840
- ["schemaVersion", graph.schemaVersion],
7841
- ["generatedAt", ctx.generatedAt],
7842
- ["corpusHash", graph.provenance.corpusHash],
7843
- ["tool", graph.provenance.tool],
7844
- ["toolVersion", graph.provenance.version],
7845
- ["resolver", graph.provenance.resolver.join(",")],
7846
- ["fingerprint", graph.provenance.toolchain?.fingerprint ?? null]
7847
- ];
7848
- for (const [k, v] of meta) lines.push(`INSERT INTO meta VALUES (${sqlStr(k)}, ${sqlStr(v)});`);
7849
- lines.push(
7850
- "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);"
7851
- );
7852
- for (const n of graph.nodes) {
7853
- lines.push(
7854
- `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)});`
7855
- );
7856
- }
7857
- lines.push(
7858
- '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);'
7859
- );
7860
- for (const e of graph.edges) {
7861
- lines.push(
7862
- `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)});`
7863
- );
7864
- }
7865
- lines.push("CREATE TABLE IF NOT EXISTS areas (id INTEGER PRIMARY KEY, label TEXT, size INTEGER, cohesion REAL, external_edges INTEGER);");
7866
- lines.push("CREATE TABLE IF NOT EXISTS area_members (area_id INTEGER, node_id TEXT);");
7867
- for (const a of graph.areas) {
7868
- lines.push(`INSERT INTO areas VALUES (${sqlNum(a.id)}, ${sqlStr(a.label)}, ${sqlNum(a.size)}, ${sqlNum(a.cohesion)}, ${sqlNum(a.externalEdges)});`);
7869
- for (const m of a.members) lines.push(`INSERT INTO area_members VALUES (${sqlNum(a.id)}, ${sqlStr(m)});`);
7870
- }
7871
- if (graph.facts && graph.facts.length) {
7872
- lines.push("CREATE TABLE IF NOT EXISTS facts (id TEXT PRIMARY KEY, kind TEXT, predicate_json TEXT, derived_by TEXT, confidence TEXT);");
7873
- lines.push("CREATE TABLE IF NOT EXISTS fact_subjects (fact_id TEXT, node_id TEXT);");
7874
- lines.push("CREATE TABLE IF NOT EXISTS fact_evidence (fact_id TEXT, file TEXT, span_start INTEGER, span_end INTEGER);");
7875
- for (const f of graph.facts) {
7876
- lines.push(`INSERT INTO facts VALUES (${sqlStr(f.id)}, ${sqlStr(f.kind)}, ${sqlStr(JSON.stringify(f.predicate))}, ${sqlStr(f.derivedBy)}, ${sqlStr(f.confidence)});`);
7877
- for (const s of f.subjectIds) lines.push(`INSERT INTO fact_subjects VALUES (${sqlStr(f.id)}, ${sqlStr(s)});`);
7878
- 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)});`);
7879
- }
7880
- }
7881
- lines.push("COMMIT;");
7882
- return lines.join("\n") + "\n";
7883
- }
7884
- function sqlStr(v) {
7885
- if (v == null) return "NULL";
7886
- return `'${v.replace(/'/g, "''")}'`;
7887
- }
7888
- function sqlNum(v) {
7889
- return v == null || !Number.isFinite(v) ? "NULL" : String(v);
7890
- }
7891
- function sqlBool(v) {
7892
- return v == null ? "NULL" : v ? "1" : "0";
7893
- }
7894
- function cyclonedx(ctx) {
7895
- const components = [];
7896
- for (const d of ctx.deps ?? []) {
7897
- components.push({
7898
- type: "library",
7899
- name: d.name,
7900
- version: d.installed ?? d.declared,
7901
- purl: d.ecosystem === "npm" ? `pkg:npm/${d.name}@${d.installed ?? ""}` : void 0
7902
- });
7903
- }
7904
- for (const m of ctx.models ?? []) {
7905
- components.push({ type: "machine-learning-model", name: m.name, properties: [{ name: "vg:runtime", value: m.runtime }] });
7906
- }
7907
- const bom = {
7908
- bomFormat: "CycloneDX",
7909
- specVersion: "1.6",
7910
- metadata: { timestamp: ctx.generatedAt, tools: [{ name: "vg", version: ctx.graph.provenance.version }] },
7911
- components
7912
- };
7913
- return JSON.stringify(bom, null, 2) + "\n";
7914
- }
7915
- function spdx(ctx) {
7916
- const packages = (ctx.deps ?? []).map((d) => ({
7917
- SPDXID: `SPDXRef-Package-${cypherLabel(d.name)}`,
7918
- name: d.name,
7919
- versionInfo: d.installed ?? d.declared,
7920
- downloadLocation: "NOASSERTION"
7921
- }));
7922
- const doc = {
7923
- spdxVersion: "SPDX-2.3",
7924
- dataLicense: "CC0-1.0",
7925
- SPDXID: "SPDXRef-DOCUMENT",
7926
- name: "vg-sbom",
7927
- creationInfo: { created: ctx.generatedAt, creators: [`Tool: vg-${ctx.graph.provenance.version}`] },
7928
- packages
7929
- };
7930
- return JSON.stringify(doc, null, 2) + "\n";
7931
- }
7932
5653
 
7933
- 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 };
7934
- //# sourceMappingURL=chunk-ELUFCQDR.js.map
7935
- //# sourceMappingURL=chunk-ELUFCQDR.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