@vibgrate/cli 2026.720.1 → 2026.720.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baseline-FALF7ULT.js +6 -0
- package/dist/{baseline-5QPV6API.js.map → baseline-FALF7ULT.js.map} +1 -1
- package/dist/{chunk-AJDUMRVI.js → chunk-B6ZWB3WH.js} +3 -3
- package/dist/{chunk-AJDUMRVI.js.map → chunk-B6ZWB3WH.js.map} +1 -1
- package/dist/{chunk-VFO5UDAT.js → chunk-BHA7QIPD.js} +185 -16
- package/dist/chunk-BHA7QIPD.js.map +1 -0
- package/dist/{chunk-VYSTOEAK.js → chunk-IUKCKMR6.js} +3 -3
- package/dist/{chunk-VYSTOEAK.js.map → chunk-IUKCKMR6.js.map} +1 -1
- package/dist/{chunk-VQLOVQZP.js → chunk-ZFDVHKID.js} +803 -711
- package/dist/chunk-ZFDVHKID.js.map +1 -0
- package/dist/cli.js +349 -100
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +26 -7
- package/dist/index.js +3 -3
- package/dist/parse-worker.js +1 -1
- package/grammars/tree-sitter-objc.wasm +0 -0
- package/grammars/tree-sitter-ocaml.wasm +0 -0
- package/grammars/tree-sitter-rescript.wasm +0 -0
- package/grammars/tree-sitter-solidity.wasm +0 -0
- package/package.json +7 -7
- package/skills/vg/SKILL.md +29 -9
- package/dist/baseline-5QPV6API.js +0 -6
- package/dist/chunk-VFO5UDAT.js.map +0 -1
- package/dist/chunk-VQLOVQZP.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-
|
|
2
|
-
import { writeTextFile, pathExists, readJsonFile, loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-
|
|
3
|
-
import * as
|
|
1
|
+
import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-BHA7QIPD.js';
|
|
2
|
+
import { writeTextFile, pathExists, readJsonFile, loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-B6ZWB3WH.js';
|
|
3
|
+
import * as fs17 from 'fs';
|
|
4
4
|
import { spawnSync, execFileSync } from 'child_process';
|
|
5
|
-
import * as
|
|
5
|
+
import * as path17 from 'path';
|
|
6
6
|
import ignore from 'ignore';
|
|
7
7
|
import * as v8 from 'v8';
|
|
8
8
|
import Graph from 'graphology';
|
|
@@ -41,9 +41,9 @@ function whichOnPath(cmd) {
|
|
|
41
41
|
}
|
|
42
42
|
function isOwnBinary(binPath) {
|
|
43
43
|
try {
|
|
44
|
-
const real =
|
|
44
|
+
const real = fs17.realpathSync(binPath);
|
|
45
45
|
if (/[\\/]@vibgrate[\\/]cli[\\/]/.test(real)) return true;
|
|
46
|
-
const head =
|
|
46
|
+
const head = fs17.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(
|
|
56
|
+
return NPX_SEGMENT.test(fs17.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(
|
|
222
|
+
return p.split(path17.sep).join("/");
|
|
223
223
|
}
|
|
224
224
|
function buildRootIgnore(root, exclude) {
|
|
225
225
|
const ig = ignore();
|
|
226
|
-
const gitignorePath =
|
|
227
|
-
if (
|
|
228
|
-
ig.add(
|
|
226
|
+
const gitignorePath = path17.join(root, ".gitignore");
|
|
227
|
+
if (fs17.existsSync(gitignorePath)) {
|
|
228
|
+
ig.add(fs17.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 =
|
|
234
|
+
const root = path17.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) =>
|
|
243
|
+
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path17.resolve(root, p)) : [root]).filter((p) => fs17.existsSync(p));
|
|
244
244
|
const found = /* @__PURE__ */ new Map();
|
|
245
245
|
const considerFile = (abs) => {
|
|
246
|
-
const rel2 = toPosix(
|
|
246
|
+
const rel2 = toPosix(path17.relative(root, abs));
|
|
247
247
|
if (rel2.startsWith("..")) return;
|
|
248
248
|
if (rel2 === "" || rootIg.ignores(rel2)) return;
|
|
249
|
-
if (SKIP_FILES.has(
|
|
250
|
-
const lang = langForExtension(
|
|
249
|
+
if (SKIP_FILES.has(path17.basename(abs).toLowerCase())) return;
|
|
250
|
+
const lang = langForExtension(path17.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 =
|
|
257
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
258
258
|
} catch {
|
|
259
259
|
return;
|
|
260
260
|
}
|
|
261
261
|
for (const entry of entries) {
|
|
262
|
-
const abs =
|
|
263
|
-
const rel2 = toPosix(
|
|
262
|
+
const abs = path17.join(dir, entry.name);
|
|
263
|
+
const rel2 = toPosix(path17.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 =
|
|
274
|
+
const stat = fs17.statSync(scope);
|
|
275
275
|
if (stat.isDirectory()) walk2(scope);
|
|
276
276
|
else if (stat.isFile()) considerFile(scope);
|
|
277
277
|
}
|
|
@@ -336,7 +336,7 @@ function formatBytes(n) {
|
|
|
336
336
|
if (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
337
337
|
return `${n} B`;
|
|
338
338
|
}
|
|
339
|
-
var JS_TS_EXT = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
339
|
+
var JS_TS_EXT = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte", ".astro"];
|
|
340
340
|
var OTHER_EXT = [
|
|
341
341
|
".py",
|
|
342
342
|
".pyi",
|
|
@@ -365,7 +365,13 @@ var OTHER_EXT = [
|
|
|
365
365
|
".hpp",
|
|
366
366
|
".hh",
|
|
367
367
|
".hxx",
|
|
368
|
-
".h"
|
|
368
|
+
".h",
|
|
369
|
+
".m",
|
|
370
|
+
".mm",
|
|
371
|
+
".ml",
|
|
372
|
+
".mli",
|
|
373
|
+
".res",
|
|
374
|
+
".sol"
|
|
369
375
|
];
|
|
370
376
|
var EXT_GROUPS = {
|
|
371
377
|
".py": [".py", ".pyi"],
|
|
@@ -399,7 +405,19 @@ Object.assign(EXT_GROUPS, {
|
|
|
399
405
|
".hpp": CPP_EXT,
|
|
400
406
|
".hh": CPP_EXT,
|
|
401
407
|
".hxx": CPP_EXT,
|
|
402
|
-
".h": [...CPP_EXT, ".c"]
|
|
408
|
+
".h": [...CPP_EXT, ".c"],
|
|
409
|
+
// ObjC #imports headers; ObjC++ additionally reaches C++ headers.
|
|
410
|
+
".m": [".h", ".m", ".mm"],
|
|
411
|
+
".mm": [".h", ".m", ".mm", ...CPP_EXT],
|
|
412
|
+
".ml": [".ml", ".mli"],
|
|
413
|
+
".mli": [".ml", ".mli"],
|
|
414
|
+
".res": [".res"],
|
|
415
|
+
".sol": [".sol"],
|
|
416
|
+
// Template containers: ERB fragments are Ruby; HTML/EJS script is JS.
|
|
417
|
+
".erb": [".rb", ".erb"],
|
|
418
|
+
".html": JS_TS_EXT,
|
|
419
|
+
".htm": JS_TS_EXT,
|
|
420
|
+
".ejs": JS_TS_EXT
|
|
403
421
|
});
|
|
404
422
|
function buildModuleResolver(root, relSet) {
|
|
405
423
|
const probe = makeProbe(relSet);
|
|
@@ -437,10 +455,10 @@ function buildModuleResolver(root, relSet) {
|
|
|
437
455
|
resolve(fromRel, source) {
|
|
438
456
|
if (source.includes("\\")) source = source.split("\\").join(".");
|
|
439
457
|
if (source.startsWith("./") || source.startsWith("../")) {
|
|
440
|
-
return probe(posixJoin(
|
|
458
|
+
return probe(posixJoin(path17.posix.dirname(fromRel), source));
|
|
441
459
|
}
|
|
442
460
|
if (/\.[A-Za-z0-9]+$/.test(source) && !source.startsWith("<")) {
|
|
443
|
-
const hit = probe(posixJoin(
|
|
461
|
+
const hit = probe(posixJoin(path17.posix.dirname(fromRel), source));
|
|
444
462
|
if (hit) return hit;
|
|
445
463
|
}
|
|
446
464
|
if (source.startsWith(".")) {
|
|
@@ -511,8 +529,8 @@ function resolvePyRelative(probe, fromRel, source) {
|
|
|
511
529
|
let dots = 0;
|
|
512
530
|
while (source[dots] === ".") dots++;
|
|
513
531
|
const rest = source.slice(dots);
|
|
514
|
-
let dir =
|
|
515
|
-
for (let i = 1; i < dots; i++) dir =
|
|
532
|
+
let dir = path17.posix.dirname(fromRel);
|
|
533
|
+
for (let i = 1; i < dots; i++) dir = path17.posix.dirname(dir);
|
|
516
534
|
const sub = rest.replace(/\./g, "/");
|
|
517
535
|
return probe(sub ? posixJoin(dir, sub) : dir);
|
|
518
536
|
}
|
|
@@ -520,7 +538,7 @@ function relativeResolver(relSet) {
|
|
|
520
538
|
const probe = makeProbe(relSet);
|
|
521
539
|
return {
|
|
522
540
|
resolve(fromRel, source) {
|
|
523
|
-
if (source.startsWith(".")) return probe(posixJoin(
|
|
541
|
+
if (source.startsWith(".")) return probe(posixJoin(path17.posix.dirname(fromRel), source));
|
|
524
542
|
return resolveDotted(probe, source);
|
|
525
543
|
}
|
|
526
544
|
};
|
|
@@ -538,12 +556,12 @@ function makeProbe(relSet) {
|
|
|
538
556
|
};
|
|
539
557
|
}
|
|
540
558
|
function normalizeRel(p) {
|
|
541
|
-
const norm =
|
|
559
|
+
const norm = path17.posix.normalize(p);
|
|
542
560
|
const stripped = norm.startsWith("./") ? norm.slice(2) : norm;
|
|
543
561
|
return stripped.startsWith("..") ? "" : stripped;
|
|
544
562
|
}
|
|
545
563
|
function posixJoin(a, b) {
|
|
546
|
-
return
|
|
564
|
+
return path17.posix.normalize(path17.posix.join(a, b));
|
|
547
565
|
}
|
|
548
566
|
function matchPattern(pattern, source) {
|
|
549
567
|
const star = pattern.indexOf("*");
|
|
@@ -556,8 +574,8 @@ function matchPattern(pattern, source) {
|
|
|
556
574
|
}
|
|
557
575
|
function loadTsconfigPaths(root) {
|
|
558
576
|
for (const name of ["tsconfig.base.json", "tsconfig.json"]) {
|
|
559
|
-
const file =
|
|
560
|
-
if (
|
|
577
|
+
const file = path17.join(root, name);
|
|
578
|
+
if (fs17.existsSync(file)) {
|
|
561
579
|
const merged = readTsconfigChain(root, file, /* @__PURE__ */ new Set());
|
|
562
580
|
if (merged && merged.paths.length) return merged;
|
|
563
581
|
}
|
|
@@ -569,21 +587,21 @@ function readTsconfigChain(root, file, seen) {
|
|
|
569
587
|
seen.add(file);
|
|
570
588
|
let cfg;
|
|
571
589
|
try {
|
|
572
|
-
cfg = parseJsonc(
|
|
590
|
+
cfg = parseJsonc(fs17.readFileSync(file, "utf8"));
|
|
573
591
|
} catch {
|
|
574
592
|
return null;
|
|
575
593
|
}
|
|
576
|
-
const dir =
|
|
594
|
+
const dir = path17.dirname(file);
|
|
577
595
|
let base = { baseUrlRel: "", paths: [] };
|
|
578
596
|
if (cfg.extends && cfg.extends.startsWith(".")) {
|
|
579
|
-
const extPath =
|
|
580
|
-
if (
|
|
597
|
+
const extPath = path17.resolve(dir, cfg.extends.endsWith(".json") ? cfg.extends : `${cfg.extends}.json`);
|
|
598
|
+
if (fs17.existsSync(extPath)) {
|
|
581
599
|
const inherited = readTsconfigChain(root, extPath, seen);
|
|
582
600
|
if (inherited) base = inherited;
|
|
583
601
|
}
|
|
584
602
|
}
|
|
585
603
|
const co = cfg.compilerOptions ?? {};
|
|
586
|
-
const baseUrlRel = co.baseUrl !== void 0 ?
|
|
604
|
+
const baseUrlRel = co.baseUrl !== void 0 ? path17.relative(root, path17.resolve(dir, co.baseUrl)).split(path17.sep).join("/") : base.baseUrlRel;
|
|
587
605
|
const paths = [...base.paths];
|
|
588
606
|
if (co.paths) for (const [k, v] of Object.entries(co.paths)) paths.push([k, v]);
|
|
589
607
|
return { baseUrlRel, paths };
|
|
@@ -591,27 +609,27 @@ function readTsconfigChain(root, file, seen) {
|
|
|
591
609
|
function loadWorkspacePackages(root) {
|
|
592
610
|
const map = /* @__PURE__ */ new Map();
|
|
593
611
|
const globs = [];
|
|
594
|
-
const rootPkg =
|
|
595
|
-
if (
|
|
612
|
+
const rootPkg = path17.join(root, "package.json");
|
|
613
|
+
if (fs17.existsSync(rootPkg)) {
|
|
596
614
|
try {
|
|
597
|
-
const pkg = JSON.parse(
|
|
615
|
+
const pkg = JSON.parse(fs17.readFileSync(rootPkg, "utf8"));
|
|
598
616
|
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
599
617
|
if (ws) globs.push(...ws);
|
|
600
618
|
} catch {
|
|
601
619
|
}
|
|
602
620
|
}
|
|
603
|
-
const pnpmWs =
|
|
604
|
-
if (
|
|
605
|
-
for (const line of
|
|
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")) {
|
|
606
624
|
const m = /^\s*-\s*['"]?([^'"\n]+)['"]?\s*$/.exec(line);
|
|
607
625
|
if (m) globs.push(m[1].trim());
|
|
608
626
|
}
|
|
609
627
|
}
|
|
610
628
|
for (const dir of expandWorkspaceGlobs(root, globs)) {
|
|
611
|
-
const pj =
|
|
629
|
+
const pj = path17.join(dir, "package.json");
|
|
612
630
|
try {
|
|
613
|
-
const name = JSON.parse(
|
|
614
|
-
if (name) map.set(name,
|
|
631
|
+
const name = JSON.parse(fs17.readFileSync(pj, "utf8")).name;
|
|
632
|
+
if (name) map.set(name, path17.relative(root, dir).split(path17.sep).join("/"));
|
|
615
633
|
} catch {
|
|
616
634
|
}
|
|
617
635
|
}
|
|
@@ -621,10 +639,10 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
621
639
|
const out = /* @__PURE__ */ new Set();
|
|
622
640
|
for (const glob of globs) {
|
|
623
641
|
const clean = glob.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
|
|
624
|
-
const baseDir =
|
|
642
|
+
const baseDir = path17.join(root, clean);
|
|
625
643
|
const recurse = glob.endsWith("**");
|
|
626
644
|
if (glob === clean) {
|
|
627
|
-
if (
|
|
645
|
+
if (fs17.existsSync(path17.join(baseDir, "package.json"))) out.add(baseDir);
|
|
628
646
|
continue;
|
|
629
647
|
}
|
|
630
648
|
collectPackageDirs(baseDir, recurse ? 4 : 1, out);
|
|
@@ -635,14 +653,14 @@ function collectPackageDirs(dir, depth, out) {
|
|
|
635
653
|
if (depth < 0) return;
|
|
636
654
|
let entries;
|
|
637
655
|
try {
|
|
638
|
-
entries =
|
|
656
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
639
657
|
} catch {
|
|
640
658
|
return;
|
|
641
659
|
}
|
|
642
660
|
for (const e of entries) {
|
|
643
661
|
if (!e.isDirectory() || e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
644
|
-
const child =
|
|
645
|
-
if (
|
|
662
|
+
const child = path17.join(dir, e.name);
|
|
663
|
+
if (fs17.existsSync(path17.join(child, "package.json"))) out.add(child);
|
|
646
664
|
else collectPackageDirs(child, depth - 1, out);
|
|
647
665
|
}
|
|
648
666
|
}
|
|
@@ -1141,14 +1159,14 @@ var DEFAULT_PATHS = [
|
|
|
1141
1159
|
];
|
|
1142
1160
|
function loadCoverage(root, explicit) {
|
|
1143
1161
|
const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
|
|
1144
|
-
(p) =>
|
|
1162
|
+
(p) => path17.resolve(root, p)
|
|
1145
1163
|
);
|
|
1146
|
-
const found = candidates.filter((p) =>
|
|
1164
|
+
const found = candidates.filter((p) => fs17.existsSync(p));
|
|
1147
1165
|
if (found.length === 0) return null;
|
|
1148
1166
|
const map = /* @__PURE__ */ new Map();
|
|
1149
1167
|
for (const file of found) {
|
|
1150
1168
|
try {
|
|
1151
|
-
const text =
|
|
1169
|
+
const text = fs17.readFileSync(file, "utf8");
|
|
1152
1170
|
if (file.endsWith(".json")) mergeIstanbul(map, text, root);
|
|
1153
1171
|
else mergeLcov(map, text, root);
|
|
1154
1172
|
} catch {
|
|
@@ -1157,8 +1175,8 @@ function loadCoverage(root, explicit) {
|
|
|
1157
1175
|
return map.size ? map : null;
|
|
1158
1176
|
}
|
|
1159
1177
|
function rel(root, p) {
|
|
1160
|
-
const abs =
|
|
1161
|
-
return
|
|
1178
|
+
const abs = path17.isAbsolute(p) ? p : path17.resolve(root, p);
|
|
1179
|
+
return path17.relative(root, abs).split(path17.sep).join("/");
|
|
1162
1180
|
}
|
|
1163
1181
|
function bump(map, file, line, hits) {
|
|
1164
1182
|
let lh = map.get(file);
|
|
@@ -1494,7 +1512,7 @@ async function parseInline(files, options) {
|
|
|
1494
1512
|
onProgress?.(0, files.length);
|
|
1495
1513
|
for (const file of files) {
|
|
1496
1514
|
try {
|
|
1497
|
-
const source =
|
|
1515
|
+
const source = fs17.readFileSync(file.abs, "utf8");
|
|
1498
1516
|
out.push(await parseSource(file.rel, file.lang.id, source));
|
|
1499
1517
|
} catch (err) {
|
|
1500
1518
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
@@ -1549,9 +1567,9 @@ function isWorkerOom(err) {
|
|
|
1549
1567
|
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1550
1568
|
}
|
|
1551
1569
|
function resolveWorkerFile() {
|
|
1552
|
-
const here =
|
|
1553
|
-
const candidate =
|
|
1554
|
-
return
|
|
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;
|
|
1555
1573
|
}
|
|
1556
1574
|
function chunk(items, buckets) {
|
|
1557
1575
|
const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
|
|
@@ -1590,7 +1608,7 @@ function resolve4(parses, resolver) {
|
|
|
1590
1608
|
addNode(baseNodes, {
|
|
1591
1609
|
id: fileId,
|
|
1592
1610
|
kind: "file",
|
|
1593
|
-
name:
|
|
1611
|
+
name: path17.posix.basename(p.rel),
|
|
1594
1612
|
qualifiedName: p.rel,
|
|
1595
1613
|
file: p.rel,
|
|
1596
1614
|
span: { start: 1, end: 1 },
|
|
@@ -2026,14 +2044,14 @@ function enclosing3(nodes, line) {
|
|
|
2026
2044
|
return best;
|
|
2027
2045
|
}
|
|
2028
2046
|
function normalize2(p) {
|
|
2029
|
-
return
|
|
2047
|
+
return path17.resolve(p).split(path17.sep).join("/");
|
|
2030
2048
|
}
|
|
2031
2049
|
var CACHE_VERSION = "vg-parse-cache/2";
|
|
2032
2050
|
function cacheDir(root) {
|
|
2033
|
-
return
|
|
2051
|
+
return path17.join(root, ".vibgrate", "cache");
|
|
2034
2052
|
}
|
|
2035
2053
|
function cachePath(root) {
|
|
2036
|
-
return
|
|
2054
|
+
return path17.join(cacheDir(root), "parse-cache.json");
|
|
2037
2055
|
}
|
|
2038
2056
|
function loadCache(root, opts) {
|
|
2039
2057
|
const file = cachePath(root);
|
|
@@ -2043,9 +2061,9 @@ function loadCache(root, opts) {
|
|
|
2043
2061
|
grammars: opts.grammars,
|
|
2044
2062
|
entries: {}
|
|
2045
2063
|
};
|
|
2046
|
-
if (!opts.disabled &&
|
|
2064
|
+
if (!opts.disabled && fs17.existsSync(file)) {
|
|
2047
2065
|
try {
|
|
2048
|
-
const loaded = JSON.parse(
|
|
2066
|
+
const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
2049
2067
|
if (loaded.version === CACHE_VERSION && loaded.toolVersion === opts.toolVersion && loaded.grammars === opts.grammars && loaded.entries) {
|
|
2050
2068
|
data = loaded;
|
|
2051
2069
|
}
|
|
@@ -2066,8 +2084,8 @@ function loadCache(root, opts) {
|
|
|
2066
2084
|
}
|
|
2067
2085
|
},
|
|
2068
2086
|
save() {
|
|
2069
|
-
|
|
2070
|
-
|
|
2087
|
+
fs17.mkdirSync(cacheDir(root), { recursive: true });
|
|
2088
|
+
fs17.writeFileSync(file, stableStringify(data, 0));
|
|
2071
2089
|
}
|
|
2072
2090
|
};
|
|
2073
2091
|
}
|
|
@@ -2099,7 +2117,7 @@ function epistemicBreakdown(edges) {
|
|
|
2099
2117
|
// src/engine/build.ts
|
|
2100
2118
|
async function buildGraph(options) {
|
|
2101
2119
|
const start = nowMs();
|
|
2102
|
-
const root =
|
|
2120
|
+
const root = path17.resolve(options.root);
|
|
2103
2121
|
const files = discover({
|
|
2104
2122
|
root,
|
|
2105
2123
|
only: options.only,
|
|
@@ -2127,7 +2145,7 @@ async function buildGraph(options) {
|
|
|
2127
2145
|
for (const file of files) {
|
|
2128
2146
|
let stat;
|
|
2129
2147
|
try {
|
|
2130
|
-
stat =
|
|
2148
|
+
stat = fs17.statSync(file.abs);
|
|
2131
2149
|
} catch {
|
|
2132
2150
|
continue;
|
|
2133
2151
|
}
|
|
@@ -2145,7 +2163,7 @@ async function buildGraph(options) {
|
|
|
2145
2163
|
}
|
|
2146
2164
|
let hash = "";
|
|
2147
2165
|
try {
|
|
2148
|
-
hash = hashBytes(
|
|
2166
|
+
hash = hashBytes(fs17.readFileSync(file.abs));
|
|
2149
2167
|
} catch {
|
|
2150
2168
|
continue;
|
|
2151
2169
|
}
|
|
@@ -2237,7 +2255,7 @@ async function buildGraph(options) {
|
|
|
2237
2255
|
toolchain
|
|
2238
2256
|
},
|
|
2239
2257
|
meta: {
|
|
2240
|
-
root:
|
|
2258
|
+
root: path17.basename(root) === "" ? "." : ".",
|
|
2241
2259
|
languages,
|
|
2242
2260
|
counts: {
|
|
2243
2261
|
nodes: analysis.nodes.length,
|
|
@@ -2283,14 +2301,14 @@ function toRepoRel(p) {
|
|
|
2283
2301
|
function loadScipIndex(root, explicit) {
|
|
2284
2302
|
const candidates = [
|
|
2285
2303
|
explicit,
|
|
2286
|
-
|
|
2287
|
-
|
|
2304
|
+
path17.join(root, "index.scip"),
|
|
2305
|
+
path17.join(root, ".vibgrate", "index.scip")
|
|
2288
2306
|
].filter((p) => Boolean(p));
|
|
2289
2307
|
for (const file of candidates) {
|
|
2290
|
-
const abs =
|
|
2291
|
-
if (!
|
|
2308
|
+
const abs = path17.isAbsolute(file) ? file : path17.resolve(root, file);
|
|
2309
|
+
if (!fs17.existsSync(abs)) continue;
|
|
2292
2310
|
try {
|
|
2293
|
-
const index = decodeScipIndex(new Uint8Array(
|
|
2311
|
+
const index = decodeScipIndex(new Uint8Array(fs17.readFileSync(abs)));
|
|
2294
2312
|
if (index.documents.length) {
|
|
2295
2313
|
return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
|
|
2296
2314
|
}
|
|
@@ -2451,46 +2469,46 @@ function esc(s) {
|
|
|
2451
2469
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2452
2470
|
}
|
|
2453
2471
|
function vibgrateDir(root) {
|
|
2454
|
-
return
|
|
2472
|
+
return path17.join(root, ".vibgrate");
|
|
2455
2473
|
}
|
|
2456
2474
|
function defaultGraphPath(root) {
|
|
2457
|
-
return
|
|
2475
|
+
return path17.join(vibgrateDir(root), "graph.json");
|
|
2458
2476
|
}
|
|
2459
2477
|
function writeArtifacts(graph, options) {
|
|
2460
2478
|
const dir = vibgrateDir(options.root);
|
|
2461
|
-
|
|
2479
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
2462
2480
|
const graphPath = options.graphPath ?? defaultGraphPath(options.root);
|
|
2463
|
-
|
|
2481
|
+
fs17.mkdirSync(path17.dirname(graphPath), { recursive: true });
|
|
2464
2482
|
const tmp = `${graphPath}.${process.pid}.tmp`;
|
|
2465
2483
|
try {
|
|
2466
|
-
|
|
2467
|
-
|
|
2484
|
+
fs17.writeFileSync(tmp, serializeGraph(graph));
|
|
2485
|
+
fs17.renameSync(tmp, graphPath);
|
|
2468
2486
|
} catch (err) {
|
|
2469
|
-
|
|
2487
|
+
fs17.rmSync(tmp, { force: true });
|
|
2470
2488
|
throw err;
|
|
2471
2489
|
}
|
|
2472
2490
|
const written = { graphPath };
|
|
2473
2491
|
if (options.report !== false) {
|
|
2474
|
-
const reportPath =
|
|
2475
|
-
|
|
2492
|
+
const reportPath = path17.join(dir, "GRAPH_REPORT.md");
|
|
2493
|
+
fs17.writeFileSync(reportPath, renderReport(graph));
|
|
2476
2494
|
written.reportPath = reportPath;
|
|
2477
2495
|
}
|
|
2478
2496
|
if (options.html !== false) {
|
|
2479
|
-
const htmlPath =
|
|
2480
|
-
|
|
2497
|
+
const htmlPath = path17.join(dir, "graph.html");
|
|
2498
|
+
fs17.writeFileSync(htmlPath, renderHtml(graph));
|
|
2481
2499
|
written.htmlPath = htmlPath;
|
|
2482
2500
|
}
|
|
2483
2501
|
if (graph.facts && graph.facts.length) {
|
|
2484
|
-
const factsPath =
|
|
2485
|
-
|
|
2502
|
+
const factsPath = path17.join(dir, "facts.jsonl");
|
|
2503
|
+
fs17.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
|
|
2486
2504
|
written.factsPath = factsPath;
|
|
2487
2505
|
}
|
|
2488
2506
|
return written;
|
|
2489
2507
|
}
|
|
2490
2508
|
function loadGraph(root, graphPath) {
|
|
2491
2509
|
const file = graphPath ?? defaultGraphPath(root);
|
|
2492
|
-
if (!
|
|
2493
|
-
return parseGraph(
|
|
2510
|
+
if (!fs17.existsSync(file)) return null;
|
|
2511
|
+
return parseGraph(fs17.readFileSync(file, "utf8"));
|
|
2494
2512
|
}
|
|
2495
2513
|
|
|
2496
2514
|
// src/engine/verify.ts
|
|
@@ -2567,7 +2585,7 @@ function isProcessAlive(pid) {
|
|
|
2567
2585
|
}
|
|
2568
2586
|
function lockIsStale(file, staleMs) {
|
|
2569
2587
|
try {
|
|
2570
|
-
const { pid, at } = JSON.parse(
|
|
2588
|
+
const { pid, at } = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
2571
2589
|
if (typeof at === "number" && Date.now() - at > staleMs) return true;
|
|
2572
2590
|
if (typeof pid === "number" && !isProcessAlive(pid)) return true;
|
|
2573
2591
|
return false;
|
|
@@ -2578,10 +2596,10 @@ function lockIsStale(file, staleMs) {
|
|
|
2578
2596
|
function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
2579
2597
|
const write = () => {
|
|
2580
2598
|
try {
|
|
2581
|
-
|
|
2582
|
-
const fd =
|
|
2583
|
-
|
|
2584
|
-
|
|
2599
|
+
fs17.mkdirSync(path17.dirname(file), { recursive: true });
|
|
2600
|
+
const fd = fs17.openSync(file, "wx");
|
|
2601
|
+
fs17.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
2602
|
+
fs17.closeSync(fd);
|
|
2585
2603
|
return true;
|
|
2586
2604
|
} catch {
|
|
2587
2605
|
return false;
|
|
@@ -2590,7 +2608,7 @@ function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
|
2590
2608
|
if (write()) return true;
|
|
2591
2609
|
if (lockIsStale(file, staleMs)) {
|
|
2592
2610
|
try {
|
|
2593
|
-
|
|
2611
|
+
fs17.rmSync(file, { force: true });
|
|
2594
2612
|
} catch {
|
|
2595
2613
|
}
|
|
2596
2614
|
return write();
|
|
@@ -2599,7 +2617,7 @@ function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
|
2599
2617
|
}
|
|
2600
2618
|
function releaseLock(file) {
|
|
2601
2619
|
try {
|
|
2602
|
-
|
|
2620
|
+
fs17.rmSync(file, { force: true });
|
|
2603
2621
|
} catch {
|
|
2604
2622
|
}
|
|
2605
2623
|
}
|
|
@@ -2609,14 +2627,14 @@ function resolveEmbedModel(model) {
|
|
|
2609
2627
|
return model ?? "bge-small-en-v1.5";
|
|
2610
2628
|
}
|
|
2611
2629
|
function modelCacheDir() {
|
|
2612
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
2613
|
-
return
|
|
2630
|
+
const base = process.env.XDG_CACHE_HOME || path17.join(os.homedir(), ".cache");
|
|
2631
|
+
return path17.join(base, "vibgrate", "models");
|
|
2614
2632
|
}
|
|
2615
2633
|
function modelReadyMarker(modelId) {
|
|
2616
|
-
return
|
|
2634
|
+
return path17.join(modelCacheDir(), `.ready-${safe(modelId)}`);
|
|
2617
2635
|
}
|
|
2618
2636
|
function isModelReady(modelId = resolveEmbedModel()) {
|
|
2619
|
-
return
|
|
2637
|
+
return fs17.existsSync(modelReadyMarker(modelId));
|
|
2620
2638
|
}
|
|
2621
2639
|
function unavailableMessage(reason) {
|
|
2622
2640
|
switch (reason) {
|
|
@@ -2633,13 +2651,13 @@ function unavailableMessage(reason) {
|
|
|
2633
2651
|
}
|
|
2634
2652
|
function modelCacheInfo(modelId = resolveEmbedModel()) {
|
|
2635
2653
|
const dir = modelCacheDir();
|
|
2636
|
-
return { dir, present: isModelReady(modelId) ||
|
|
2654
|
+
return { dir, present: isModelReady(modelId) || fs17.existsSync(dir), bytes: dirSize(dir) };
|
|
2637
2655
|
}
|
|
2638
2656
|
function clearModelCache() {
|
|
2639
2657
|
const dir = modelCacheDir();
|
|
2640
2658
|
const bytes = dirSize(dir);
|
|
2641
2659
|
try {
|
|
2642
|
-
|
|
2660
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
2643
2661
|
} catch {
|
|
2644
2662
|
}
|
|
2645
2663
|
return bytes;
|
|
@@ -2648,16 +2666,16 @@ function dirSize(dir) {
|
|
|
2648
2666
|
let total = 0;
|
|
2649
2667
|
let entries;
|
|
2650
2668
|
try {
|
|
2651
|
-
entries =
|
|
2669
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
2652
2670
|
} catch {
|
|
2653
2671
|
return 0;
|
|
2654
2672
|
}
|
|
2655
2673
|
for (const e of entries) {
|
|
2656
|
-
const p =
|
|
2674
|
+
const p = path17.join(dir, e.name);
|
|
2657
2675
|
if (e.isDirectory()) total += dirSize(p);
|
|
2658
2676
|
else {
|
|
2659
2677
|
try {
|
|
2660
|
-
total +=
|
|
2678
|
+
total += fs17.statSync(p).size;
|
|
2661
2679
|
} catch {
|
|
2662
2680
|
}
|
|
2663
2681
|
}
|
|
@@ -2669,7 +2687,7 @@ function isPermissionError(e) {
|
|
|
2669
2687
|
return code === "EACCES" || code === "EPERM" || code === "EROFS";
|
|
2670
2688
|
}
|
|
2671
2689
|
function embeddingsCached(root, modelId) {
|
|
2672
|
-
return
|
|
2690
|
+
return fs17.existsSync(path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
|
|
2673
2691
|
}
|
|
2674
2692
|
async function loadEmbedder(options = {}) {
|
|
2675
2693
|
if (options.local) return null;
|
|
@@ -2683,7 +2701,7 @@ async function loadEmbedder(options = {}) {
|
|
|
2683
2701
|
if (!mod?.FlagEmbedding) return fail("not-installed");
|
|
2684
2702
|
const cache = modelCacheDir();
|
|
2685
2703
|
try {
|
|
2686
|
-
|
|
2704
|
+
fs17.mkdirSync(cache, { recursive: true });
|
|
2687
2705
|
} catch (e) {
|
|
2688
2706
|
return fail(isPermissionError(e) ? "no-permission" : "init-failed");
|
|
2689
2707
|
}
|
|
@@ -2698,7 +2716,7 @@ async function loadEmbedder(options = {}) {
|
|
|
2698
2716
|
"embedding model init timed out"
|
|
2699
2717
|
);
|
|
2700
2718
|
try {
|
|
2701
|
-
|
|
2719
|
+
fs17.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
|
|
2702
2720
|
} catch {
|
|
2703
2721
|
}
|
|
2704
2722
|
return {
|
|
@@ -2723,7 +2741,7 @@ async function importEmbedBackend() {
|
|
|
2723
2741
|
const hostDir = process.env.VIBGRATE_EMBEDDER_PATH;
|
|
2724
2742
|
if (hostDir) {
|
|
2725
2743
|
try {
|
|
2726
|
-
const req = createRequire(
|
|
2744
|
+
const req = createRequire(path17.join(hostDir, "package.json"));
|
|
2727
2745
|
const mod = await import(pathToFileURL(req.resolve("fastembed")).href);
|
|
2728
2746
|
const resolved = mod?.FlagEmbedding ? mod : mod?.default;
|
|
2729
2747
|
if (resolved?.FlagEmbedding) return resolved;
|
|
@@ -2809,7 +2827,7 @@ function cosine(a, b) {
|
|
|
2809
2827
|
var EMBED_CHUNK = 256;
|
|
2810
2828
|
var CACHE_WRITE_INTERVAL_MS = 1500;
|
|
2811
2829
|
function vectorCachePath(root, modelId) {
|
|
2812
|
-
return
|
|
2830
|
+
return path17.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
|
|
2813
2831
|
}
|
|
2814
2832
|
function countPending(graph, root, modelId) {
|
|
2815
2833
|
const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
|
|
@@ -2824,9 +2842,9 @@ function countPending(graph, root, modelId) {
|
|
|
2824
2842
|
return pending;
|
|
2825
2843
|
}
|
|
2826
2844
|
function readCacheEntries(file, modelId) {
|
|
2827
|
-
if (!
|
|
2845
|
+
if (!fs17.existsSync(file)) return {};
|
|
2828
2846
|
try {
|
|
2829
|
-
const loaded = JSON.parse(
|
|
2847
|
+
const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
2830
2848
|
if (loaded.model === modelId && loaded.entries) return loaded.entries;
|
|
2831
2849
|
} catch {
|
|
2832
2850
|
}
|
|
@@ -2835,9 +2853,9 @@ function readCacheEntries(file, modelId) {
|
|
|
2835
2853
|
async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
2836
2854
|
const file = vectorCachePath(root, embedder.id);
|
|
2837
2855
|
let cache = { model: embedder.id, entries: {} };
|
|
2838
|
-
if (
|
|
2856
|
+
if (fs17.existsSync(file)) {
|
|
2839
2857
|
try {
|
|
2840
|
-
const loaded = JSON.parse(
|
|
2858
|
+
const loaded = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
2841
2859
|
if (loaded.model === embedder.id && loaded.entries) cache = loaded;
|
|
2842
2860
|
} catch {
|
|
2843
2861
|
}
|
|
@@ -2855,10 +2873,10 @@ async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
|
2855
2873
|
}
|
|
2856
2874
|
const persist = () => {
|
|
2857
2875
|
try {
|
|
2858
|
-
|
|
2876
|
+
fs17.mkdirSync(cacheDir(root), { recursive: true });
|
|
2859
2877
|
const tmp = `${file}.tmp.${process.pid}`;
|
|
2860
|
-
|
|
2861
|
-
|
|
2878
|
+
fs17.writeFileSync(tmp, JSON.stringify(cache));
|
|
2879
|
+
fs17.renameSync(tmp, file);
|
|
2862
2880
|
} catch {
|
|
2863
2881
|
}
|
|
2864
2882
|
};
|
|
@@ -2896,21 +2914,21 @@ function safe(id) {
|
|
|
2896
2914
|
}
|
|
2897
2915
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2898
2916
|
function snapshotPath(root) {
|
|
2899
|
-
return
|
|
2917
|
+
return path17.join(cacheDir(root), "freshness.json");
|
|
2900
2918
|
}
|
|
2901
2919
|
function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
|
|
2902
2920
|
const files = {};
|
|
2903
2921
|
for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
|
|
2904
2922
|
const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
|
|
2905
2923
|
try {
|
|
2906
|
-
|
|
2907
|
-
|
|
2924
|
+
fs17.mkdirSync(cacheDir(root), { recursive: true });
|
|
2925
|
+
fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2908
2926
|
} catch {
|
|
2909
2927
|
}
|
|
2910
2928
|
}
|
|
2911
2929
|
function loadSnapshot(root) {
|
|
2912
2930
|
try {
|
|
2913
|
-
const loaded = JSON.parse(
|
|
2931
|
+
const loaded = JSON.parse(fs17.readFileSync(snapshotPath(root), "utf8"));
|
|
2914
2932
|
if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
|
|
2915
2933
|
return { ...loaded, scope: loaded.scope ?? {} };
|
|
2916
2934
|
}
|
|
@@ -2946,7 +2964,7 @@ function probeFreshness(root) {
|
|
|
2946
2964
|
}
|
|
2947
2965
|
let stat;
|
|
2948
2966
|
try {
|
|
2949
|
-
stat =
|
|
2967
|
+
stat = fs17.statSync(file.abs);
|
|
2950
2968
|
} catch {
|
|
2951
2969
|
drift.removed.push(file.rel);
|
|
2952
2970
|
continue;
|
|
@@ -2954,7 +2972,7 @@ function probeFreshness(root) {
|
|
|
2954
2972
|
if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
|
|
2955
2973
|
let hash = "";
|
|
2956
2974
|
try {
|
|
2957
|
-
hash = hashBytes(
|
|
2975
|
+
hash = hashBytes(fs17.readFileSync(file.abs));
|
|
2958
2976
|
} catch {
|
|
2959
2977
|
drift.removed.push(file.rel);
|
|
2960
2978
|
continue;
|
|
@@ -2971,7 +2989,7 @@ function probeFreshness(root) {
|
|
|
2971
2989
|
}
|
|
2972
2990
|
if (absorbed && !hasDrift(drift)) {
|
|
2973
2991
|
try {
|
|
2974
|
-
|
|
2992
|
+
fs17.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2975
2993
|
} catch {
|
|
2976
2994
|
}
|
|
2977
2995
|
}
|
|
@@ -3288,221 +3306,6 @@ function estimateTokens(text) {
|
|
|
3288
3306
|
function round2(x) {
|
|
3289
3307
|
return Math.round(x * 1e6) / 1e6;
|
|
3290
3308
|
}
|
|
3291
|
-
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
3292
|
-
function refreshLockPath(root) {
|
|
3293
|
-
return path19.join(cacheDir(root), "refresh.lock");
|
|
3294
|
-
}
|
|
3295
|
-
async function refreshIfStale(root, opts = {}) {
|
|
3296
|
-
const probe = probeFreshness(root);
|
|
3297
|
-
if (!probe) return { status: "no-snapshot" };
|
|
3298
|
-
if (!hasDrift(probe.drift)) return { status: "fresh" };
|
|
3299
|
-
const lock = refreshLockPath(root);
|
|
3300
|
-
if (!acquireLock(lock, REFRESH_LOCK_STALE_MS)) return { status: "locked" };
|
|
3301
|
-
const start = process.hrtime.bigint();
|
|
3302
|
-
try {
|
|
3303
|
-
const { scope } = probe;
|
|
3304
|
-
const result = await buildGraph({
|
|
3305
|
-
root,
|
|
3306
|
-
only: scope.only,
|
|
3307
|
-
exclude: scope.exclude,
|
|
3308
|
-
paths: scope.paths,
|
|
3309
|
-
deep: scope.deep,
|
|
3310
|
-
noGround: scope.noGround,
|
|
3311
|
-
scip: scope.scip,
|
|
3312
|
-
noScip: scope.noScip,
|
|
3313
|
-
noTsc: scope.noTsc,
|
|
3314
|
-
cluster: scope.cluster,
|
|
3315
|
-
grammarsDir: scope.grammarsDir,
|
|
3316
|
-
inline: opts.inline,
|
|
3317
|
-
jobs: opts.jobs
|
|
3318
|
-
});
|
|
3319
|
-
const wrote = result.graph.provenance.corpusHash !== probe.corpusHash;
|
|
3320
|
-
if (wrote) {
|
|
3321
|
-
const dir = vibgrateDir(root);
|
|
3322
|
-
writeArtifacts(result.graph, {
|
|
3323
|
-
root,
|
|
3324
|
-
report: fs19.existsSync(path19.join(dir, "GRAPH_REPORT.md")),
|
|
3325
|
-
html: fs19.existsSync(path19.join(dir, "graph.html"))
|
|
3326
|
-
});
|
|
3327
|
-
}
|
|
3328
|
-
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
3329
|
-
const ms = Number((process.hrtime.bigint() - start) / 1000000n);
|
|
3330
|
-
return {
|
|
3331
|
-
status: "refreshed",
|
|
3332
|
-
drift: probe.drift,
|
|
3333
|
-
ms,
|
|
3334
|
-
reparsed: result.reparsed,
|
|
3335
|
-
totalFiles: result.totalFiles,
|
|
3336
|
-
wrote
|
|
3337
|
-
};
|
|
3338
|
-
} catch (err) {
|
|
3339
|
-
return { status: "error", message: err.message };
|
|
3340
|
-
} finally {
|
|
3341
|
-
releaseLock(lock);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
var LEDGER = "savings.jsonl";
|
|
3345
|
-
var PER_FILE_TOKENS = 400;
|
|
3346
|
-
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node"]);
|
|
3347
|
-
var CLI_TOOL_ALIASES = {
|
|
3348
|
-
ask: "query_graph",
|
|
3349
|
-
show: "get_node",
|
|
3350
|
-
impact: "impact_of",
|
|
3351
|
-
path: "find_path",
|
|
3352
|
-
hubs: "list_hubs",
|
|
3353
|
-
areas: "list_areas",
|
|
3354
|
-
tree: "tree"
|
|
3355
|
-
};
|
|
3356
|
-
function sanitizeClient(name) {
|
|
3357
|
-
if (typeof name !== "string") return "unknown";
|
|
3358
|
-
const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
3359
|
-
return cleaned || "unknown";
|
|
3360
|
-
}
|
|
3361
|
-
function recordCliCall(root, entry, now) {
|
|
3362
|
-
recordSaving(
|
|
3363
|
-
root,
|
|
3364
|
-
{
|
|
3365
|
-
tool: entry.tool,
|
|
3366
|
-
source: "cli",
|
|
3367
|
-
client: sanitizeClient(entry.client),
|
|
3368
|
-
outcome: entry.outcome,
|
|
3369
|
-
vgTokens: entry.vgTokens ?? 0,
|
|
3370
|
-
baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
|
|
3371
|
-
},
|
|
3372
|
-
now
|
|
3373
|
-
);
|
|
3374
|
-
}
|
|
3375
|
-
function ledgerPath(root) {
|
|
3376
|
-
return path19.join(cacheDir(root), LEDGER);
|
|
3377
|
-
}
|
|
3378
|
-
function savingsRecorded(root) {
|
|
3379
|
-
return fs19.existsSync(ledgerPath(root));
|
|
3380
|
-
}
|
|
3381
|
-
function recordSaving(root, entry, now) {
|
|
3382
|
-
try {
|
|
3383
|
-
fs19.mkdirSync(cacheDir(root), { recursive: true });
|
|
3384
|
-
const line = JSON.stringify({ ts: now, ...entry });
|
|
3385
|
-
fs19.appendFileSync(ledgerPath(root), line + "\n");
|
|
3386
|
-
} catch {
|
|
3387
|
-
}
|
|
3388
|
-
}
|
|
3389
|
-
var DEFAULT_RATE_PER_M = 3;
|
|
3390
|
-
var DEFAULT_RATE_LABEL = "input @ $3/1M";
|
|
3391
|
-
function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
3392
|
-
const file = ledgerPath(root);
|
|
3393
|
-
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
3394
|
-
let queries = 0;
|
|
3395
|
-
let vgTokens = 0;
|
|
3396
|
-
let baselineTokens = 0;
|
|
3397
|
-
if (fs19.existsSync(file)) {
|
|
3398
|
-
for (const line of fs19.readFileSync(file, "utf8").split("\n")) {
|
|
3399
|
-
if (!line.trim()) continue;
|
|
3400
|
-
try {
|
|
3401
|
-
const e = JSON.parse(line);
|
|
3402
|
-
if (e.ts < cutoff) continue;
|
|
3403
|
-
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
3404
|
-
queries++;
|
|
3405
|
-
vgTokens += e.vgTokens;
|
|
3406
|
-
baselineTokens += e.baselineTokens;
|
|
3407
|
-
} catch {
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
const estCostVg = vgTokens / 1e6 * ratePerM;
|
|
3412
|
-
const estCostBaseline = baselineTokens / 1e6 * ratePerM;
|
|
3413
|
-
return {
|
|
3414
|
-
enabled: savingsRecorded(root),
|
|
3415
|
-
days,
|
|
3416
|
-
queries,
|
|
3417
|
-
vgTokens,
|
|
3418
|
-
baselineTokens,
|
|
3419
|
-
ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
|
|
3420
|
-
estCostVg: round22(estCostVg),
|
|
3421
|
-
estCostBaseline: round22(estCostBaseline),
|
|
3422
|
-
saved: round22(estCostBaseline - estCostVg),
|
|
3423
|
-
rateLabel: DEFAULT_RATE_LABEL
|
|
3424
|
-
};
|
|
3425
|
-
}
|
|
3426
|
-
function round22(x) {
|
|
3427
|
-
return Math.round(x * 100) / 100;
|
|
3428
|
-
}
|
|
3429
|
-
function toDimensionStats(byKey) {
|
|
3430
|
-
return [...byKey.entries()].map(([key, r]) => {
|
|
3431
|
-
const calls = r.complete + r.partial + r.miss;
|
|
3432
|
-
return {
|
|
3433
|
-
key,
|
|
3434
|
-
calls,
|
|
3435
|
-
complete: r.complete,
|
|
3436
|
-
partial: r.partial,
|
|
3437
|
-
miss: r.miss,
|
|
3438
|
-
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
3439
|
-
};
|
|
3440
|
-
}).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
|
|
3441
|
-
}
|
|
3442
|
-
function readUsage(root, days, now) {
|
|
3443
|
-
const file = ledgerPath(root);
|
|
3444
|
-
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
3445
|
-
const byTool = /* @__PURE__ */ new Map();
|
|
3446
|
-
const bySource = /* @__PURE__ */ new Map();
|
|
3447
|
-
const byClient = /* @__PURE__ */ new Map();
|
|
3448
|
-
const msByTool = /* @__PURE__ */ new Map();
|
|
3449
|
-
const bump2 = (m, key, outcome) => {
|
|
3450
|
-
const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
|
|
3451
|
-
row[outcome]++;
|
|
3452
|
-
m.set(key, row);
|
|
3453
|
-
};
|
|
3454
|
-
if (fs19.existsSync(file)) {
|
|
3455
|
-
for (const line of fs19.readFileSync(file, "utf8").split("\n")) {
|
|
3456
|
-
if (!line.trim()) continue;
|
|
3457
|
-
try {
|
|
3458
|
-
const e = JSON.parse(line);
|
|
3459
|
-
if (e.ts < cutoff) continue;
|
|
3460
|
-
const outcome = e.outcome ?? "complete";
|
|
3461
|
-
bump2(byTool, e.tool, outcome);
|
|
3462
|
-
bump2(bySource, e.source ?? "mcp", outcome);
|
|
3463
|
-
bump2(byClient, e.client ?? "unknown", outcome);
|
|
3464
|
-
if (typeof e.ms === "number" && e.ms >= 0) {
|
|
3465
|
-
const timing = msByTool.get(e.tool) ?? { sum: 0, n: 0 };
|
|
3466
|
-
timing.sum += e.ms;
|
|
3467
|
-
timing.n++;
|
|
3468
|
-
msByTool.set(e.tool, timing);
|
|
3469
|
-
}
|
|
3470
|
-
} catch {
|
|
3471
|
-
}
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
const commands = toDimensionStats(byTool).map((d) => {
|
|
3475
|
-
const timing = msByTool.get(d.key);
|
|
3476
|
-
return {
|
|
3477
|
-
tool: d.key,
|
|
3478
|
-
calls: d.calls,
|
|
3479
|
-
complete: d.complete,
|
|
3480
|
-
partial: d.partial,
|
|
3481
|
-
miss: d.miss,
|
|
3482
|
-
successPct: d.successPct,
|
|
3483
|
-
avgMs: timing && timing.n > 0 ? Math.round(timing.sum / timing.n) : null
|
|
3484
|
-
};
|
|
3485
|
-
});
|
|
3486
|
-
const totals = commands.reduce(
|
|
3487
|
-
(t, c) => ({
|
|
3488
|
-
calls: t.calls + c.calls,
|
|
3489
|
-
complete: t.complete + c.complete,
|
|
3490
|
-
partial: t.partial + c.partial,
|
|
3491
|
-
miss: t.miss + c.miss
|
|
3492
|
-
}),
|
|
3493
|
-
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
3494
|
-
);
|
|
3495
|
-
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
3496
|
-
return {
|
|
3497
|
-
enabled: savingsRecorded(root),
|
|
3498
|
-
days,
|
|
3499
|
-
commands,
|
|
3500
|
-
sources: toDimensionStats(bySource),
|
|
3501
|
-
clients: toDimensionStats(byClient),
|
|
3502
|
-
totals,
|
|
3503
|
-
avgSuccessPct
|
|
3504
|
-
};
|
|
3505
|
-
}
|
|
3506
3309
|
|
|
3507
3310
|
// src/engine/lookup.ts
|
|
3508
3311
|
function findNodes(graph, query) {
|
|
@@ -3660,10 +3463,10 @@ function testsToRun(graph, rootId, depth = 4) {
|
|
|
3660
3463
|
};
|
|
3661
3464
|
}
|
|
3662
3465
|
function detectRunner(root, lang) {
|
|
3663
|
-
const pkgPath =
|
|
3664
|
-
if (
|
|
3466
|
+
const pkgPath = path17.join(root, "package.json");
|
|
3467
|
+
if (fs17.existsSync(pkgPath)) {
|
|
3665
3468
|
try {
|
|
3666
|
-
const pkg = JSON.parse(
|
|
3469
|
+
const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf8"));
|
|
3667
3470
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3668
3471
|
if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
|
|
3669
3472
|
if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
|
|
@@ -3671,10 +3474,10 @@ function detectRunner(root, lang) {
|
|
|
3671
3474
|
} catch {
|
|
3672
3475
|
}
|
|
3673
3476
|
}
|
|
3674
|
-
if (lang === "py" ||
|
|
3477
|
+
if (lang === "py" || fs17.existsSync(path17.join(root, "pyproject.toml")) || fs17.existsSync(path17.join(root, "pytest.ini"))) {
|
|
3675
3478
|
return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
|
|
3676
3479
|
}
|
|
3677
|
-
if (lang === "go" ||
|
|
3480
|
+
if (lang === "go" || fs17.existsSync(path17.join(root, "go.mod"))) {
|
|
3678
3481
|
return { name: "go test", command: () => "go test ./..." };
|
|
3679
3482
|
}
|
|
3680
3483
|
if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
|
|
@@ -3685,7 +3488,7 @@ function detectRunner(root, lang) {
|
|
|
3685
3488
|
}
|
|
3686
3489
|
function hasFile(root, re) {
|
|
3687
3490
|
try {
|
|
3688
|
-
return
|
|
3491
|
+
return fs17.readdirSync(root).some((f) => re.test(f));
|
|
3689
3492
|
} catch {
|
|
3690
3493
|
return false;
|
|
3691
3494
|
}
|
|
@@ -3757,7 +3560,7 @@ function findManifests(root) {
|
|
|
3757
3560
|
if (depth > 8 || scanned > MAX_ENTRIES2) return;
|
|
3758
3561
|
let entries;
|
|
3759
3562
|
try {
|
|
3760
|
-
entries =
|
|
3563
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
3761
3564
|
} catch {
|
|
3762
3565
|
return;
|
|
3763
3566
|
}
|
|
@@ -3765,11 +3568,11 @@ function findManifests(root) {
|
|
|
3765
3568
|
scanned++;
|
|
3766
3569
|
if (scanned > MAX_ENTRIES2) break;
|
|
3767
3570
|
if (e.isDirectory()) {
|
|
3768
|
-
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(
|
|
3571
|
+
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(path17.join(dir, e.name), depth + 1);
|
|
3769
3572
|
continue;
|
|
3770
3573
|
}
|
|
3771
3574
|
const eco = MANIFEST_BY_FILE[e.name] ?? (/\.(cs|fs)proj$/i.test(e.name) ? "dotnet" : void 0);
|
|
3772
|
-
if (eco) set[eco].push(
|
|
3575
|
+
if (eco) set[eco].push(path17.join(dir, e.name));
|
|
3773
3576
|
}
|
|
3774
3577
|
};
|
|
3775
3578
|
walk2(root, 0);
|
|
@@ -3781,11 +3584,11 @@ function npmDeps(files) {
|
|
|
3781
3584
|
for (const file of files) {
|
|
3782
3585
|
let pkg;
|
|
3783
3586
|
try {
|
|
3784
|
-
pkg = JSON.parse(
|
|
3587
|
+
pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
3785
3588
|
} catch {
|
|
3786
3589
|
continue;
|
|
3787
3590
|
}
|
|
3788
|
-
const dir =
|
|
3591
|
+
const dir = path17.dirname(file);
|
|
3789
3592
|
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3790
3593
|
for (const [name, declared] of Object.entries(all)) {
|
|
3791
3594
|
out.push({ name, ecosystem: "npm", declared, installed: installedNpmVersion(dir, name) });
|
|
@@ -3796,12 +3599,12 @@ function npmDeps(files) {
|
|
|
3796
3599
|
function installedNpmVersion(dir, name) {
|
|
3797
3600
|
let cur = dir;
|
|
3798
3601
|
for (let i = 0; i < 12; i++) {
|
|
3799
|
-
const p =
|
|
3602
|
+
const p = path17.join(cur, "node_modules", name, "package.json");
|
|
3800
3603
|
try {
|
|
3801
|
-
return JSON.parse(
|
|
3604
|
+
return JSON.parse(fs17.readFileSync(p, "utf8")).version;
|
|
3802
3605
|
} catch {
|
|
3803
3606
|
}
|
|
3804
|
-
const parent =
|
|
3607
|
+
const parent = path17.dirname(cur);
|
|
3805
3608
|
if (parent === cur) break;
|
|
3806
3609
|
cur = parent;
|
|
3807
3610
|
}
|
|
@@ -3818,21 +3621,21 @@ function normalizePep503(name) {
|
|
|
3818
3621
|
function sitePackagesDirs(base) {
|
|
3819
3622
|
const out = [];
|
|
3820
3623
|
try {
|
|
3821
|
-
for (const d of
|
|
3822
|
-
if (d.startsWith("python")) out.push(
|
|
3624
|
+
for (const d of fs17.readdirSync(path17.join(base, "lib"))) {
|
|
3625
|
+
if (d.startsWith("python")) out.push(path17.join(base, "lib", d, "site-packages"));
|
|
3823
3626
|
}
|
|
3824
3627
|
} catch {
|
|
3825
3628
|
}
|
|
3826
|
-
out.push(
|
|
3629
|
+
out.push(path17.join(base, "Lib", "site-packages"));
|
|
3827
3630
|
return out;
|
|
3828
3631
|
}
|
|
3829
3632
|
function installedPypiVersion(root, name) {
|
|
3830
3633
|
const target = normalizePep503(name);
|
|
3831
3634
|
for (const venv of [".venv", "venv", "env", ".tox"]) {
|
|
3832
|
-
for (const sp of sitePackagesDirs(
|
|
3635
|
+
for (const sp of sitePackagesDirs(path17.join(root, venv))) {
|
|
3833
3636
|
let entries;
|
|
3834
3637
|
try {
|
|
3835
|
-
entries =
|
|
3638
|
+
entries = fs17.readdirSync(sp);
|
|
3836
3639
|
} catch {
|
|
3837
3640
|
continue;
|
|
3838
3641
|
}
|
|
@@ -3847,7 +3650,7 @@ function installedPypiVersion(root, name) {
|
|
|
3847
3650
|
function installedPhpVersion(root, name) {
|
|
3848
3651
|
let data;
|
|
3849
3652
|
try {
|
|
3850
|
-
data = JSON.parse(
|
|
3653
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "vendor", "composer", "installed.json"), "utf8"));
|
|
3851
3654
|
} catch {
|
|
3852
3655
|
return void 0;
|
|
3853
3656
|
}
|
|
@@ -3866,11 +3669,11 @@ function pypiDeps(files) {
|
|
|
3866
3669
|
for (const file of files) {
|
|
3867
3670
|
let text;
|
|
3868
3671
|
try {
|
|
3869
|
-
text =
|
|
3672
|
+
text = fs17.readFileSync(file, "utf8");
|
|
3870
3673
|
} catch {
|
|
3871
3674
|
continue;
|
|
3872
3675
|
}
|
|
3873
|
-
if (
|
|
3676
|
+
if (path17.basename(file) === "requirements.txt") {
|
|
3874
3677
|
for (const line of text.split("\n")) {
|
|
3875
3678
|
const t = line.trim();
|
|
3876
3679
|
if (!t || t.startsWith("#") || t.startsWith("-")) continue;
|
|
@@ -3954,7 +3757,7 @@ function goDeps(files) {
|
|
|
3954
3757
|
for (const mod of files) {
|
|
3955
3758
|
let text;
|
|
3956
3759
|
try {
|
|
3957
|
-
text =
|
|
3760
|
+
text = fs17.readFileSync(mod, "utf8");
|
|
3958
3761
|
} catch {
|
|
3959
3762
|
continue;
|
|
3960
3763
|
}
|
|
@@ -3972,7 +3775,7 @@ function cargoDeps(files) {
|
|
|
3972
3775
|
for (const file of files) {
|
|
3973
3776
|
let text;
|
|
3974
3777
|
try {
|
|
3975
|
-
text =
|
|
3778
|
+
text = fs17.readFileSync(file, "utf8");
|
|
3976
3779
|
} catch {
|
|
3977
3780
|
continue;
|
|
3978
3781
|
}
|
|
@@ -4004,7 +3807,7 @@ function rubyDeps(files) {
|
|
|
4004
3807
|
for (const file of files) {
|
|
4005
3808
|
let text;
|
|
4006
3809
|
try {
|
|
4007
|
-
text =
|
|
3810
|
+
text = fs17.readFileSync(file, "utf8");
|
|
4008
3811
|
} catch {
|
|
4009
3812
|
continue;
|
|
4010
3813
|
}
|
|
@@ -4019,7 +3822,7 @@ function phpDeps(files) {
|
|
|
4019
3822
|
for (const file of files) {
|
|
4020
3823
|
let pkg;
|
|
4021
3824
|
try {
|
|
4022
|
-
pkg = JSON.parse(
|
|
3825
|
+
pkg = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
4023
3826
|
} catch {
|
|
4024
3827
|
continue;
|
|
4025
3828
|
}
|
|
@@ -4036,7 +3839,7 @@ function dotnetDeps(files) {
|
|
|
4036
3839
|
for (const file of files) {
|
|
4037
3840
|
let text;
|
|
4038
3841
|
try {
|
|
4039
|
-
text =
|
|
3842
|
+
text = fs17.readFileSync(file, "utf8");
|
|
4040
3843
|
} catch {
|
|
4041
3844
|
continue;
|
|
4042
3845
|
}
|
|
@@ -4056,7 +3859,7 @@ function swiftDeps(files) {
|
|
|
4056
3859
|
for (const file of files) {
|
|
4057
3860
|
let text;
|
|
4058
3861
|
try {
|
|
4059
|
-
text =
|
|
3862
|
+
text = fs17.readFileSync(file, "utf8");
|
|
4060
3863
|
} catch {
|
|
4061
3864
|
continue;
|
|
4062
3865
|
}
|
|
@@ -4078,7 +3881,7 @@ function dartDeps(files) {
|
|
|
4078
3881
|
for (const file of files) {
|
|
4079
3882
|
let text;
|
|
4080
3883
|
try {
|
|
4081
|
-
text =
|
|
3884
|
+
text = fs17.readFileSync(file, "utf8");
|
|
4082
3885
|
} catch {
|
|
4083
3886
|
continue;
|
|
4084
3887
|
}
|
|
@@ -4103,11 +3906,11 @@ function javaDeps(files) {
|
|
|
4103
3906
|
for (const file of files) {
|
|
4104
3907
|
let text;
|
|
4105
3908
|
try {
|
|
4106
|
-
text =
|
|
3909
|
+
text = fs17.readFileSync(file, "utf8");
|
|
4107
3910
|
} catch {
|
|
4108
3911
|
continue;
|
|
4109
3912
|
}
|
|
4110
|
-
if (
|
|
3913
|
+
if (path17.basename(file) === "pom.xml") {
|
|
4111
3914
|
for (const block of text.match(/<dependency>[\s\S]*?<\/dependency>/g) ?? []) {
|
|
4112
3915
|
const g = /<groupId>([^<]+)<\/groupId>/.exec(block);
|
|
4113
3916
|
const a = /<artifactId>([^<]+)<\/artifactId>/.exec(block);
|
|
@@ -4153,10 +3956,10 @@ function discoverModels(home = os.homedir()) {
|
|
|
4153
3956
|
);
|
|
4154
3957
|
}
|
|
4155
3958
|
function ollama(home) {
|
|
4156
|
-
const base =
|
|
3959
|
+
const base = path17.join(home, ".ollama", "models", "manifests");
|
|
4157
3960
|
const out = [];
|
|
4158
3961
|
walk(base, 4, (file) => {
|
|
4159
|
-
const rel2 =
|
|
3962
|
+
const rel2 = path17.relative(base, file).split(path17.sep);
|
|
4160
3963
|
if (rel2.length >= 2) {
|
|
4161
3964
|
const tag = rel2[rel2.length - 1];
|
|
4162
3965
|
const model = rel2[rel2.length - 2];
|
|
@@ -4166,24 +3969,24 @@ function ollama(home) {
|
|
|
4166
3969
|
return dedupe(out);
|
|
4167
3970
|
}
|
|
4168
3971
|
function lmStudio(home) {
|
|
4169
|
-
const bases = [
|
|
3972
|
+
const bases = [path17.join(home, ".lmstudio", "models"), path17.join(home, ".cache", "lm-studio", "models")];
|
|
4170
3973
|
const out = [];
|
|
4171
3974
|
for (const base of bases) {
|
|
4172
3975
|
walk(base, 5, (file) => {
|
|
4173
3976
|
if (!file.endsWith(".gguf")) return;
|
|
4174
|
-
const rel2 =
|
|
3977
|
+
const rel2 = path17.relative(base, file).replace(/\\/g, "/");
|
|
4175
3978
|
out.push({ runtime: "lm-studio", name: rel2, path: file });
|
|
4176
3979
|
});
|
|
4177
3980
|
}
|
|
4178
3981
|
return dedupe(out);
|
|
4179
3982
|
}
|
|
4180
3983
|
function looseGguf(home) {
|
|
4181
|
-
const bases = [
|
|
3984
|
+
const bases = [path17.join(home, "models"), path17.join(home, ".cache", "huggingface")];
|
|
4182
3985
|
const out = [];
|
|
4183
3986
|
for (const base of bases) {
|
|
4184
3987
|
walk(base, 4, (file) => {
|
|
4185
3988
|
if (!file.endsWith(".gguf")) return;
|
|
4186
|
-
out.push({ runtime: "gguf", name:
|
|
3989
|
+
out.push({ runtime: "gguf", name: path17.basename(file), path: file });
|
|
4187
3990
|
});
|
|
4188
3991
|
}
|
|
4189
3992
|
return dedupe(out);
|
|
@@ -4192,12 +3995,12 @@ function walk(dir, depth, onFile) {
|
|
|
4192
3995
|
if (depth < 0) return;
|
|
4193
3996
|
let entries;
|
|
4194
3997
|
try {
|
|
4195
|
-
entries =
|
|
3998
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
4196
3999
|
} catch {
|
|
4197
4000
|
return;
|
|
4198
4001
|
}
|
|
4199
4002
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4200
|
-
const abs =
|
|
4003
|
+
const abs = path17.join(dir, e.name);
|
|
4201
4004
|
if (e.isDirectory()) walk(abs, depth - 1, onFile);
|
|
4202
4005
|
else if (e.isFile()) onFile(abs);
|
|
4203
4006
|
}
|
|
@@ -4228,7 +4031,7 @@ function lockfileVersion(root, ecosystem, name) {
|
|
|
4228
4031
|
function gradleLock(root, name) {
|
|
4229
4032
|
let text;
|
|
4230
4033
|
try {
|
|
4231
|
-
text =
|
|
4034
|
+
text = fs17.readFileSync(path17.join(root, "gradle.lockfile"), "utf8");
|
|
4232
4035
|
} catch {
|
|
4233
4036
|
return void 0;
|
|
4234
4037
|
}
|
|
@@ -4238,7 +4041,7 @@ function gradleLock(root, name) {
|
|
|
4238
4041
|
function packageResolved(root, name) {
|
|
4239
4042
|
let data;
|
|
4240
4043
|
try {
|
|
4241
|
-
data = JSON.parse(
|
|
4044
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "Package.resolved"), "utf8"));
|
|
4242
4045
|
} catch {
|
|
4243
4046
|
return void 0;
|
|
4244
4047
|
}
|
|
@@ -4254,7 +4057,7 @@ function packageResolved(root, name) {
|
|
|
4254
4057
|
function pubspecLock(root, name) {
|
|
4255
4058
|
let text;
|
|
4256
4059
|
try {
|
|
4257
|
-
text =
|
|
4060
|
+
text = fs17.readFileSync(path17.join(root, "pubspec.lock"), "utf8");
|
|
4258
4061
|
} catch {
|
|
4259
4062
|
return void 0;
|
|
4260
4063
|
}
|
|
@@ -4278,7 +4081,7 @@ function escapeRegExp(s) {
|
|
|
4278
4081
|
function gemfileLock(root, name) {
|
|
4279
4082
|
let text;
|
|
4280
4083
|
try {
|
|
4281
|
-
text =
|
|
4084
|
+
text = fs17.readFileSync(path17.join(root, "Gemfile.lock"), "utf8");
|
|
4282
4085
|
} catch {
|
|
4283
4086
|
return void 0;
|
|
4284
4087
|
}
|
|
@@ -4288,7 +4091,7 @@ function gemfileLock(root, name) {
|
|
|
4288
4091
|
function composerLock(root, name) {
|
|
4289
4092
|
let data;
|
|
4290
4093
|
try {
|
|
4291
|
-
data = JSON.parse(
|
|
4094
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "composer.lock"), "utf8"));
|
|
4292
4095
|
} catch {
|
|
4293
4096
|
return void 0;
|
|
4294
4097
|
}
|
|
@@ -4303,7 +4106,7 @@ function composerLock(root, name) {
|
|
|
4303
4106
|
function packagesLock(root, name) {
|
|
4304
4107
|
let data;
|
|
4305
4108
|
try {
|
|
4306
|
-
data = JSON.parse(
|
|
4109
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "packages.lock.json"), "utf8"));
|
|
4307
4110
|
} catch {
|
|
4308
4111
|
return void 0;
|
|
4309
4112
|
}
|
|
@@ -4328,7 +4131,7 @@ function pypiLockVersion(root, name) {
|
|
|
4328
4131
|
function tomlPackageLock(root, file, name, normalize3) {
|
|
4329
4132
|
let text;
|
|
4330
4133
|
try {
|
|
4331
|
-
text =
|
|
4134
|
+
text = fs17.readFileSync(path17.join(root, file), "utf8");
|
|
4332
4135
|
} catch {
|
|
4333
4136
|
return void 0;
|
|
4334
4137
|
}
|
|
@@ -4345,7 +4148,7 @@ function tomlPackageLock(root, file, name, normalize3) {
|
|
|
4345
4148
|
function pipfileLock(root, name) {
|
|
4346
4149
|
let data;
|
|
4347
4150
|
try {
|
|
4348
|
-
data = JSON.parse(
|
|
4151
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "Pipfile.lock"), "utf8"));
|
|
4349
4152
|
} catch {
|
|
4350
4153
|
return void 0;
|
|
4351
4154
|
}
|
|
@@ -4364,7 +4167,7 @@ function pipfileLock(root, name) {
|
|
|
4364
4167
|
function packageLockVersion(root, name) {
|
|
4365
4168
|
let data;
|
|
4366
4169
|
try {
|
|
4367
|
-
data = JSON.parse(
|
|
4170
|
+
data = JSON.parse(fs17.readFileSync(path17.join(root, "package-lock.json"), "utf8"));
|
|
4368
4171
|
} catch {
|
|
4369
4172
|
return void 0;
|
|
4370
4173
|
}
|
|
@@ -4376,7 +4179,7 @@ function packageLockVersion(root, name) {
|
|
|
4376
4179
|
function yarnLockVersion(root, name) {
|
|
4377
4180
|
let text;
|
|
4378
4181
|
try {
|
|
4379
|
-
text =
|
|
4182
|
+
text = fs17.readFileSync(path17.join(root, "yarn.lock"), "utf8");
|
|
4380
4183
|
} catch {
|
|
4381
4184
|
return void 0;
|
|
4382
4185
|
}
|
|
@@ -4404,19 +4207,19 @@ function headerNamesPackage(header, name) {
|
|
|
4404
4207
|
// src/engine/lib.ts
|
|
4405
4208
|
var LIB_SCHEMA = "vg-lib/1.0";
|
|
4406
4209
|
function catalogPath(root) {
|
|
4407
|
-
return
|
|
4210
|
+
return path17.join(root, "vibgrate.lib.json");
|
|
4408
4211
|
}
|
|
4409
4212
|
function libDir(root) {
|
|
4410
|
-
return
|
|
4213
|
+
return path17.join(root, ".vibgrate", "lib");
|
|
4411
4214
|
}
|
|
4412
4215
|
function libId(name) {
|
|
4413
4216
|
return name.trim().toLowerCase().replace(/^@/, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
4414
4217
|
}
|
|
4415
4218
|
function loadCatalog(root) {
|
|
4416
4219
|
const file = catalogPath(root);
|
|
4417
|
-
if (
|
|
4220
|
+
if (fs17.existsSync(file)) {
|
|
4418
4221
|
try {
|
|
4419
|
-
const data = JSON.parse(
|
|
4222
|
+
const data = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
4420
4223
|
if (data.schemaVersion === LIB_SCHEMA && data.libraries) return data;
|
|
4421
4224
|
} catch {
|
|
4422
4225
|
}
|
|
@@ -4424,7 +4227,7 @@ function loadCatalog(root) {
|
|
|
4424
4227
|
return { schemaVersion: LIB_SCHEMA, libraries: {} };
|
|
4425
4228
|
}
|
|
4426
4229
|
function saveCatalog(root, catalog) {
|
|
4427
|
-
|
|
4230
|
+
fs17.writeFileSync(catalogPath(root), `${stableStringify(catalog, 2)}
|
|
4428
4231
|
`);
|
|
4429
4232
|
}
|
|
4430
4233
|
function resolveLib(catalog, name) {
|
|
@@ -4500,23 +4303,23 @@ async function addLibrary(source, opts) {
|
|
|
4500
4303
|
content = await res.text();
|
|
4501
4304
|
type = source.endsWith("llms.txt") ? "llms.txt" : source.endsWith(".json") ? "openapi" : "website";
|
|
4502
4305
|
} else {
|
|
4503
|
-
const abs =
|
|
4504
|
-
if (!
|
|
4306
|
+
const abs = path17.resolve(root, source);
|
|
4307
|
+
if (!fs17.existsSync(abs)) throw new Error(`source not found: ${source}`);
|
|
4505
4308
|
content = readLocal(abs);
|
|
4506
4309
|
type = "local";
|
|
4507
4310
|
}
|
|
4508
4311
|
const name = opts.name ?? inferName(source);
|
|
4509
4312
|
const id = libId(name);
|
|
4510
4313
|
const version = opts.version ?? installedVersion(root, name) ?? "*";
|
|
4511
|
-
|
|
4512
|
-
const docFileAbs =
|
|
4513
|
-
|
|
4314
|
+
fs17.mkdirSync(libDir(root), { recursive: true });
|
|
4315
|
+
const docFileAbs = path17.join(libDir(root), `${id}.md`);
|
|
4316
|
+
fs17.writeFileSync(docFileAbs, content);
|
|
4514
4317
|
const entry = {
|
|
4515
4318
|
id,
|
|
4516
4319
|
name,
|
|
4517
4320
|
version,
|
|
4518
4321
|
source: { type, location: source },
|
|
4519
|
-
docFile:
|
|
4322
|
+
docFile: path17.relative(root, docFileAbs).split(path17.sep).join("/"),
|
|
4520
4323
|
docHash: hashString(content),
|
|
4521
4324
|
bytes: Buffer.byteLength(content, "utf8")
|
|
4522
4325
|
};
|
|
@@ -4526,15 +4329,15 @@ async function addLibrary(source, opts) {
|
|
|
4526
4329
|
return entry;
|
|
4527
4330
|
}
|
|
4528
4331
|
function readDoc(root, entry) {
|
|
4529
|
-
const abs =
|
|
4530
|
-
return
|
|
4332
|
+
const abs = path17.resolve(root, entry.docFile);
|
|
4333
|
+
return fs17.existsSync(abs) ? fs17.readFileSync(abs, "utf8") : "";
|
|
4531
4334
|
}
|
|
4532
4335
|
function npmPackageDir(root, name) {
|
|
4533
4336
|
let cur = root;
|
|
4534
4337
|
for (let i = 0; i < 12; i++) {
|
|
4535
|
-
const dir =
|
|
4536
|
-
if (
|
|
4537
|
-
const parent =
|
|
4338
|
+
const dir = path17.join(cur, "node_modules", name);
|
|
4339
|
+
if (fs17.existsSync(path17.join(dir, "package.json"))) return dir;
|
|
4340
|
+
const parent = path17.dirname(cur);
|
|
4538
4341
|
if (parent === cur) break;
|
|
4539
4342
|
cur = parent;
|
|
4540
4343
|
}
|
|
@@ -4545,24 +4348,24 @@ function localPackageDocs(root, name) {
|
|
|
4545
4348
|
if (!dir) return void 0;
|
|
4546
4349
|
const version = resolveVersion(root, name).served;
|
|
4547
4350
|
for (const f of ["llms.txt", "README.md", "README.mdx", "README", "readme.md"]) {
|
|
4548
|
-
const p =
|
|
4351
|
+
const p = path17.join(dir, f);
|
|
4549
4352
|
try {
|
|
4550
|
-
const docs =
|
|
4353
|
+
const docs = fs17.readFileSync(p, "utf8");
|
|
4551
4354
|
if (docs.trim()) {
|
|
4552
|
-
return { docs, version, source:
|
|
4355
|
+
return { docs, version, source: path17.relative(root, p).split(path17.sep).join("/") };
|
|
4553
4356
|
}
|
|
4554
4357
|
} catch {
|
|
4555
4358
|
}
|
|
4556
4359
|
}
|
|
4557
4360
|
try {
|
|
4558
|
-
const pkg = JSON.parse(
|
|
4361
|
+
const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
|
|
4559
4362
|
if (pkg.description?.trim()) {
|
|
4560
4363
|
return {
|
|
4561
4364
|
docs: `# ${name}
|
|
4562
4365
|
|
|
4563
4366
|
${pkg.description}`,
|
|
4564
4367
|
version: version ?? pkg.version,
|
|
4565
|
-
source:
|
|
4368
|
+
source: path17.relative(root, path17.join(dir, "package.json")).split(path17.sep).join("/")
|
|
4566
4369
|
};
|
|
4567
4370
|
}
|
|
4568
4371
|
} catch {
|
|
@@ -4571,17 +4374,17 @@ ${pkg.description}`,
|
|
|
4571
4374
|
}
|
|
4572
4375
|
function dtsEntry(dir) {
|
|
4573
4376
|
try {
|
|
4574
|
-
const pkg = JSON.parse(
|
|
4377
|
+
const pkg = JSON.parse(fs17.readFileSync(path17.join(dir, "package.json"), "utf8"));
|
|
4575
4378
|
const rel2 = pkg.types ?? pkg.typings;
|
|
4576
4379
|
if (typeof rel2 === "string") {
|
|
4577
|
-
const p =
|
|
4578
|
-
if (
|
|
4380
|
+
const p = path17.join(dir, rel2);
|
|
4381
|
+
if (fs17.existsSync(p)) return p;
|
|
4579
4382
|
}
|
|
4580
4383
|
} catch {
|
|
4581
4384
|
}
|
|
4582
4385
|
for (const c of ["index.d.ts", "dist/index.d.ts", "lib/index.d.ts", "types/index.d.ts"]) {
|
|
4583
|
-
const p =
|
|
4584
|
-
if (
|
|
4386
|
+
const p = path17.join(dir, c);
|
|
4387
|
+
if (fs17.existsSync(p)) return p;
|
|
4585
4388
|
}
|
|
4586
4389
|
return void 0;
|
|
4587
4390
|
}
|
|
@@ -4614,7 +4417,7 @@ function localApiSurface(root, name) {
|
|
|
4614
4417
|
if (!dts) return void 0;
|
|
4615
4418
|
let src;
|
|
4616
4419
|
try {
|
|
4617
|
-
src =
|
|
4420
|
+
src = fs17.readFileSync(dts, "utf8");
|
|
4618
4421
|
} catch {
|
|
4619
4422
|
return void 0;
|
|
4620
4423
|
}
|
|
@@ -4622,36 +4425,36 @@ function localApiSurface(root, name) {
|
|
|
4622
4425
|
return decls.length ? decls.join("\n\n") : void 0;
|
|
4623
4426
|
}
|
|
4624
4427
|
function cloneAndReadGit(url) {
|
|
4625
|
-
const dir =
|
|
4428
|
+
const dir = fs17.mkdtempSync(path17.join(os.tmpdir(), "vg-lib-git-"));
|
|
4626
4429
|
try {
|
|
4627
4430
|
execFileSync("git", ["clone", "--depth", "1", "--quiet", url, dir], {
|
|
4628
4431
|
stdio: ["ignore", "ignore", "pipe"]
|
|
4629
4432
|
});
|
|
4630
4433
|
} catch (err) {
|
|
4631
|
-
|
|
4434
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
4632
4435
|
const detail = err instanceof Error && err.message ? `: ${err.message.split("\n")[0]}` : "";
|
|
4633
4436
|
throw new Error(`git clone failed for ${url}${detail}`);
|
|
4634
4437
|
}
|
|
4635
4438
|
try {
|
|
4636
4439
|
return readGitDocs(dir);
|
|
4637
4440
|
} finally {
|
|
4638
|
-
|
|
4441
|
+
fs17.rmSync(dir, { recursive: true, force: true });
|
|
4639
4442
|
}
|
|
4640
4443
|
}
|
|
4641
4444
|
function readGitDocs(dir) {
|
|
4642
|
-
const docsDir =
|
|
4643
|
-
if (
|
|
4644
|
-
const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) =>
|
|
4645
|
-
if (readme) return
|
|
4445
|
+
const docsDir = path17.join(dir, "docs");
|
|
4446
|
+
if (fs17.existsSync(docsDir) && fs17.statSync(docsDir).isDirectory()) return readLocal(docsDir);
|
|
4447
|
+
const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) => path17.join(dir, f)).find((p) => fs17.existsSync(p));
|
|
4448
|
+
if (readme) return fs17.readFileSync(readme, "utf8");
|
|
4646
4449
|
return readLocal(dir);
|
|
4647
4450
|
}
|
|
4648
4451
|
function readLocal(abs) {
|
|
4649
|
-
const stat =
|
|
4650
|
-
if (stat.isFile()) return
|
|
4452
|
+
const stat = fs17.statSync(abs);
|
|
4453
|
+
if (stat.isFile()) return fs17.readFileSync(abs, "utf8");
|
|
4651
4454
|
const parts = [];
|
|
4652
|
-
const files =
|
|
4455
|
+
const files = fs17.readdirSync(abs, { withFileTypes: true }).filter((e) => e.isFile() && /\.(md|mdx|txt|rst)$/i.test(e.name)).map((e) => e.name).sort();
|
|
4653
4456
|
for (const f of files) parts.push(`<!-- ${f} -->
|
|
4654
|
-
${
|
|
4457
|
+
${fs17.readFileSync(path17.join(abs, f), "utf8")}`);
|
|
4655
4458
|
return parts.join("\n\n");
|
|
4656
4459
|
}
|
|
4657
4460
|
function inferName(source) {
|
|
@@ -4660,10 +4463,10 @@ function inferName(source) {
|
|
|
4660
4463
|
return base.replace(/\.(md|mdx|txt|rst|json)$/i, "").replace(/^llms$/, "docs");
|
|
4661
4464
|
}
|
|
4662
4465
|
function findGitRoot(startDir = process.cwd()) {
|
|
4663
|
-
let dir =
|
|
4466
|
+
let dir = path17.resolve(startDir);
|
|
4664
4467
|
for (; ; ) {
|
|
4665
|
-
if (
|
|
4666
|
-
const parent =
|
|
4468
|
+
if (fs17.existsSync(path17.join(dir, ".git"))) return dir;
|
|
4469
|
+
const parent = path17.dirname(dir);
|
|
4667
4470
|
if (parent === dir) return null;
|
|
4668
4471
|
dir = parent;
|
|
4669
4472
|
}
|
|
@@ -4674,19 +4477,19 @@ function normalizeEntry(line) {
|
|
|
4674
4477
|
function ensureGitignored(entry, startDir = process.cwd()) {
|
|
4675
4478
|
const root = findGitRoot(startDir);
|
|
4676
4479
|
if (!root) return { status: "not-a-repo", entry };
|
|
4677
|
-
const gitignorePath =
|
|
4480
|
+
const gitignorePath = path17.join(root, ".gitignore");
|
|
4678
4481
|
const target = normalizeEntry(entry);
|
|
4679
4482
|
let existing = "";
|
|
4680
4483
|
let fileExisted = true;
|
|
4681
4484
|
try {
|
|
4682
|
-
existing =
|
|
4485
|
+
existing = fs17.readFileSync(gitignorePath, "utf8");
|
|
4683
4486
|
} catch {
|
|
4684
4487
|
fileExisted = false;
|
|
4685
4488
|
}
|
|
4686
4489
|
const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
|
|
4687
4490
|
if (alreadyPresent) return { status: "present", entry, gitignorePath };
|
|
4688
4491
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
4689
|
-
|
|
4492
|
+
fs17.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
|
|
4690
4493
|
`, "utf8");
|
|
4691
4494
|
return { status: fileExisted ? "added" : "created", entry, gitignorePath };
|
|
4692
4495
|
}
|
|
@@ -4840,14 +4643,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
4840
4643
|
console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
4841
4644
|
}
|
|
4842
4645
|
if (opts.write) {
|
|
4843
|
-
const writePath =
|
|
4646
|
+
const writePath = path17.resolve(opts.write);
|
|
4844
4647
|
await writeTextFile(writePath, dsn + "\n");
|
|
4845
4648
|
console.log("");
|
|
4846
4649
|
console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
4847
|
-
const root = findGitRoot(
|
|
4848
|
-
const rel2 = root ?
|
|
4650
|
+
const root = findGitRoot(path17.dirname(writePath));
|
|
4651
|
+
const rel2 = root ? path17.relative(root, writePath).split(path17.sep).join("/") : "";
|
|
4849
4652
|
if (root && rel2 && !rel2.startsWith("..")) {
|
|
4850
|
-
const res = ensureGitignored(rel2,
|
|
4653
|
+
const res = ensureGitignored(rel2, path17.dirname(writePath));
|
|
4851
4654
|
if (res.status === "created") {
|
|
4852
4655
|
console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
|
|
4853
4656
|
} else if (res.status === "added") {
|
|
@@ -4865,30 +4668,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
4865
4668
|
var STORE_DIRNAME = ".vibgrate";
|
|
4866
4669
|
var STORE_FILENAME = "credentials.json";
|
|
4867
4670
|
function homeCredentialsPath() {
|
|
4868
|
-
return
|
|
4671
|
+
return path17.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
|
|
4869
4672
|
}
|
|
4870
4673
|
function projectCredentialsPath(cwd = process.cwd()) {
|
|
4871
4674
|
const root = findGitRoot(cwd) ?? cwd;
|
|
4872
|
-
return
|
|
4675
|
+
return path17.join(root, STORE_DIRNAME, STORE_FILENAME);
|
|
4873
4676
|
}
|
|
4874
4677
|
function credentialsPath(opts = {}) {
|
|
4875
4678
|
const override = process.env.VIBGRATE_CREDENTIALS?.trim();
|
|
4876
|
-
if (override) return
|
|
4679
|
+
if (override) return path17.resolve(override);
|
|
4877
4680
|
const local = projectCredentialsPath(opts.cwd);
|
|
4878
4681
|
if (opts.local) return local;
|
|
4879
|
-
if (
|
|
4682
|
+
if (fs17.existsSync(local)) return local;
|
|
4880
4683
|
return homeCredentialsPath();
|
|
4881
4684
|
}
|
|
4882
4685
|
function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
|
|
4883
|
-
const rel2 =
|
|
4884
|
-
if (rel2 && !rel2.startsWith("..") && !
|
|
4885
|
-
return rel2.split(
|
|
4686
|
+
const rel2 = path17.relative(repoRoot, credsFile);
|
|
4687
|
+
if (rel2 && !rel2.startsWith("..") && !path17.isAbsolute(rel2)) {
|
|
4688
|
+
return rel2.split(path17.sep).join("/");
|
|
4886
4689
|
}
|
|
4887
4690
|
return `${STORE_DIRNAME}/${STORE_FILENAME}`;
|
|
4888
4691
|
}
|
|
4889
4692
|
function readStoredCredentials(opts = {}) {
|
|
4890
4693
|
try {
|
|
4891
|
-
const raw =
|
|
4694
|
+
const raw = fs17.readFileSync(credentialsPath(opts), "utf8");
|
|
4892
4695
|
const parsed = JSON.parse(raw);
|
|
4893
4696
|
return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
|
|
4894
4697
|
} catch {
|
|
@@ -4897,16 +4700,16 @@ function readStoredCredentials(opts = {}) {
|
|
|
4897
4700
|
}
|
|
4898
4701
|
function writeStoredCredentials(creds, opts = {}) {
|
|
4899
4702
|
const file = credentialsPath(opts);
|
|
4900
|
-
|
|
4901
|
-
|
|
4703
|
+
fs17.mkdirSync(path17.dirname(file), { recursive: true });
|
|
4704
|
+
fs17.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
4902
4705
|
try {
|
|
4903
|
-
|
|
4706
|
+
fs17.chmodSync(file, 384);
|
|
4904
4707
|
} catch {
|
|
4905
4708
|
}
|
|
4906
4709
|
}
|
|
4907
4710
|
function clearStoredCredentials(opts = {}) {
|
|
4908
4711
|
try {
|
|
4909
|
-
|
|
4712
|
+
fs17.rmSync(credentialsPath(opts));
|
|
4910
4713
|
return true;
|
|
4911
4714
|
} catch {
|
|
4912
4715
|
return false;
|
|
@@ -5277,7 +5080,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
5277
5080
|
if (opts.strict) process.exit(1);
|
|
5278
5081
|
return;
|
|
5279
5082
|
}
|
|
5280
|
-
const filePath =
|
|
5083
|
+
const filePath = path17.resolve(opts.file);
|
|
5281
5084
|
if (!await pathExists(filePath)) {
|
|
5282
5085
|
console.error(chalk.red(`Scan artifact not found: ${filePath}`));
|
|
5283
5086
|
console.error(chalk.dim('Run "vibgrate scan" first.'));
|
|
@@ -5340,8 +5143,8 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
5340
5143
|
});
|
|
5341
5144
|
function readScanArtifact(root) {
|
|
5342
5145
|
try {
|
|
5343
|
-
const file =
|
|
5344
|
-
return JSON.parse(
|
|
5146
|
+
const file = path17.join(root, ".vibgrate", "scan_result.json");
|
|
5147
|
+
return JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
5345
5148
|
} catch {
|
|
5346
5149
|
return null;
|
|
5347
5150
|
}
|
|
@@ -5731,7 +5534,7 @@ var CACHE_VERSION2 = "vg-hosted-docs/1";
|
|
|
5731
5534
|
var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5732
5535
|
var MAX_ENTRIES = 64;
|
|
5733
5536
|
function cachePath2(root) {
|
|
5734
|
-
return
|
|
5537
|
+
return path17.join(cacheDir(root), "hosted-docs.json");
|
|
5735
5538
|
}
|
|
5736
5539
|
function hostedDocsCacheKey(req, opts = {}) {
|
|
5737
5540
|
const material = JSON.stringify([
|
|
@@ -5747,7 +5550,7 @@ function hostedDocsCacheKey(req, opts = {}) {
|
|
|
5747
5550
|
}
|
|
5748
5551
|
function loadFile(root) {
|
|
5749
5552
|
try {
|
|
5750
|
-
const parsed = JSON.parse(
|
|
5553
|
+
const parsed = JSON.parse(fs17.readFileSync(cachePath2(root), "utf8"));
|
|
5751
5554
|
if (parsed && parsed.version === CACHE_VERSION2 && parsed.entries && typeof parsed.entries === "object") return parsed;
|
|
5752
5555
|
} catch {
|
|
5753
5556
|
}
|
|
@@ -5761,8 +5564,8 @@ function saveFile(root, file) {
|
|
|
5761
5564
|
delete file.entries[k];
|
|
5762
5565
|
}
|
|
5763
5566
|
}
|
|
5764
|
-
|
|
5765
|
-
|
|
5567
|
+
fs17.mkdirSync(cacheDir(root), { recursive: true });
|
|
5568
|
+
fs17.writeFileSync(cachePath2(root), JSON.stringify(file));
|
|
5766
5569
|
} catch {
|
|
5767
5570
|
}
|
|
5768
5571
|
}
|
|
@@ -5881,7 +5684,7 @@ function measure(root, files) {
|
|
|
5881
5684
|
for (const rel2 of files) {
|
|
5882
5685
|
let size;
|
|
5883
5686
|
try {
|
|
5884
|
-
size =
|
|
5687
|
+
size = fs17.statSync(path17.join(root, rel2)).size;
|
|
5885
5688
|
} catch {
|
|
5886
5689
|
continue;
|
|
5887
5690
|
}
|
|
@@ -5904,9 +5707,9 @@ function matchLines(text, needleLower) {
|
|
|
5904
5707
|
}
|
|
5905
5708
|
function readCandidate(abs, knownSize) {
|
|
5906
5709
|
try {
|
|
5907
|
-
const size = knownSize ??
|
|
5710
|
+
const size = knownSize ?? fs17.statSync(abs).size;
|
|
5908
5711
|
if (size > MAX_FILE_BYTES) return null;
|
|
5909
|
-
const text =
|
|
5712
|
+
const text = fs17.readFileSync(abs, "utf8");
|
|
5910
5713
|
return text.includes(NUL) ? null : text;
|
|
5911
5714
|
} catch {
|
|
5912
5715
|
return null;
|
|
@@ -5917,7 +5720,7 @@ function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
|
|
|
5917
5720
|
let total = 0;
|
|
5918
5721
|
let truncated = false;
|
|
5919
5722
|
for (const rel2 of files) {
|
|
5920
|
-
const text = readCandidate(
|
|
5723
|
+
const text = readCandidate(path17.join(root, rel2), sizes?.get(rel2));
|
|
5921
5724
|
if (text === null) continue;
|
|
5922
5725
|
for (const h of matchLines(text, needleLower)) {
|
|
5923
5726
|
total++;
|
|
@@ -6096,13 +5899,13 @@ function walkCandidates(root) {
|
|
|
6096
5899
|
const dir = stack.pop();
|
|
6097
5900
|
let entries;
|
|
6098
5901
|
try {
|
|
6099
|
-
entries =
|
|
5902
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
6100
5903
|
} catch {
|
|
6101
5904
|
continue;
|
|
6102
5905
|
}
|
|
6103
5906
|
for (const e of entries) {
|
|
6104
5907
|
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
6105
|
-
const abs =
|
|
5908
|
+
const abs = path17.join(dir, e.name);
|
|
6106
5909
|
if (e.isDirectory()) {
|
|
6107
5910
|
stack.push(abs);
|
|
6108
5911
|
continue;
|
|
@@ -6111,7 +5914,7 @@ function walkCandidates(root) {
|
|
|
6111
5914
|
truncated = true;
|
|
6112
5915
|
continue;
|
|
6113
5916
|
}
|
|
6114
|
-
files.push(
|
|
5917
|
+
files.push(path17.relative(root, abs));
|
|
6115
5918
|
}
|
|
6116
5919
|
}
|
|
6117
5920
|
files.sort();
|
|
@@ -6827,211 +6630,19 @@ function navigationToolsetConfig(serverName = "vibgrate") {
|
|
|
6827
6630
|
configs: Object.fromEntries(HOT_TOOLS.map((n) => [n, { defer_loading: false }]))
|
|
6828
6631
|
};
|
|
6829
6632
|
}
|
|
6830
|
-
var PROBE_INTERVAL_MS = 2e3;
|
|
6831
|
-
var MAX_PROBE_INTERVAL_MS = 3e4;
|
|
6832
|
-
var PROBE_DUTY_FACTOR = 20;
|
|
6833
|
-
var REFRESH_BUDGET_MS = 5e3;
|
|
6834
|
-
var FAILURE_COOLDOWN_MS = 6e4;
|
|
6835
|
-
var GraphSource = class {
|
|
6836
|
-
constructor(graphPath, refresh = false, tuning = {}) {
|
|
6837
|
-
this.graphPath = graphPath;
|
|
6838
|
-
this.refresh = refresh;
|
|
6839
|
-
this.tuning = tuning;
|
|
6840
|
-
this.root = path19.dirname(path19.dirname(graphPath));
|
|
6841
|
-
}
|
|
6842
|
-
graphPath;
|
|
6843
|
-
refresh;
|
|
6844
|
-
tuning;
|
|
6845
|
-
cachedMtimeMs = -1;
|
|
6846
|
-
cached = null;
|
|
6847
|
-
root;
|
|
6848
|
-
lastProbeAt = 0;
|
|
6849
|
-
failedUntil = 0;
|
|
6850
|
-
inflight = null;
|
|
6851
|
-
/** Self-tuned: grows with measured probe cost so huge repos aren't penalized. */
|
|
6852
|
-
probeIntervalMs = PROBE_INTERVAL_MS;
|
|
6853
|
-
/** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */
|
|
6854
|
-
async get() {
|
|
6855
|
-
if (this.refresh) await this.maybeRefresh();
|
|
6856
|
-
const stat = fs19.statSync(this.graphPath);
|
|
6857
|
-
if (stat.mtimeMs !== this.cachedMtimeMs || !this.cached) {
|
|
6858
|
-
this.cached = parseGraph(fs19.readFileSync(this.graphPath, "utf8"));
|
|
6859
|
-
this.cachedMtimeMs = stat.mtimeMs;
|
|
6860
|
-
}
|
|
6861
|
-
return this.cached;
|
|
6862
|
-
}
|
|
6863
|
-
/**
|
|
6864
|
-
* Debounced, single-flight refresh. Never throws — a refresh problem must
|
|
6865
|
-
* degrade to "answer from the current map", not break the tool call.
|
|
6866
|
-
*/
|
|
6867
|
-
async maybeRefresh() {
|
|
6868
|
-
const now = Date.now();
|
|
6869
|
-
if (!this.inflight) {
|
|
6870
|
-
const interval = this.tuning.probeIntervalMs ?? this.probeIntervalMs;
|
|
6871
|
-
if (now < this.failedUntil || now - this.lastProbeAt < interval) return;
|
|
6872
|
-
this.lastProbeAt = now;
|
|
6873
|
-
this.inflight = refreshIfStale(this.root).then((r) => {
|
|
6874
|
-
if (r.status === "error") this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
|
|
6875
|
-
if (r.status === "fresh" || r.status === "no-snapshot" || r.status === "locked") {
|
|
6876
|
-
const cost = Date.now() - now;
|
|
6877
|
-
this.probeIntervalMs = Math.min(
|
|
6878
|
-
MAX_PROBE_INTERVAL_MS,
|
|
6879
|
-
Math.max(PROBE_INTERVAL_MS, cost * PROBE_DUTY_FACTOR)
|
|
6880
|
-
);
|
|
6881
|
-
}
|
|
6882
|
-
}).catch(() => {
|
|
6883
|
-
this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
|
|
6884
|
-
}).finally(() => {
|
|
6885
|
-
this.inflight = null;
|
|
6886
|
-
});
|
|
6887
|
-
}
|
|
6888
|
-
await Promise.race([this.inflight, sleep(this.tuning.refreshBudgetMs ?? REFRESH_BUDGET_MS)]);
|
|
6889
|
-
}
|
|
6890
|
-
};
|
|
6891
|
-
function createServer(source, opts = {}) {
|
|
6892
|
-
const { savings = false, shareStats = false, local = false, dedup = false, stats } = opts;
|
|
6893
|
-
const record = savings || shareStats;
|
|
6894
|
-
const root = path19.dirname(path19.dirname(source.graphPath));
|
|
6895
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6896
|
-
const server = new Server(
|
|
6897
|
-
{ name: "vg", version: VERSION },
|
|
6898
|
-
{
|
|
6899
|
-
capabilities: { tools: {} },
|
|
6900
|
-
// Routing guidance once at the server level (hosts that surface
|
|
6901
|
-
// `instructions` get it at zero per-step schema cost): the flashlight
|
|
6902
|
-
// vs the map.
|
|
6903
|
-
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.'
|
|
6904
|
-
}
|
|
6905
|
-
);
|
|
6906
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
6907
|
-
tools: TOOLS.map((t) => ({
|
|
6908
|
-
name: t.name,
|
|
6909
|
-
description: t.description,
|
|
6910
|
-
inputSchema: t.inputSchema,
|
|
6911
|
-
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
6912
|
-
}))
|
|
6913
|
-
}));
|
|
6914
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
6915
|
-
const tool = TOOLS.find((t) => t.name === request.params.name);
|
|
6916
|
-
if (!tool) {
|
|
6917
|
-
return errorResult(`unknown tool "${request.params.name}"`);
|
|
6918
|
-
}
|
|
6919
|
-
let graph;
|
|
6920
|
-
try {
|
|
6921
|
-
graph = await source.get();
|
|
6922
|
-
} catch {
|
|
6923
|
-
return errorResult(
|
|
6924
|
-
"no code map found. Run `vg` in the project to build .vibgrate/graph.json, then retry."
|
|
6925
|
-
);
|
|
6926
|
-
}
|
|
6927
|
-
const startedAt = Date.now();
|
|
6928
|
-
try {
|
|
6929
|
-
const args = request.params.arguments ?? {};
|
|
6930
|
-
const result = await tool.handler(graph, args, { root, local, dedup, seen });
|
|
6931
|
-
const ms = Date.now() - startedAt;
|
|
6932
|
-
stats?.record({ ...measureCall(tool.name, result), client: sanitizeClient(detectClient(server)), ms });
|
|
6933
|
-
if (record) recordUsage(root, tool.name, result, detectClient(server), ms);
|
|
6934
|
-
return renderToolResult(result);
|
|
6935
|
-
} catch (err) {
|
|
6936
|
-
stats?.record({
|
|
6937
|
-
tool: tool.name,
|
|
6938
|
-
client: sanitizeClient(detectClient(server)),
|
|
6939
|
-
outcome: "miss",
|
|
6940
|
-
ms: Date.now() - startedAt,
|
|
6941
|
-
vgTokens: 0,
|
|
6942
|
-
baselineTokens: 0
|
|
6943
|
-
});
|
|
6944
|
-
return errorResult(`tool "${tool.name}" failed: ${err.message}`);
|
|
6945
|
-
}
|
|
6946
|
-
});
|
|
6947
|
-
return server;
|
|
6948
|
-
}
|
|
6949
|
-
async function serveStdio(graphPath, opts = {}) {
|
|
6950
|
-
const source = new GraphSource(graphPath, opts.refresh !== false);
|
|
6951
|
-
const server = createServer(source, opts);
|
|
6952
|
-
warmEmbedderInBackground(opts.local);
|
|
6953
|
-
await server.connect(new StdioServerTransport());
|
|
6954
|
-
}
|
|
6955
|
-
function sleep(ms) {
|
|
6956
|
-
return new Promise((resolve12) => {
|
|
6957
|
-
const timer = setTimeout(resolve12, ms);
|
|
6958
|
-
timer.unref?.();
|
|
6959
|
-
});
|
|
6960
|
-
}
|
|
6961
|
-
function recordUsage(root, tool, result, client, ms) {
|
|
6962
|
-
recordSaving(
|
|
6963
|
-
root,
|
|
6964
|
-
{ ...measureCall(tool, result), source: "mcp", client: sanitizeClient(client), ...ms !== void 0 ? { ms } : {} },
|
|
6965
|
-
Date.now()
|
|
6966
|
-
);
|
|
6967
|
-
}
|
|
6968
|
-
function measureCall(tool, result) {
|
|
6969
|
-
const outcome = classifyOutcome(result);
|
|
6970
|
-
let vgTokens = 0;
|
|
6971
|
-
let baselineTokens = 0;
|
|
6972
|
-
if (SAVINGS_TOOLS.has(tool) && result && typeof result === "object") {
|
|
6973
|
-
vgTokens = countTokens(renderedText(renderToolResult(result)));
|
|
6974
|
-
baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
|
|
6975
|
-
}
|
|
6976
|
-
return { tool, outcome, vgTokens, baselineTokens };
|
|
6977
|
-
}
|
|
6978
|
-
function detectClient(server) {
|
|
6979
|
-
try {
|
|
6980
|
-
const info = server.getClientVersion?.();
|
|
6981
|
-
return typeof info?.name === "string" ? info.name : void 0;
|
|
6982
|
-
} catch {
|
|
6983
|
-
return void 0;
|
|
6984
|
-
}
|
|
6985
|
-
}
|
|
6986
|
-
function classifyOutcome(result) {
|
|
6987
|
-
if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
|
|
6988
|
-
if (!result || typeof result !== "object") return "miss";
|
|
6989
|
-
const r = result;
|
|
6990
|
-
if (typeof r.error === "string" && r.error) return "miss";
|
|
6991
|
-
if (r.connected === false) return "miss";
|
|
6992
|
-
if (Array.isArray(r.matches) && r.matches.length === 0) return "miss";
|
|
6993
|
-
return isPartial(r) ? "partial" : "complete";
|
|
6994
|
-
}
|
|
6995
|
-
function isPartial(r) {
|
|
6996
|
-
if (r.moreAvailable === true) return true;
|
|
6997
|
-
if (r._truncated && typeof r._truncated === "object") return true;
|
|
6998
|
-
for (const [key, value] of Object.entries(r)) {
|
|
6999
|
-
if (typeof value !== "number" || !key.endsWith("Total")) continue;
|
|
7000
|
-
const base = key.slice(0, -"Total".length);
|
|
7001
|
-
const shown = Array.isArray(r[base]) ? r[base].length : 0;
|
|
7002
|
-
if (value > shown) return true;
|
|
7003
|
-
}
|
|
7004
|
-
return false;
|
|
7005
|
-
}
|
|
7006
|
-
function renderedText(rendered) {
|
|
7007
|
-
const block = rendered.content?.find((b) => b.type === "text");
|
|
7008
|
-
return block && "text" in block && typeof block.text === "string" ? block.text : "";
|
|
7009
|
-
}
|
|
7010
|
-
function referencedFiles(result) {
|
|
7011
|
-
const r = result;
|
|
7012
|
-
const files = /* @__PURE__ */ new Set();
|
|
7013
|
-
if (typeof r.file === "string" && r.file) files.add(r.file);
|
|
7014
|
-
for (const m of asArray(r.matches)) {
|
|
7015
|
-
const f = m.file;
|
|
7016
|
-
if (typeof f === "string" && f) files.add(f);
|
|
7017
|
-
}
|
|
7018
|
-
for (const name of [...asArray(r.calls), ...asArray(r.calledBy)]) {
|
|
7019
|
-
if (typeof name !== "string") continue;
|
|
7020
|
-
const i = name.indexOf(":");
|
|
7021
|
-
if (i > 0) files.add(name.slice(0, i));
|
|
7022
|
-
}
|
|
7023
|
-
return files;
|
|
7024
|
-
}
|
|
7025
|
-
function asArray(v) {
|
|
7026
|
-
return Array.isArray(v) ? v : [];
|
|
7027
|
-
}
|
|
7028
|
-
function errorResult(message) {
|
|
7029
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
7030
|
-
}
|
|
7031
6633
|
|
|
7032
6634
|
// src/install/content.ts
|
|
7033
6635
|
var NUDGE_BEGIN = "<!-- vg:begin -->";
|
|
7034
6636
|
var NUDGE_END = "<!-- vg:end -->";
|
|
6637
|
+
var INSTALL_CONTENT_VERSION = 2;
|
|
6638
|
+
var VERSION_MARKER_RE = /<!--\s*vg:v(\d+)\b[^>]*-->/;
|
|
6639
|
+
function versionMarker() {
|
|
6640
|
+
return `<!-- vg:v${INSTALL_CONTENT_VERSION} \xB7 managed by \`vg install\` \u2014 auto-refreshed when these instructions evolve; remove this line to opt out -->`;
|
|
6641
|
+
}
|
|
6642
|
+
function installedContentVersion(text) {
|
|
6643
|
+
const m = VERSION_MARKER_RE.exec(text);
|
|
6644
|
+
return m ? Number(m[1]) : null;
|
|
6645
|
+
}
|
|
7035
6646
|
function clientFlag(client) {
|
|
7036
6647
|
return client ? `--client=${client}` : "--client=<your-ai>";
|
|
7037
6648
|
}
|
|
@@ -7042,26 +6653,31 @@ name: vg
|
|
|
7042
6653
|
description: Query the local code graph (vg) for structure, impact, and navigation instead of grepping/reading many files.
|
|
7043
6654
|
---
|
|
7044
6655
|
|
|
6656
|
+
${versionMarker()}
|
|
6657
|
+
|
|
7045
6658
|
# vg \u2014 the code map
|
|
7046
6659
|
|
|
7047
6660
|
This repo has a deterministic code graph built by \`vg\`. Prefer it over reading or
|
|
7048
6661
|
grepping many files \u2014 it is smaller, more relevant context, and free.
|
|
7049
6662
|
|
|
7050
|
-
##
|
|
6663
|
+
## Use the MCP tools \u2014 not the CLI
|
|
7051
6664
|
|
|
7052
|
-
|
|
7053
|
-
read-only tools
|
|
7054
|
-
map parsed, the relation index warm, and the embedding model loaded across
|
|
7055
|
-
so
|
|
6665
|
+
When the \`vg\` MCP server is registered (it is after \`vg install\`), **always call
|
|
6666
|
+
its read-only tools instead of shelling out to the CLI.** The server keeps the
|
|
6667
|
+
map parsed, the relation index warm, and the embedding model loaded across
|
|
6668
|
+
calls, so an MCP call answers in milliseconds \u2014 while every CLI invocation pays
|
|
6669
|
+
Node startup plus a fresh map parse, hundreds of times more. Tools:
|
|
7056
6670
|
\`query_graph\`, \`get_node\`, \`impact_of\`, \`find_path\`, \`list_hubs\`, \`list_areas\`,
|
|
7057
6671
|
\`get_graph_summary\`, \`search_symbols\`. They are side-effect-free and
|
|
7058
6672
|
auto-approvable, and the server records which client is calling automatically.
|
|
6673
|
+
Reach for the CLI only when the MCP server is genuinely unavailable \u2014 never as
|
|
6674
|
+
the first resort.
|
|
7059
6675
|
|
|
7060
|
-
##
|
|
6676
|
+
## CLI fallback \u2014 only when the MCP server is unavailable
|
|
7061
6677
|
|
|
7062
|
-
|
|
7063
|
-
so your calls are counted (that's how the CLI-vs-MCP split is
|
|
7064
|
-
tools improved):
|
|
6678
|
+
If (and only if) no \`vg\` MCP server is available, use the CLI \u2014 and **always
|
|
6679
|
+
pass \`${cf}\`** so your calls are counted (that's how the CLI-vs-MCP split is
|
|
6680
|
+
measured and the tools improved):
|
|
7065
6681
|
|
|
7066
6682
|
- **Understand code:** \`vg "<question>" ${cf}\` \u2014 a budget-bounded, fact-annotated context block.
|
|
7067
6683
|
- **Find a symbol:** \`vg show <name> ${cf}\` \u2014 what it is, what it calls, what calls it.
|
|
@@ -7104,22 +6720,28 @@ function nudgeMarkdown(smallRepo, client) {
|
|
|
7104
6720
|
const cf = clientFlag(client);
|
|
7105
6721
|
if (smallRepo) {
|
|
7106
6722
|
return `${NUDGE_BEGIN}
|
|
6723
|
+
${versionMarker()}
|
|
7107
6724
|
## Code navigation (vg)
|
|
7108
6725
|
|
|
7109
6726
|
This repo is small enough that searching files directly is fine. Still, before a
|
|
7110
|
-
change, \`vg
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
counted
|
|
6727
|
+
change, the \`vg\` graph shows what breaks: use the \`vg\` MCP tools when the server
|
|
6728
|
+
is registered (\`impact_of\`, \`query_graph\` \u2014 the warm server answers far faster
|
|
6729
|
+
than spawning the CLI). Only without the server, fall back to the CLI and pass
|
|
6730
|
+
\`${cf}\` so the call is counted: \`vg impact <name> ${cf}\`,
|
|
6731
|
+
\`vg "<question>" ${cf}\`.
|
|
7114
6732
|
${NUDGE_END}`;
|
|
7115
6733
|
}
|
|
7116
6734
|
return `${NUDGE_BEGIN}
|
|
7117
|
-
|
|
6735
|
+
${versionMarker()}
|
|
6736
|
+
## Code navigation (vg) \u2014 use the MCP tools first
|
|
7118
6737
|
|
|
7119
6738
|
A deterministic code graph is available. It is usually the faster, cheaper path
|
|
7120
|
-
than reading or grepping many files.
|
|
7121
|
-
|
|
7122
|
-
|
|
6739
|
+
than reading or grepping many files. **When the \`vg\` MCP server is registered,
|
|
6740
|
+
always use its tools** (\`query_graph\`, \`get_node\`, \`impact_of\`,
|
|
6741
|
+
\`search_symbols\`, \u2026) \u2014 the map, indexes, and embedding model stay warm across
|
|
6742
|
+
calls, so they answer in milliseconds, while every CLI invocation pays Node
|
|
6743
|
+
startup plus a fresh map parse. Fall back to the CLI only when the server is
|
|
6744
|
+
unavailable, and then pass \`${cf}\` so the call is counted:
|
|
7123
6745
|
|
|
7124
6746
|
- \`vg "<question>" ${cf}\` \u2014 a compact, fact-annotated context block for a question.
|
|
7125
6747
|
- \`vg show <name> ${cf}\` \u2014 what a symbol is, its callers and callees.
|
|
@@ -7215,12 +6837,12 @@ function detectAssistants(root, opts = {}) {
|
|
|
7215
6837
|
const which = opts.which ?? whichOnPath;
|
|
7216
6838
|
const found = [];
|
|
7217
6839
|
for (const assistant of ASSISTANTS) {
|
|
7218
|
-
const repoHit = (assistant.markers ?? []).find((m) =>
|
|
6840
|
+
const repoHit = (assistant.markers ?? []).find((m) => fs17.existsSync(path17.join(root, m)));
|
|
7219
6841
|
if (repoHit) {
|
|
7220
6842
|
found.push({ assistant, via: "repo", marker: repoHit });
|
|
7221
6843
|
continue;
|
|
7222
6844
|
}
|
|
7223
|
-
const homeHit = (assistant.homeMarkers ?? []).find((m) =>
|
|
6845
|
+
const homeHit = (assistant.homeMarkers ?? []).find((m) => fs17.existsSync(path17.join(home, m)));
|
|
7224
6846
|
if (homeHit) {
|
|
7225
6847
|
found.push({ assistant, via: "home", marker: homeHit });
|
|
7226
6848
|
continue;
|
|
@@ -7252,19 +6874,19 @@ function installAssistant(a, opts) {
|
|
|
7252
6874
|
const skipped = [];
|
|
7253
6875
|
let note;
|
|
7254
6876
|
if (a.skill) {
|
|
7255
|
-
writeFileEnsured(
|
|
6877
|
+
writeFileEnsured(path17.join(opts.root, a.skill), skillMarkdown(a.id));
|
|
7256
6878
|
wrote.push(a.skill);
|
|
7257
6879
|
}
|
|
7258
6880
|
if (a.mcp) {
|
|
7259
6881
|
const launch = opts.launch ?? detectServeLaunch();
|
|
7260
|
-
upsertMcp(
|
|
6882
|
+
upsertMcp(path17.join(opts.root, a.mcp.file), a.mcp, launch);
|
|
7261
6883
|
wrote.push(a.mcp.file);
|
|
7262
6884
|
note = launch.note;
|
|
7263
6885
|
} else {
|
|
7264
6886
|
skipped.push("mcp (host-specific setup)");
|
|
7265
6887
|
}
|
|
7266
6888
|
if (a.nudge && opts.hook !== false) {
|
|
7267
|
-
writeNudge(
|
|
6889
|
+
writeNudge(path17.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
|
|
7268
6890
|
wrote.push(a.nudge.file);
|
|
7269
6891
|
} else if (a.nudge) {
|
|
7270
6892
|
skipped.push("nudge (--no-hook)");
|
|
@@ -7274,40 +6896,40 @@ function installAssistant(a, opts) {
|
|
|
7274
6896
|
function uninstallAssistant(a, root, purge) {
|
|
7275
6897
|
const removed = [];
|
|
7276
6898
|
if (a.mcp) {
|
|
7277
|
-
const file =
|
|
6899
|
+
const file = path17.join(root, a.mcp.file);
|
|
7278
6900
|
if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
|
|
7279
6901
|
}
|
|
7280
6902
|
if (a.nudge) {
|
|
7281
|
-
const file =
|
|
6903
|
+
const file = path17.join(root, a.nudge.file);
|
|
7282
6904
|
if (a.nudge.kind === "block") {
|
|
7283
6905
|
if (removeBlock(file)) removed.push(a.nudge.file);
|
|
7284
|
-
} else if (
|
|
7285
|
-
|
|
6906
|
+
} else if (fs17.existsSync(file)) {
|
|
6907
|
+
fs17.rmSync(file);
|
|
7286
6908
|
removed.push(a.nudge.file);
|
|
7287
6909
|
}
|
|
7288
6910
|
}
|
|
7289
6911
|
if (purge && a.skill) {
|
|
7290
|
-
const file =
|
|
7291
|
-
if (
|
|
7292
|
-
|
|
6912
|
+
const file = path17.join(root, a.skill);
|
|
6913
|
+
if (fs17.existsSync(file)) {
|
|
6914
|
+
fs17.rmSync(file);
|
|
7293
6915
|
removed.push(a.skill);
|
|
7294
6916
|
}
|
|
7295
6917
|
}
|
|
7296
6918
|
return removed;
|
|
7297
6919
|
}
|
|
7298
6920
|
function writeFileEnsured(file, content) {
|
|
7299
|
-
|
|
7300
|
-
|
|
6921
|
+
fs17.mkdirSync(path17.dirname(file), { recursive: true });
|
|
6922
|
+
fs17.writeFileSync(file, content);
|
|
7301
6923
|
}
|
|
7302
6924
|
function writeNavigationConfig(root) {
|
|
7303
|
-
const rel2 =
|
|
6925
|
+
const rel2 = path17.join(".vibgrate", "mcp-navigation.json");
|
|
7304
6926
|
const doc = {
|
|
7305
6927
|
_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.",
|
|
7306
6928
|
hot_core: [...HOT_TOOLS],
|
|
7307
6929
|
deferred: deferredToolNames(),
|
|
7308
6930
|
toolset: navigationToolsetConfig()
|
|
7309
6931
|
};
|
|
7310
|
-
writeFileEnsured(
|
|
6932
|
+
writeFileEnsured(path17.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
7311
6933
|
`);
|
|
7312
6934
|
return rel2;
|
|
7313
6935
|
}
|
|
@@ -7322,7 +6944,7 @@ function upsertMcp(file, target, launch) {
|
|
|
7322
6944
|
`);
|
|
7323
6945
|
}
|
|
7324
6946
|
function removeMcp(file, target) {
|
|
7325
|
-
if (!
|
|
6947
|
+
if (!fs17.existsSync(file)) return false;
|
|
7326
6948
|
const config = readJson(file);
|
|
7327
6949
|
const bag = config[target.key];
|
|
7328
6950
|
if (!bag || !(bag.vg !== void 0)) return false;
|
|
@@ -7331,6 +6953,57 @@ function removeMcp(file, target) {
|
|
|
7331
6953
|
`);
|
|
7332
6954
|
return true;
|
|
7333
6955
|
}
|
|
6956
|
+
var SMALL_REPO_FILES = 150;
|
|
6957
|
+
function refreshInstalledInstructions(root, smallRepo) {
|
|
6958
|
+
const out = [];
|
|
6959
|
+
const staleVersion = (text, legacySignature) => {
|
|
6960
|
+
const v = installedContentVersion(text);
|
|
6961
|
+
if (v !== null) return v < INSTALL_CONTENT_VERSION ? v : null;
|
|
6962
|
+
return text.includes(legacySignature) ? 0 : null;
|
|
6963
|
+
};
|
|
6964
|
+
for (const a of ASSISTANTS) {
|
|
6965
|
+
if (a.skill) {
|
|
6966
|
+
const file = path17.join(root, a.skill);
|
|
6967
|
+
if (fs17.existsSync(file)) {
|
|
6968
|
+
const text = readTextSafe(file);
|
|
6969
|
+
const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
|
|
6970
|
+
if (from !== null) {
|
|
6971
|
+
writeFileEnsured(file, skillMarkdown(a.id));
|
|
6972
|
+
out.push({ file: a.skill, from, to: INSTALL_CONTENT_VERSION });
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
if (a.nudge) {
|
|
6977
|
+
const file = path17.join(root, a.nudge.file);
|
|
6978
|
+
if (!fs17.existsSync(file)) continue;
|
|
6979
|
+
const text = readTextSafe(file);
|
|
6980
|
+
if (text === null) continue;
|
|
6981
|
+
if (a.nudge.kind === "block") {
|
|
6982
|
+
const block = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`).exec(text);
|
|
6983
|
+
if (!block) continue;
|
|
6984
|
+
const from = staleVersion(block[0], "## Code navigation (vg)");
|
|
6985
|
+
if (from !== null) {
|
|
6986
|
+
writeNudge(file, a.nudge, smallRepo, a.id);
|
|
6987
|
+
out.push({ file: a.nudge.file, from, to: INSTALL_CONTENT_VERSION });
|
|
6988
|
+
}
|
|
6989
|
+
} else {
|
|
6990
|
+
const from = staleVersion(text, "## Code navigation (vg)");
|
|
6991
|
+
if (from !== null) {
|
|
6992
|
+
writeNudge(file, a.nudge, smallRepo, a.id);
|
|
6993
|
+
out.push({ file: a.nudge.file, from, to: INSTALL_CONTENT_VERSION });
|
|
6994
|
+
}
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
return out;
|
|
6999
|
+
}
|
|
7000
|
+
function readTextSafe(file) {
|
|
7001
|
+
try {
|
|
7002
|
+
return fs17.readFileSync(file, "utf8");
|
|
7003
|
+
} catch {
|
|
7004
|
+
return null;
|
|
7005
|
+
}
|
|
7006
|
+
}
|
|
7334
7007
|
function writeNudge(file, target, smallRepo, client) {
|
|
7335
7008
|
if (target.kind === "file") {
|
|
7336
7009
|
const body = file.endsWith(".mdc") ? `---
|
|
@@ -7345,8 +7018,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
|
|
|
7345
7018
|
upsertBlock(file, nudgeMarkdown(smallRepo, client));
|
|
7346
7019
|
}
|
|
7347
7020
|
function upsertBlock(file, block) {
|
|
7348
|
-
|
|
7349
|
-
let existing =
|
|
7021
|
+
fs17.mkdirSync(path17.dirname(file), { recursive: true });
|
|
7022
|
+
let existing = fs17.existsSync(file) ? fs17.readFileSync(file, "utf8") : "";
|
|
7350
7023
|
const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
|
|
7351
7024
|
if (re.test(existing)) existing = existing.replace(re, block);
|
|
7352
7025
|
else existing = existing.length ? `${existing.replace(/\s*$/, "")}
|
|
@@ -7354,20 +7027,20 @@ function upsertBlock(file, block) {
|
|
|
7354
7027
|
${block}
|
|
7355
7028
|
` : `${block}
|
|
7356
7029
|
`;
|
|
7357
|
-
|
|
7030
|
+
fs17.writeFileSync(file, existing);
|
|
7358
7031
|
}
|
|
7359
7032
|
function removeBlock(file) {
|
|
7360
|
-
if (!
|
|
7361
|
-
const existing =
|
|
7033
|
+
if (!fs17.existsSync(file)) return false;
|
|
7034
|
+
const existing = fs17.readFileSync(file, "utf8");
|
|
7362
7035
|
const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
|
|
7363
7036
|
if (!re.test(existing)) return false;
|
|
7364
|
-
|
|
7037
|
+
fs17.writeFileSync(file, existing.replace(re, "\n"));
|
|
7365
7038
|
return true;
|
|
7366
7039
|
}
|
|
7367
7040
|
function readJson(file) {
|
|
7368
|
-
if (!
|
|
7041
|
+
if (!fs17.existsSync(file)) return {};
|
|
7369
7042
|
try {
|
|
7370
|
-
const parsed = JSON.parse(
|
|
7043
|
+
const parsed = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
7371
7044
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7372
7045
|
return parsed;
|
|
7373
7046
|
}
|
|
@@ -7385,6 +7058,425 @@ function stripMarkers(s) {
|
|
|
7385
7058
|
function escapeRe(s) {
|
|
7386
7059
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7387
7060
|
}
|
|
7061
|
+
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
7062
|
+
function refreshLockPath(root) {
|
|
7063
|
+
return path17.join(cacheDir(root), "refresh.lock");
|
|
7064
|
+
}
|
|
7065
|
+
async function refreshIfStale(root, opts = {}) {
|
|
7066
|
+
const probe = probeFreshness(root);
|
|
7067
|
+
if (!probe) return { status: "no-snapshot" };
|
|
7068
|
+
if (!hasDrift(probe.drift)) return { status: "fresh" };
|
|
7069
|
+
const lock = refreshLockPath(root);
|
|
7070
|
+
if (!acquireLock(lock, REFRESH_LOCK_STALE_MS)) return { status: "locked" };
|
|
7071
|
+
const start = process.hrtime.bigint();
|
|
7072
|
+
try {
|
|
7073
|
+
const { scope } = probe;
|
|
7074
|
+
const result = await buildGraph({
|
|
7075
|
+
root,
|
|
7076
|
+
only: scope.only,
|
|
7077
|
+
exclude: scope.exclude,
|
|
7078
|
+
paths: scope.paths,
|
|
7079
|
+
deep: scope.deep,
|
|
7080
|
+
noGround: scope.noGround,
|
|
7081
|
+
scip: scope.scip,
|
|
7082
|
+
noScip: scope.noScip,
|
|
7083
|
+
noTsc: scope.noTsc,
|
|
7084
|
+
cluster: scope.cluster,
|
|
7085
|
+
grammarsDir: scope.grammarsDir,
|
|
7086
|
+
inline: opts.inline,
|
|
7087
|
+
jobs: opts.jobs
|
|
7088
|
+
});
|
|
7089
|
+
const wrote = result.graph.provenance.corpusHash !== probe.corpusHash;
|
|
7090
|
+
if (wrote) {
|
|
7091
|
+
const dir = vibgrateDir(root);
|
|
7092
|
+
writeArtifacts(result.graph, {
|
|
7093
|
+
root,
|
|
7094
|
+
report: fs17.existsSync(path17.join(dir, "GRAPH_REPORT.md")),
|
|
7095
|
+
html: fs17.existsSync(path17.join(dir, "graph.html"))
|
|
7096
|
+
});
|
|
7097
|
+
}
|
|
7098
|
+
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
7099
|
+
const ms = Number((process.hrtime.bigint() - start) / 1000000n);
|
|
7100
|
+
return {
|
|
7101
|
+
status: "refreshed",
|
|
7102
|
+
drift: probe.drift,
|
|
7103
|
+
ms,
|
|
7104
|
+
reparsed: result.reparsed,
|
|
7105
|
+
totalFiles: result.totalFiles,
|
|
7106
|
+
wrote
|
|
7107
|
+
};
|
|
7108
|
+
} catch (err) {
|
|
7109
|
+
return { status: "error", message: err.message };
|
|
7110
|
+
} finally {
|
|
7111
|
+
releaseLock(lock);
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7114
|
+
var LEDGER = "savings.jsonl";
|
|
7115
|
+
var PER_FILE_TOKENS = 400;
|
|
7116
|
+
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node"]);
|
|
7117
|
+
var CLI_TOOL_ALIASES = {
|
|
7118
|
+
ask: "query_graph",
|
|
7119
|
+
show: "get_node",
|
|
7120
|
+
impact: "impact_of",
|
|
7121
|
+
path: "find_path",
|
|
7122
|
+
hubs: "list_hubs",
|
|
7123
|
+
areas: "list_areas",
|
|
7124
|
+
tree: "tree"
|
|
7125
|
+
};
|
|
7126
|
+
function sanitizeClient(name) {
|
|
7127
|
+
if (typeof name !== "string") return "unknown";
|
|
7128
|
+
const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
7129
|
+
return cleaned || "unknown";
|
|
7130
|
+
}
|
|
7131
|
+
function recordCliCall(root, entry, now) {
|
|
7132
|
+
recordSaving(
|
|
7133
|
+
root,
|
|
7134
|
+
{
|
|
7135
|
+
tool: entry.tool,
|
|
7136
|
+
source: "cli",
|
|
7137
|
+
client: sanitizeClient(entry.client),
|
|
7138
|
+
outcome: entry.outcome,
|
|
7139
|
+
vgTokens: entry.vgTokens ?? 0,
|
|
7140
|
+
baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
|
|
7141
|
+
},
|
|
7142
|
+
now
|
|
7143
|
+
);
|
|
7144
|
+
}
|
|
7145
|
+
function savingsLedgerPath(root) {
|
|
7146
|
+
return path17.join(cacheDir(root), LEDGER);
|
|
7147
|
+
}
|
|
7148
|
+
function ledgerPath(root) {
|
|
7149
|
+
return savingsLedgerPath(root);
|
|
7150
|
+
}
|
|
7151
|
+
function savingsRecorded(root) {
|
|
7152
|
+
return fs17.existsSync(ledgerPath(root));
|
|
7153
|
+
}
|
|
7154
|
+
function recordSaving(root, entry, now) {
|
|
7155
|
+
try {
|
|
7156
|
+
fs17.mkdirSync(cacheDir(root), { recursive: true });
|
|
7157
|
+
const line = JSON.stringify({ ts: now, ...entry });
|
|
7158
|
+
fs17.appendFileSync(ledgerPath(root), line + "\n");
|
|
7159
|
+
} catch {
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
var DEFAULT_RATE_PER_M = 3;
|
|
7163
|
+
var DEFAULT_RATE_LABEL = "input @ $3/1M";
|
|
7164
|
+
function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
7165
|
+
const file = ledgerPath(root);
|
|
7166
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
7167
|
+
let queries = 0;
|
|
7168
|
+
let vgTokens = 0;
|
|
7169
|
+
let baselineTokens = 0;
|
|
7170
|
+
if (fs17.existsSync(file)) {
|
|
7171
|
+
for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
|
|
7172
|
+
if (!line.trim()) continue;
|
|
7173
|
+
try {
|
|
7174
|
+
const e = JSON.parse(line);
|
|
7175
|
+
if (e.ts < cutoff) continue;
|
|
7176
|
+
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
7177
|
+
queries++;
|
|
7178
|
+
vgTokens += e.vgTokens;
|
|
7179
|
+
baselineTokens += e.baselineTokens;
|
|
7180
|
+
} catch {
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
}
|
|
7184
|
+
const estCostVg = vgTokens / 1e6 * ratePerM;
|
|
7185
|
+
const estCostBaseline = baselineTokens / 1e6 * ratePerM;
|
|
7186
|
+
return {
|
|
7187
|
+
enabled: savingsRecorded(root),
|
|
7188
|
+
days,
|
|
7189
|
+
queries,
|
|
7190
|
+
vgTokens,
|
|
7191
|
+
baselineTokens,
|
|
7192
|
+
ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
|
|
7193
|
+
estCostVg: round22(estCostVg),
|
|
7194
|
+
estCostBaseline: round22(estCostBaseline),
|
|
7195
|
+
saved: round22(estCostBaseline - estCostVg),
|
|
7196
|
+
rateLabel: DEFAULT_RATE_LABEL
|
|
7197
|
+
};
|
|
7198
|
+
}
|
|
7199
|
+
function round22(x) {
|
|
7200
|
+
return Math.round(x * 100) / 100;
|
|
7201
|
+
}
|
|
7202
|
+
function toDimensionStats(byKey) {
|
|
7203
|
+
return [...byKey.entries()].map(([key, r]) => {
|
|
7204
|
+
const calls = r.complete + r.partial + r.miss;
|
|
7205
|
+
return {
|
|
7206
|
+
key,
|
|
7207
|
+
calls,
|
|
7208
|
+
complete: r.complete,
|
|
7209
|
+
partial: r.partial,
|
|
7210
|
+
miss: r.miss,
|
|
7211
|
+
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
7212
|
+
};
|
|
7213
|
+
}).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
|
|
7214
|
+
}
|
|
7215
|
+
function readUsage(root, days, now) {
|
|
7216
|
+
const file = ledgerPath(root);
|
|
7217
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
7218
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
7219
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
7220
|
+
const byClient = /* @__PURE__ */ new Map();
|
|
7221
|
+
const msByTool = /* @__PURE__ */ new Map();
|
|
7222
|
+
const bump2 = (m, key, outcome) => {
|
|
7223
|
+
const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
|
|
7224
|
+
row[outcome]++;
|
|
7225
|
+
m.set(key, row);
|
|
7226
|
+
};
|
|
7227
|
+
if (fs17.existsSync(file)) {
|
|
7228
|
+
for (const line of fs17.readFileSync(file, "utf8").split("\n")) {
|
|
7229
|
+
if (!line.trim()) continue;
|
|
7230
|
+
try {
|
|
7231
|
+
const e = JSON.parse(line);
|
|
7232
|
+
if (e.ts < cutoff) continue;
|
|
7233
|
+
const outcome = e.outcome ?? "complete";
|
|
7234
|
+
bump2(byTool, e.tool, outcome);
|
|
7235
|
+
bump2(bySource, e.source ?? "mcp", outcome);
|
|
7236
|
+
bump2(byClient, e.client ?? "unknown", outcome);
|
|
7237
|
+
if (typeof e.ms === "number" && e.ms >= 0) {
|
|
7238
|
+
const timing = msByTool.get(e.tool) ?? { sum: 0, n: 0 };
|
|
7239
|
+
timing.sum += e.ms;
|
|
7240
|
+
timing.n++;
|
|
7241
|
+
msByTool.set(e.tool, timing);
|
|
7242
|
+
}
|
|
7243
|
+
} catch {
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
const commands = toDimensionStats(byTool).map((d) => {
|
|
7248
|
+
const timing = msByTool.get(d.key);
|
|
7249
|
+
return {
|
|
7250
|
+
tool: d.key,
|
|
7251
|
+
calls: d.calls,
|
|
7252
|
+
complete: d.complete,
|
|
7253
|
+
partial: d.partial,
|
|
7254
|
+
miss: d.miss,
|
|
7255
|
+
successPct: d.successPct,
|
|
7256
|
+
avgMs: timing && timing.n > 0 ? Math.round(timing.sum / timing.n) : null
|
|
7257
|
+
};
|
|
7258
|
+
});
|
|
7259
|
+
const totals = commands.reduce(
|
|
7260
|
+
(t, c) => ({
|
|
7261
|
+
calls: t.calls + c.calls,
|
|
7262
|
+
complete: t.complete + c.complete,
|
|
7263
|
+
partial: t.partial + c.partial,
|
|
7264
|
+
miss: t.miss + c.miss
|
|
7265
|
+
}),
|
|
7266
|
+
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
7267
|
+
);
|
|
7268
|
+
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
7269
|
+
return {
|
|
7270
|
+
enabled: savingsRecorded(root),
|
|
7271
|
+
days,
|
|
7272
|
+
commands,
|
|
7273
|
+
sources: toDimensionStats(bySource),
|
|
7274
|
+
clients: toDimensionStats(byClient),
|
|
7275
|
+
totals,
|
|
7276
|
+
avgSuccessPct
|
|
7277
|
+
};
|
|
7278
|
+
}
|
|
7279
|
+
var PROBE_INTERVAL_MS = 2e3;
|
|
7280
|
+
var MAX_PROBE_INTERVAL_MS = 3e4;
|
|
7281
|
+
var PROBE_DUTY_FACTOR = 20;
|
|
7282
|
+
var REFRESH_BUDGET_MS = 5e3;
|
|
7283
|
+
var FAILURE_COOLDOWN_MS = 6e4;
|
|
7284
|
+
var GraphSource = class {
|
|
7285
|
+
constructor(graphPath, refresh = false, tuning = {}) {
|
|
7286
|
+
this.graphPath = graphPath;
|
|
7287
|
+
this.refresh = refresh;
|
|
7288
|
+
this.tuning = tuning;
|
|
7289
|
+
this.root = path17.dirname(path17.dirname(graphPath));
|
|
7290
|
+
}
|
|
7291
|
+
graphPath;
|
|
7292
|
+
refresh;
|
|
7293
|
+
tuning;
|
|
7294
|
+
cachedMtimeMs = -1;
|
|
7295
|
+
cached = null;
|
|
7296
|
+
root;
|
|
7297
|
+
lastProbeAt = 0;
|
|
7298
|
+
failedUntil = 0;
|
|
7299
|
+
inflight = null;
|
|
7300
|
+
/** Self-tuned: grows with measured probe cost so huge repos aren't penalized. */
|
|
7301
|
+
probeIntervalMs = PROBE_INTERVAL_MS;
|
|
7302
|
+
/** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */
|
|
7303
|
+
async get() {
|
|
7304
|
+
if (this.refresh) await this.maybeRefresh();
|
|
7305
|
+
const stat = fs17.statSync(this.graphPath);
|
|
7306
|
+
if (stat.mtimeMs !== this.cachedMtimeMs || !this.cached) {
|
|
7307
|
+
this.cached = parseGraph(fs17.readFileSync(this.graphPath, "utf8"));
|
|
7308
|
+
this.cachedMtimeMs = stat.mtimeMs;
|
|
7309
|
+
}
|
|
7310
|
+
return this.cached;
|
|
7311
|
+
}
|
|
7312
|
+
/**
|
|
7313
|
+
* Debounced, single-flight refresh. Never throws — a refresh problem must
|
|
7314
|
+
* degrade to "answer from the current map", not break the tool call.
|
|
7315
|
+
*/
|
|
7316
|
+
async maybeRefresh() {
|
|
7317
|
+
const now = Date.now();
|
|
7318
|
+
if (!this.inflight) {
|
|
7319
|
+
const interval = this.tuning.probeIntervalMs ?? this.probeIntervalMs;
|
|
7320
|
+
if (now < this.failedUntil || now - this.lastProbeAt < interval) return;
|
|
7321
|
+
this.lastProbeAt = now;
|
|
7322
|
+
this.inflight = refreshIfStale(this.root).then((r) => {
|
|
7323
|
+
if (r.status === "error") this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
|
|
7324
|
+
if (r.status === "fresh" || r.status === "no-snapshot" || r.status === "locked") {
|
|
7325
|
+
const cost = Date.now() - now;
|
|
7326
|
+
this.probeIntervalMs = Math.min(
|
|
7327
|
+
MAX_PROBE_INTERVAL_MS,
|
|
7328
|
+
Math.max(PROBE_INTERVAL_MS, cost * PROBE_DUTY_FACTOR)
|
|
7329
|
+
);
|
|
7330
|
+
}
|
|
7331
|
+
}).catch(() => {
|
|
7332
|
+
this.failedUntil = Date.now() + FAILURE_COOLDOWN_MS;
|
|
7333
|
+
}).finally(() => {
|
|
7334
|
+
this.inflight = null;
|
|
7335
|
+
});
|
|
7336
|
+
}
|
|
7337
|
+
await Promise.race([this.inflight, sleep(this.tuning.refreshBudgetMs ?? REFRESH_BUDGET_MS)]);
|
|
7338
|
+
}
|
|
7339
|
+
};
|
|
7340
|
+
function createServer(source, opts = {}) {
|
|
7341
|
+
const { savings = false, shareStats = false, local = false, dedup = false, stats } = opts;
|
|
7342
|
+
const record = savings || shareStats;
|
|
7343
|
+
const root = path17.dirname(path17.dirname(source.graphPath));
|
|
7344
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7345
|
+
const server = new Server(
|
|
7346
|
+
{ name: "vg", version: VERSION },
|
|
7347
|
+
{
|
|
7348
|
+
capabilities: { tools: {} },
|
|
7349
|
+
// Routing guidance once at the server level (hosts that surface
|
|
7350
|
+
// `instructions` get it at zero per-step schema cost): the flashlight
|
|
7351
|
+
// vs the map.
|
|
7352
|
+
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.'
|
|
7353
|
+
}
|
|
7354
|
+
);
|
|
7355
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
7356
|
+
tools: TOOLS.map((t) => ({
|
|
7357
|
+
name: t.name,
|
|
7358
|
+
description: t.description,
|
|
7359
|
+
inputSchema: t.inputSchema,
|
|
7360
|
+
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
7361
|
+
}))
|
|
7362
|
+
}));
|
|
7363
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
7364
|
+
const tool = TOOLS.find((t) => t.name === request.params.name);
|
|
7365
|
+
if (!tool) {
|
|
7366
|
+
return errorResult(`unknown tool "${request.params.name}"`);
|
|
7367
|
+
}
|
|
7368
|
+
let graph;
|
|
7369
|
+
try {
|
|
7370
|
+
graph = await source.get();
|
|
7371
|
+
} catch {
|
|
7372
|
+
return errorResult(
|
|
7373
|
+
"no code map found. Run `vg` in the project to build .vibgrate/graph.json, then retry."
|
|
7374
|
+
);
|
|
7375
|
+
}
|
|
7376
|
+
const startedAt = Date.now();
|
|
7377
|
+
try {
|
|
7378
|
+
const args = request.params.arguments ?? {};
|
|
7379
|
+
const result = await tool.handler(graph, args, { root, local, dedup, seen });
|
|
7380
|
+
const ms = Date.now() - startedAt;
|
|
7381
|
+
stats?.record({ ...measureCall(tool.name, result), client: sanitizeClient(detectClient(server)), ms });
|
|
7382
|
+
if (record) recordUsage(root, tool.name, result, detectClient(server), ms);
|
|
7383
|
+
return renderToolResult(result);
|
|
7384
|
+
} catch (err) {
|
|
7385
|
+
stats?.record({
|
|
7386
|
+
tool: tool.name,
|
|
7387
|
+
client: sanitizeClient(detectClient(server)),
|
|
7388
|
+
outcome: "miss",
|
|
7389
|
+
ms: Date.now() - startedAt,
|
|
7390
|
+
vgTokens: 0,
|
|
7391
|
+
baselineTokens: 0
|
|
7392
|
+
});
|
|
7393
|
+
return errorResult(`tool "${tool.name}" failed: ${err.message}`);
|
|
7394
|
+
}
|
|
7395
|
+
});
|
|
7396
|
+
return server;
|
|
7397
|
+
}
|
|
7398
|
+
async function serveStdio(graphPath, opts = {}) {
|
|
7399
|
+
const source = new GraphSource(graphPath, opts.refresh !== false);
|
|
7400
|
+
const server = createServer(source, opts);
|
|
7401
|
+
warmEmbedderInBackground(opts.local);
|
|
7402
|
+
await server.connect(new StdioServerTransport());
|
|
7403
|
+
}
|
|
7404
|
+
function sleep(ms) {
|
|
7405
|
+
return new Promise((resolve12) => {
|
|
7406
|
+
const timer = setTimeout(resolve12, ms);
|
|
7407
|
+
timer.unref?.();
|
|
7408
|
+
});
|
|
7409
|
+
}
|
|
7410
|
+
function recordUsage(root, tool, result, client, ms) {
|
|
7411
|
+
recordSaving(
|
|
7412
|
+
root,
|
|
7413
|
+
{ ...measureCall(tool, result), source: "mcp", client: sanitizeClient(client), ...ms !== void 0 ? { ms } : {} },
|
|
7414
|
+
Date.now()
|
|
7415
|
+
);
|
|
7416
|
+
}
|
|
7417
|
+
function measureCall(tool, result) {
|
|
7418
|
+
const outcome = classifyOutcome(result);
|
|
7419
|
+
let vgTokens = 0;
|
|
7420
|
+
let baselineTokens = 0;
|
|
7421
|
+
if (SAVINGS_TOOLS.has(tool) && result && typeof result === "object") {
|
|
7422
|
+
vgTokens = countTokens(renderedText(renderToolResult(result)));
|
|
7423
|
+
baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
|
|
7424
|
+
}
|
|
7425
|
+
return { tool, outcome, vgTokens, baselineTokens };
|
|
7426
|
+
}
|
|
7427
|
+
function detectClient(server) {
|
|
7428
|
+
try {
|
|
7429
|
+
const info = server.getClientVersion?.();
|
|
7430
|
+
return typeof info?.name === "string" ? info.name : void 0;
|
|
7431
|
+
} catch {
|
|
7432
|
+
return void 0;
|
|
7433
|
+
}
|
|
7434
|
+
}
|
|
7435
|
+
function classifyOutcome(result) {
|
|
7436
|
+
if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
|
|
7437
|
+
if (!result || typeof result !== "object") return "miss";
|
|
7438
|
+
const r = result;
|
|
7439
|
+
if (typeof r.error === "string" && r.error) return "miss";
|
|
7440
|
+
if (r.connected === false) return "miss";
|
|
7441
|
+
if (Array.isArray(r.matches) && r.matches.length === 0) return "miss";
|
|
7442
|
+
return isPartial(r) ? "partial" : "complete";
|
|
7443
|
+
}
|
|
7444
|
+
function isPartial(r) {
|
|
7445
|
+
if (r.moreAvailable === true) return true;
|
|
7446
|
+
if (r._truncated && typeof r._truncated === "object") return true;
|
|
7447
|
+
for (const [key, value] of Object.entries(r)) {
|
|
7448
|
+
if (typeof value !== "number" || !key.endsWith("Total")) continue;
|
|
7449
|
+
const base = key.slice(0, -"Total".length);
|
|
7450
|
+
const shown = Array.isArray(r[base]) ? r[base].length : 0;
|
|
7451
|
+
if (value > shown) return true;
|
|
7452
|
+
}
|
|
7453
|
+
return false;
|
|
7454
|
+
}
|
|
7455
|
+
function renderedText(rendered) {
|
|
7456
|
+
const block = rendered.content?.find((b) => b.type === "text");
|
|
7457
|
+
return block && "text" in block && typeof block.text === "string" ? block.text : "";
|
|
7458
|
+
}
|
|
7459
|
+
function referencedFiles(result) {
|
|
7460
|
+
const r = result;
|
|
7461
|
+
const files = /* @__PURE__ */ new Set();
|
|
7462
|
+
if (typeof r.file === "string" && r.file) files.add(r.file);
|
|
7463
|
+
for (const m of asArray(r.matches)) {
|
|
7464
|
+
const f = m.file;
|
|
7465
|
+
if (typeof f === "string" && f) files.add(f);
|
|
7466
|
+
}
|
|
7467
|
+
for (const name of [...asArray(r.calls), ...asArray(r.calledBy)]) {
|
|
7468
|
+
if (typeof name !== "string") continue;
|
|
7469
|
+
const i = name.indexOf(":");
|
|
7470
|
+
if (i > 0) files.add(name.slice(0, i));
|
|
7471
|
+
}
|
|
7472
|
+
return files;
|
|
7473
|
+
}
|
|
7474
|
+
function asArray(v) {
|
|
7475
|
+
return Array.isArray(v) ? v : [];
|
|
7476
|
+
}
|
|
7477
|
+
function errorResult(message) {
|
|
7478
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
7479
|
+
}
|
|
7388
7480
|
|
|
7389
7481
|
// src/engine/export.ts
|
|
7390
7482
|
function formatForExt(ext) {
|
|
@@ -7589,6 +7681,6 @@ function spdx(ctx) {
|
|
|
7589
7681
|
return JSON.stringify(doc, null, 2) + "\n";
|
|
7590
7682
|
}
|
|
7591
7683
|
|
|
7592
|
-
export { ASSISTANTS, CLI_TOOL_ALIASES, CliError,
|
|
7593
|
-
//# sourceMappingURL=chunk-
|
|
7594
|
-
//# sourceMappingURL=chunk-
|
|
7684
|
+
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, 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 };
|
|
7685
|
+
//# sourceMappingURL=chunk-ZFDVHKID.js.map
|
|
7686
|
+
//# sourceMappingURL=chunk-ZFDVHKID.js.map
|