@vibgrate/cli 2026.708.3 → 2026.709.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +9 -1
- package/dist/baseline-7AFTGG3I.js +6 -0
- package/dist/{baseline-AUC3HAXW.js.map → baseline-7AFTGG3I.js.map} +1 -1
- package/dist/chunk-IRZCYPFC.js +3735 -0
- package/dist/chunk-IRZCYPFC.js.map +1 -0
- package/dist/{chunk-2DJ3UXG7.js → chunk-MPKMAERO.js} +496 -405
- package/dist/chunk-MPKMAERO.js.map +1 -0
- package/dist/{chunk-QUELGOCR.js → chunk-PXZ5KPVN.js} +4 -4
- package/dist/{chunk-QUELGOCR.js.map → chunk-PXZ5KPVN.js.map} +1 -1
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +819 -527
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/skills/vg/SKILL.md +26 -15
- package/dist/baseline-AUC3HAXW.js +0 -6
- package/dist/chunk-2DJ3UXG7.js.map +0 -1
- package/dist/chunk-JXGVMXYE.js +0 -31
- package/dist/chunk-JXGVMXYE.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-X5YT263H.js';
|
|
2
|
-
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-
|
|
3
|
-
import * as
|
|
2
|
+
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-PXZ5KPVN.js';
|
|
3
|
+
import * as fs19 from 'fs';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
|
-
import * as
|
|
5
|
+
import * as path19 from 'path';
|
|
6
6
|
import ignore from 'ignore';
|
|
7
7
|
import * as v8 from 'v8';
|
|
8
8
|
import Graph from 'graphology';
|
|
@@ -38,9 +38,9 @@ function whichOnPath(cmd) {
|
|
|
38
38
|
}
|
|
39
39
|
function isOwnBinary(binPath) {
|
|
40
40
|
try {
|
|
41
|
-
const real =
|
|
41
|
+
const real = fs19.realpathSync(binPath);
|
|
42
42
|
if (/[\\/]@vibgrate[\\/]cli[\\/]/.test(real)) return true;
|
|
43
|
-
const head =
|
|
43
|
+
const head = fs19.readFileSync(real, { encoding: "utf8" }).slice(0, 2048);
|
|
44
44
|
return head.includes("@vibgrate/cli") || head.includes("vibgrate");
|
|
45
45
|
} catch {
|
|
46
46
|
return false;
|
|
@@ -50,7 +50,7 @@ function isEphemeralNpxBinary(binPath) {
|
|
|
50
50
|
const NPX_SEGMENT = /[\\/]_npx[\\/]/;
|
|
51
51
|
if (NPX_SEGMENT.test(binPath)) return true;
|
|
52
52
|
try {
|
|
53
|
-
return NPX_SEGMENT.test(
|
|
53
|
+
return NPX_SEGMENT.test(fs19.realpathSync(binPath));
|
|
54
54
|
} catch {
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
@@ -216,19 +216,19 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
|
216
216
|
"flake.lock"
|
|
217
217
|
]);
|
|
218
218
|
function toPosix(p) {
|
|
219
|
-
return p.split(
|
|
219
|
+
return p.split(path19.sep).join("/");
|
|
220
220
|
}
|
|
221
221
|
function buildRootIgnore(root, exclude) {
|
|
222
222
|
const ig = ignore();
|
|
223
|
-
const gitignorePath =
|
|
224
|
-
if (
|
|
225
|
-
ig.add(
|
|
223
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
224
|
+
if (fs19.existsSync(gitignorePath)) {
|
|
225
|
+
ig.add(fs19.readFileSync(gitignorePath, "utf8"));
|
|
226
226
|
}
|
|
227
227
|
if (exclude.length) ig.add(exclude);
|
|
228
228
|
return ig;
|
|
229
229
|
}
|
|
230
230
|
function discover(options) {
|
|
231
|
-
const root =
|
|
231
|
+
const root = path19.resolve(options.root);
|
|
232
232
|
const onlyLangs = (options.only ?? []).filter(Boolean);
|
|
233
233
|
const allowLang = (lang) => onlyLangs.length === 0 || onlyLangs.includes(lang.id);
|
|
234
234
|
for (const id of onlyLangs) {
|
|
@@ -237,27 +237,27 @@ function discover(options) {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
const rootIg = buildRootIgnore(root, options.exclude ?? []);
|
|
240
|
-
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) =>
|
|
240
|
+
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path19.resolve(root, p)) : [root]).filter((p) => fs19.existsSync(p));
|
|
241
241
|
const found = /* @__PURE__ */ new Map();
|
|
242
242
|
const considerFile = (abs) => {
|
|
243
|
-
const rel2 = toPosix(
|
|
243
|
+
const rel2 = toPosix(path19.relative(root, abs));
|
|
244
244
|
if (rel2.startsWith("..")) return;
|
|
245
245
|
if (rel2 === "" || rootIg.ignores(rel2)) return;
|
|
246
|
-
if (SKIP_FILES.has(
|
|
247
|
-
const lang = langForExtension(
|
|
246
|
+
if (SKIP_FILES.has(path19.basename(abs).toLowerCase())) return;
|
|
247
|
+
const lang = langForExtension(path19.extname(abs));
|
|
248
248
|
if (!lang || !allowLang(lang)) return;
|
|
249
249
|
found.set(rel2, { rel: rel2, abs, lang });
|
|
250
250
|
};
|
|
251
251
|
const walk2 = (dir) => {
|
|
252
252
|
let entries;
|
|
253
253
|
try {
|
|
254
|
-
entries =
|
|
254
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
255
255
|
} catch {
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
258
258
|
for (const entry of entries) {
|
|
259
|
-
const abs =
|
|
260
|
-
const rel2 = toPosix(
|
|
259
|
+
const abs = path19.join(dir, entry.name);
|
|
260
|
+
const rel2 = toPosix(path19.relative(root, abs));
|
|
261
261
|
if (entry.isDirectory()) {
|
|
262
262
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
263
263
|
if (rel2 && rootIg.ignores(`${rel2}/`)) continue;
|
|
@@ -268,7 +268,7 @@ function discover(options) {
|
|
|
268
268
|
}
|
|
269
269
|
};
|
|
270
270
|
for (const scope of scopeAbs) {
|
|
271
|
-
const stat =
|
|
271
|
+
const stat = fs19.statSync(scope);
|
|
272
272
|
if (stat.isDirectory()) walk2(scope);
|
|
273
273
|
else if (stat.isFile()) considerFile(scope);
|
|
274
274
|
}
|
|
@@ -434,10 +434,10 @@ function buildModuleResolver(root, relSet) {
|
|
|
434
434
|
resolve(fromRel, source) {
|
|
435
435
|
if (source.includes("\\")) source = source.split("\\").join(".");
|
|
436
436
|
if (source.startsWith("./") || source.startsWith("../")) {
|
|
437
|
-
return probe(posixJoin(
|
|
437
|
+
return probe(posixJoin(path19.posix.dirname(fromRel), source));
|
|
438
438
|
}
|
|
439
439
|
if (/\.[A-Za-z0-9]+$/.test(source) && !source.startsWith("<")) {
|
|
440
|
-
const hit = probe(posixJoin(
|
|
440
|
+
const hit = probe(posixJoin(path19.posix.dirname(fromRel), source));
|
|
441
441
|
if (hit) return hit;
|
|
442
442
|
}
|
|
443
443
|
if (source.startsWith(".")) {
|
|
@@ -508,8 +508,8 @@ function resolvePyRelative(probe, fromRel, source) {
|
|
|
508
508
|
let dots = 0;
|
|
509
509
|
while (source[dots] === ".") dots++;
|
|
510
510
|
const rest = source.slice(dots);
|
|
511
|
-
let dir =
|
|
512
|
-
for (let i = 1; i < dots; i++) dir =
|
|
511
|
+
let dir = path19.posix.dirname(fromRel);
|
|
512
|
+
for (let i = 1; i < dots; i++) dir = path19.posix.dirname(dir);
|
|
513
513
|
const sub = rest.replace(/\./g, "/");
|
|
514
514
|
return probe(sub ? posixJoin(dir, sub) : dir);
|
|
515
515
|
}
|
|
@@ -517,7 +517,7 @@ function relativeResolver(relSet) {
|
|
|
517
517
|
const probe = makeProbe(relSet);
|
|
518
518
|
return {
|
|
519
519
|
resolve(fromRel, source) {
|
|
520
|
-
if (source.startsWith(".")) return probe(posixJoin(
|
|
520
|
+
if (source.startsWith(".")) return probe(posixJoin(path19.posix.dirname(fromRel), source));
|
|
521
521
|
return resolveDotted(probe, source);
|
|
522
522
|
}
|
|
523
523
|
};
|
|
@@ -535,12 +535,12 @@ function makeProbe(relSet) {
|
|
|
535
535
|
};
|
|
536
536
|
}
|
|
537
537
|
function normalizeRel(p) {
|
|
538
|
-
const norm =
|
|
538
|
+
const norm = path19.posix.normalize(p);
|
|
539
539
|
const stripped = norm.startsWith("./") ? norm.slice(2) : norm;
|
|
540
540
|
return stripped.startsWith("..") ? "" : stripped;
|
|
541
541
|
}
|
|
542
542
|
function posixJoin(a, b) {
|
|
543
|
-
return
|
|
543
|
+
return path19.posix.normalize(path19.posix.join(a, b));
|
|
544
544
|
}
|
|
545
545
|
function matchPattern(pattern, source) {
|
|
546
546
|
const star = pattern.indexOf("*");
|
|
@@ -553,8 +553,8 @@ function matchPattern(pattern, source) {
|
|
|
553
553
|
}
|
|
554
554
|
function loadTsconfigPaths(root) {
|
|
555
555
|
for (const name of ["tsconfig.base.json", "tsconfig.json"]) {
|
|
556
|
-
const file =
|
|
557
|
-
if (
|
|
556
|
+
const file = path19.join(root, name);
|
|
557
|
+
if (fs19.existsSync(file)) {
|
|
558
558
|
const merged = readTsconfigChain(root, file, /* @__PURE__ */ new Set());
|
|
559
559
|
if (merged && merged.paths.length) return merged;
|
|
560
560
|
}
|
|
@@ -566,21 +566,21 @@ function readTsconfigChain(root, file, seen) {
|
|
|
566
566
|
seen.add(file);
|
|
567
567
|
let cfg;
|
|
568
568
|
try {
|
|
569
|
-
cfg = parseJsonc(
|
|
569
|
+
cfg = parseJsonc(fs19.readFileSync(file, "utf8"));
|
|
570
570
|
} catch {
|
|
571
571
|
return null;
|
|
572
572
|
}
|
|
573
|
-
const dir =
|
|
573
|
+
const dir = path19.dirname(file);
|
|
574
574
|
let base = { baseUrlRel: "", paths: [] };
|
|
575
575
|
if (cfg.extends && cfg.extends.startsWith(".")) {
|
|
576
|
-
const extPath =
|
|
577
|
-
if (
|
|
576
|
+
const extPath = path19.resolve(dir, cfg.extends.endsWith(".json") ? cfg.extends : `${cfg.extends}.json`);
|
|
577
|
+
if (fs19.existsSync(extPath)) {
|
|
578
578
|
const inherited = readTsconfigChain(root, extPath, seen);
|
|
579
579
|
if (inherited) base = inherited;
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
const co = cfg.compilerOptions ?? {};
|
|
583
|
-
const baseUrlRel = co.baseUrl !== void 0 ?
|
|
583
|
+
const baseUrlRel = co.baseUrl !== void 0 ? path19.relative(root, path19.resolve(dir, co.baseUrl)).split(path19.sep).join("/") : base.baseUrlRel;
|
|
584
584
|
const paths = [...base.paths];
|
|
585
585
|
if (co.paths) for (const [k, v] of Object.entries(co.paths)) paths.push([k, v]);
|
|
586
586
|
return { baseUrlRel, paths };
|
|
@@ -588,27 +588,27 @@ function readTsconfigChain(root, file, seen) {
|
|
|
588
588
|
function loadWorkspacePackages(root) {
|
|
589
589
|
const map = /* @__PURE__ */ new Map();
|
|
590
590
|
const globs = [];
|
|
591
|
-
const rootPkg =
|
|
592
|
-
if (
|
|
591
|
+
const rootPkg = path19.join(root, "package.json");
|
|
592
|
+
if (fs19.existsSync(rootPkg)) {
|
|
593
593
|
try {
|
|
594
|
-
const pkg = JSON.parse(
|
|
594
|
+
const pkg = JSON.parse(fs19.readFileSync(rootPkg, "utf8"));
|
|
595
595
|
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
596
596
|
if (ws) globs.push(...ws);
|
|
597
597
|
} catch {
|
|
598
598
|
}
|
|
599
599
|
}
|
|
600
|
-
const pnpmWs =
|
|
601
|
-
if (
|
|
602
|
-
for (const line of
|
|
600
|
+
const pnpmWs = path19.join(root, "pnpm-workspace.yaml");
|
|
601
|
+
if (fs19.existsSync(pnpmWs)) {
|
|
602
|
+
for (const line of fs19.readFileSync(pnpmWs, "utf8").split("\n")) {
|
|
603
603
|
const m = /^\s*-\s*['"]?([^'"\n]+)['"]?\s*$/.exec(line);
|
|
604
604
|
if (m) globs.push(m[1].trim());
|
|
605
605
|
}
|
|
606
606
|
}
|
|
607
607
|
for (const dir of expandWorkspaceGlobs(root, globs)) {
|
|
608
|
-
const pj =
|
|
608
|
+
const pj = path19.join(dir, "package.json");
|
|
609
609
|
try {
|
|
610
|
-
const name = JSON.parse(
|
|
611
|
-
if (name) map.set(name,
|
|
610
|
+
const name = JSON.parse(fs19.readFileSync(pj, "utf8")).name;
|
|
611
|
+
if (name) map.set(name, path19.relative(root, dir).split(path19.sep).join("/"));
|
|
612
612
|
} catch {
|
|
613
613
|
}
|
|
614
614
|
}
|
|
@@ -618,10 +618,10 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
618
618
|
const out = /* @__PURE__ */ new Set();
|
|
619
619
|
for (const glob of globs) {
|
|
620
620
|
const clean = glob.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
|
|
621
|
-
const baseDir =
|
|
621
|
+
const baseDir = path19.join(root, clean);
|
|
622
622
|
const recurse = glob.endsWith("**");
|
|
623
623
|
if (glob === clean) {
|
|
624
|
-
if (
|
|
624
|
+
if (fs19.existsSync(path19.join(baseDir, "package.json"))) out.add(baseDir);
|
|
625
625
|
continue;
|
|
626
626
|
}
|
|
627
627
|
collectPackageDirs(baseDir, recurse ? 4 : 1, out);
|
|
@@ -632,14 +632,14 @@ function collectPackageDirs(dir, depth, out) {
|
|
|
632
632
|
if (depth < 0) return;
|
|
633
633
|
let entries;
|
|
634
634
|
try {
|
|
635
|
-
entries =
|
|
635
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
636
636
|
} catch {
|
|
637
637
|
return;
|
|
638
638
|
}
|
|
639
639
|
for (const e of entries) {
|
|
640
640
|
if (!e.isDirectory() || e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
641
|
-
const child =
|
|
642
|
-
if (
|
|
641
|
+
const child = path19.join(dir, e.name);
|
|
642
|
+
if (fs19.existsSync(path19.join(child, "package.json"))) out.add(child);
|
|
643
643
|
else collectPackageDirs(child, depth - 1, out);
|
|
644
644
|
}
|
|
645
645
|
}
|
|
@@ -1137,14 +1137,14 @@ var DEFAULT_PATHS = [
|
|
|
1137
1137
|
];
|
|
1138
1138
|
function loadCoverage(root, explicit) {
|
|
1139
1139
|
const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
|
|
1140
|
-
(p) =>
|
|
1140
|
+
(p) => path19.resolve(root, p)
|
|
1141
1141
|
);
|
|
1142
|
-
const found = candidates.filter((p) =>
|
|
1142
|
+
const found = candidates.filter((p) => fs19.existsSync(p));
|
|
1143
1143
|
if (found.length === 0) return null;
|
|
1144
1144
|
const map = /* @__PURE__ */ new Map();
|
|
1145
1145
|
for (const file of found) {
|
|
1146
1146
|
try {
|
|
1147
|
-
const text =
|
|
1147
|
+
const text = fs19.readFileSync(file, "utf8");
|
|
1148
1148
|
if (file.endsWith(".json")) mergeIstanbul(map, text, root);
|
|
1149
1149
|
else mergeLcov(map, text, root);
|
|
1150
1150
|
} catch {
|
|
@@ -1153,8 +1153,8 @@ function loadCoverage(root, explicit) {
|
|
|
1153
1153
|
return map.size ? map : null;
|
|
1154
1154
|
}
|
|
1155
1155
|
function rel(root, p) {
|
|
1156
|
-
const abs =
|
|
1157
|
-
return
|
|
1156
|
+
const abs = path19.isAbsolute(p) ? p : path19.resolve(root, p);
|
|
1157
|
+
return path19.relative(root, abs).split(path19.sep).join("/");
|
|
1158
1158
|
}
|
|
1159
1159
|
function bump(map, file, line, hits) {
|
|
1160
1160
|
let lh = map.get(file);
|
|
@@ -1490,7 +1490,7 @@ async function parseInline(files, options) {
|
|
|
1490
1490
|
onProgress?.(0, files.length);
|
|
1491
1491
|
for (const file of files) {
|
|
1492
1492
|
try {
|
|
1493
|
-
const source =
|
|
1493
|
+
const source = fs19.readFileSync(file.abs, "utf8");
|
|
1494
1494
|
out.push(await parseSource(file.rel, file.lang.id, source));
|
|
1495
1495
|
} catch (err) {
|
|
1496
1496
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
@@ -1545,9 +1545,9 @@ function isWorkerOom(err) {
|
|
|
1545
1545
|
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1546
1546
|
}
|
|
1547
1547
|
function resolveWorkerFile() {
|
|
1548
|
-
const here =
|
|
1549
|
-
const candidate =
|
|
1550
|
-
return
|
|
1548
|
+
const here = path19.dirname(fileURLToPath(import.meta.url));
|
|
1549
|
+
const candidate = path19.join(here, "parse-worker.js");
|
|
1550
|
+
return fs19.existsSync(candidate) ? candidate : null;
|
|
1551
1551
|
}
|
|
1552
1552
|
function chunk(items, buckets) {
|
|
1553
1553
|
const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
|
|
@@ -1585,7 +1585,7 @@ function resolve4(parses, resolver) {
|
|
|
1585
1585
|
addNode(baseNodes, {
|
|
1586
1586
|
id: fileId,
|
|
1587
1587
|
kind: "file",
|
|
1588
|
-
name:
|
|
1588
|
+
name: path19.posix.basename(p.rel),
|
|
1589
1589
|
qualifiedName: p.rel,
|
|
1590
1590
|
file: p.rel,
|
|
1591
1591
|
span: { start: 1, end: 1 },
|
|
@@ -2002,14 +2002,14 @@ function enclosing3(nodes, line) {
|
|
|
2002
2002
|
return best;
|
|
2003
2003
|
}
|
|
2004
2004
|
function normalize2(p) {
|
|
2005
|
-
return
|
|
2005
|
+
return path19.resolve(p).split(path19.sep).join("/");
|
|
2006
2006
|
}
|
|
2007
2007
|
var CACHE_VERSION = "vg-parse-cache/2";
|
|
2008
2008
|
function cacheDir(root) {
|
|
2009
|
-
return
|
|
2009
|
+
return path19.join(root, ".vibgrate", "cache");
|
|
2010
2010
|
}
|
|
2011
2011
|
function cachePath(root) {
|
|
2012
|
-
return
|
|
2012
|
+
return path19.join(cacheDir(root), "parse-cache.json");
|
|
2013
2013
|
}
|
|
2014
2014
|
function loadCache(root, opts) {
|
|
2015
2015
|
const file = cachePath(root);
|
|
@@ -2019,9 +2019,9 @@ function loadCache(root, opts) {
|
|
|
2019
2019
|
grammars: opts.grammars,
|
|
2020
2020
|
entries: {}
|
|
2021
2021
|
};
|
|
2022
|
-
if (!opts.disabled &&
|
|
2022
|
+
if (!opts.disabled && fs19.existsSync(file)) {
|
|
2023
2023
|
try {
|
|
2024
|
-
const loaded = JSON.parse(
|
|
2024
|
+
const loaded = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
2025
2025
|
if (loaded.version === CACHE_VERSION && loaded.toolVersion === opts.toolVersion && loaded.grammars === opts.grammars && loaded.entries) {
|
|
2026
2026
|
data = loaded;
|
|
2027
2027
|
}
|
|
@@ -2042,8 +2042,8 @@ function loadCache(root, opts) {
|
|
|
2042
2042
|
}
|
|
2043
2043
|
},
|
|
2044
2044
|
save() {
|
|
2045
|
-
|
|
2046
|
-
|
|
2045
|
+
fs19.mkdirSync(cacheDir(root), { recursive: true });
|
|
2046
|
+
fs19.writeFileSync(file, stableStringify(data, 0));
|
|
2047
2047
|
}
|
|
2048
2048
|
};
|
|
2049
2049
|
}
|
|
@@ -2075,7 +2075,7 @@ function epistemicBreakdown(edges) {
|
|
|
2075
2075
|
// src/engine/build.ts
|
|
2076
2076
|
async function buildGraph(options) {
|
|
2077
2077
|
const start = nowMs();
|
|
2078
|
-
const root =
|
|
2078
|
+
const root = path19.resolve(options.root);
|
|
2079
2079
|
const files = discover({
|
|
2080
2080
|
root,
|
|
2081
2081
|
only: options.only,
|
|
@@ -2103,7 +2103,7 @@ async function buildGraph(options) {
|
|
|
2103
2103
|
for (const file of files) {
|
|
2104
2104
|
let stat;
|
|
2105
2105
|
try {
|
|
2106
|
-
stat =
|
|
2106
|
+
stat = fs19.statSync(file.abs);
|
|
2107
2107
|
} catch {
|
|
2108
2108
|
continue;
|
|
2109
2109
|
}
|
|
@@ -2121,7 +2121,7 @@ async function buildGraph(options) {
|
|
|
2121
2121
|
}
|
|
2122
2122
|
let hash = "";
|
|
2123
2123
|
try {
|
|
2124
|
-
hash = hashBytes(
|
|
2124
|
+
hash = hashBytes(fs19.readFileSync(file.abs));
|
|
2125
2125
|
} catch {
|
|
2126
2126
|
continue;
|
|
2127
2127
|
}
|
|
@@ -2213,7 +2213,7 @@ async function buildGraph(options) {
|
|
|
2213
2213
|
toolchain
|
|
2214
2214
|
},
|
|
2215
2215
|
meta: {
|
|
2216
|
-
root:
|
|
2216
|
+
root: path19.basename(root) === "" ? "." : ".",
|
|
2217
2217
|
languages,
|
|
2218
2218
|
counts: {
|
|
2219
2219
|
nodes: analysis.nodes.length,
|
|
@@ -2259,14 +2259,14 @@ function toRepoRel(p) {
|
|
|
2259
2259
|
function loadScipIndex(root, explicit) {
|
|
2260
2260
|
const candidates = [
|
|
2261
2261
|
explicit,
|
|
2262
|
-
|
|
2263
|
-
|
|
2262
|
+
path19.join(root, "index.scip"),
|
|
2263
|
+
path19.join(root, ".vibgrate", "index.scip")
|
|
2264
2264
|
].filter((p) => Boolean(p));
|
|
2265
2265
|
for (const file of candidates) {
|
|
2266
|
-
const abs =
|
|
2267
|
-
if (!
|
|
2266
|
+
const abs = path19.isAbsolute(file) ? file : path19.resolve(root, file);
|
|
2267
|
+
if (!fs19.existsSync(abs)) continue;
|
|
2268
2268
|
try {
|
|
2269
|
-
const index = decodeScipIndex(new Uint8Array(
|
|
2269
|
+
const index = decodeScipIndex(new Uint8Array(fs19.readFileSync(abs)));
|
|
2270
2270
|
if (index.documents.length) {
|
|
2271
2271
|
return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
|
|
2272
2272
|
}
|
|
@@ -2427,46 +2427,46 @@ function esc(s) {
|
|
|
2427
2427
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2428
2428
|
}
|
|
2429
2429
|
function vibgrateDir(root) {
|
|
2430
|
-
return
|
|
2430
|
+
return path19.join(root, ".vibgrate");
|
|
2431
2431
|
}
|
|
2432
2432
|
function defaultGraphPath(root) {
|
|
2433
|
-
return
|
|
2433
|
+
return path19.join(vibgrateDir(root), "graph.json");
|
|
2434
2434
|
}
|
|
2435
2435
|
function writeArtifacts(graph, options) {
|
|
2436
2436
|
const dir = vibgrateDir(options.root);
|
|
2437
|
-
|
|
2437
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
2438
2438
|
const graphPath = options.graphPath ?? defaultGraphPath(options.root);
|
|
2439
|
-
|
|
2439
|
+
fs19.mkdirSync(path19.dirname(graphPath), { recursive: true });
|
|
2440
2440
|
const tmp = `${graphPath}.${process.pid}.tmp`;
|
|
2441
2441
|
try {
|
|
2442
|
-
|
|
2443
|
-
|
|
2442
|
+
fs19.writeFileSync(tmp, serializeGraph(graph));
|
|
2443
|
+
fs19.renameSync(tmp, graphPath);
|
|
2444
2444
|
} catch (err) {
|
|
2445
|
-
|
|
2445
|
+
fs19.rmSync(tmp, { force: true });
|
|
2446
2446
|
throw err;
|
|
2447
2447
|
}
|
|
2448
2448
|
const written = { graphPath };
|
|
2449
2449
|
if (options.report !== false) {
|
|
2450
|
-
const reportPath =
|
|
2451
|
-
|
|
2450
|
+
const reportPath = path19.join(dir, "GRAPH_REPORT.md");
|
|
2451
|
+
fs19.writeFileSync(reportPath, renderReport(graph));
|
|
2452
2452
|
written.reportPath = reportPath;
|
|
2453
2453
|
}
|
|
2454
2454
|
if (options.html !== false) {
|
|
2455
|
-
const htmlPath =
|
|
2456
|
-
|
|
2455
|
+
const htmlPath = path19.join(dir, "graph.html");
|
|
2456
|
+
fs19.writeFileSync(htmlPath, renderHtml(graph));
|
|
2457
2457
|
written.htmlPath = htmlPath;
|
|
2458
2458
|
}
|
|
2459
2459
|
if (graph.facts && graph.facts.length) {
|
|
2460
|
-
const factsPath =
|
|
2461
|
-
|
|
2460
|
+
const factsPath = path19.join(dir, "facts.jsonl");
|
|
2461
|
+
fs19.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
|
|
2462
2462
|
written.factsPath = factsPath;
|
|
2463
2463
|
}
|
|
2464
2464
|
return written;
|
|
2465
2465
|
}
|
|
2466
2466
|
function loadGraph(root, graphPath) {
|
|
2467
2467
|
const file = graphPath ?? defaultGraphPath(root);
|
|
2468
|
-
if (!
|
|
2469
|
-
return parseGraph(
|
|
2468
|
+
if (!fs19.existsSync(file)) return null;
|
|
2469
|
+
return parseGraph(fs19.readFileSync(file, "utf8"));
|
|
2470
2470
|
}
|
|
2471
2471
|
|
|
2472
2472
|
// src/engine/verify.ts
|
|
@@ -2543,7 +2543,7 @@ function isProcessAlive(pid) {
|
|
|
2543
2543
|
}
|
|
2544
2544
|
function lockIsStale(file, staleMs) {
|
|
2545
2545
|
try {
|
|
2546
|
-
const { pid, at } = JSON.parse(
|
|
2546
|
+
const { pid, at } = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
2547
2547
|
if (typeof at === "number" && Date.now() - at > staleMs) return true;
|
|
2548
2548
|
if (typeof pid === "number" && !isProcessAlive(pid)) return true;
|
|
2549
2549
|
return false;
|
|
@@ -2554,10 +2554,10 @@ function lockIsStale(file, staleMs) {
|
|
|
2554
2554
|
function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
2555
2555
|
const write = () => {
|
|
2556
2556
|
try {
|
|
2557
|
-
|
|
2558
|
-
const fd =
|
|
2559
|
-
|
|
2560
|
-
|
|
2557
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
2558
|
+
const fd = fs19.openSync(file, "wx");
|
|
2559
|
+
fs19.writeFileSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
2560
|
+
fs19.closeSync(fd);
|
|
2561
2561
|
return true;
|
|
2562
2562
|
} catch {
|
|
2563
2563
|
return false;
|
|
@@ -2566,7 +2566,7 @@ function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
|
2566
2566
|
if (write()) return true;
|
|
2567
2567
|
if (lockIsStale(file, staleMs)) {
|
|
2568
2568
|
try {
|
|
2569
|
-
|
|
2569
|
+
fs19.rmSync(file, { force: true });
|
|
2570
2570
|
} catch {
|
|
2571
2571
|
}
|
|
2572
2572
|
return write();
|
|
@@ -2575,7 +2575,7 @@ function acquireLock(file, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
|
2575
2575
|
}
|
|
2576
2576
|
function releaseLock(file) {
|
|
2577
2577
|
try {
|
|
2578
|
-
|
|
2578
|
+
fs19.rmSync(file, { force: true });
|
|
2579
2579
|
} catch {
|
|
2580
2580
|
}
|
|
2581
2581
|
}
|
|
@@ -2585,14 +2585,14 @@ function resolveEmbedModel(model) {
|
|
|
2585
2585
|
return model ?? "bge-small-en-v1.5";
|
|
2586
2586
|
}
|
|
2587
2587
|
function modelCacheDir() {
|
|
2588
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
2589
|
-
return
|
|
2588
|
+
const base = process.env.XDG_CACHE_HOME || path19.join(os.homedir(), ".cache");
|
|
2589
|
+
return path19.join(base, "vibgrate", "models");
|
|
2590
2590
|
}
|
|
2591
2591
|
function modelReadyMarker(modelId) {
|
|
2592
|
-
return
|
|
2592
|
+
return path19.join(modelCacheDir(), `.ready-${safe(modelId)}`);
|
|
2593
2593
|
}
|
|
2594
2594
|
function isModelReady(modelId = resolveEmbedModel()) {
|
|
2595
|
-
return
|
|
2595
|
+
return fs19.existsSync(modelReadyMarker(modelId));
|
|
2596
2596
|
}
|
|
2597
2597
|
function unavailableMessage(reason) {
|
|
2598
2598
|
switch (reason) {
|
|
@@ -2609,13 +2609,13 @@ function unavailableMessage(reason) {
|
|
|
2609
2609
|
}
|
|
2610
2610
|
function modelCacheInfo(modelId = resolveEmbedModel()) {
|
|
2611
2611
|
const dir = modelCacheDir();
|
|
2612
|
-
return { dir, present: isModelReady(modelId) ||
|
|
2612
|
+
return { dir, present: isModelReady(modelId) || fs19.existsSync(dir), bytes: dirSize(dir) };
|
|
2613
2613
|
}
|
|
2614
2614
|
function clearModelCache() {
|
|
2615
2615
|
const dir = modelCacheDir();
|
|
2616
2616
|
const bytes = dirSize(dir);
|
|
2617
2617
|
try {
|
|
2618
|
-
|
|
2618
|
+
fs19.rmSync(dir, { recursive: true, force: true });
|
|
2619
2619
|
} catch {
|
|
2620
2620
|
}
|
|
2621
2621
|
return bytes;
|
|
@@ -2624,16 +2624,16 @@ function dirSize(dir) {
|
|
|
2624
2624
|
let total = 0;
|
|
2625
2625
|
let entries;
|
|
2626
2626
|
try {
|
|
2627
|
-
entries =
|
|
2627
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
2628
2628
|
} catch {
|
|
2629
2629
|
return 0;
|
|
2630
2630
|
}
|
|
2631
2631
|
for (const e of entries) {
|
|
2632
|
-
const p =
|
|
2632
|
+
const p = path19.join(dir, e.name);
|
|
2633
2633
|
if (e.isDirectory()) total += dirSize(p);
|
|
2634
2634
|
else {
|
|
2635
2635
|
try {
|
|
2636
|
-
total +=
|
|
2636
|
+
total += fs19.statSync(p).size;
|
|
2637
2637
|
} catch {
|
|
2638
2638
|
}
|
|
2639
2639
|
}
|
|
@@ -2645,7 +2645,7 @@ function isPermissionError(e) {
|
|
|
2645
2645
|
return code === "EACCES" || code === "EPERM" || code === "EROFS";
|
|
2646
2646
|
}
|
|
2647
2647
|
function embeddingsCached(root, modelId) {
|
|
2648
|
-
return
|
|
2648
|
+
return fs19.existsSync(path19.join(cacheDir(root), `embeddings-${safe(modelId)}.json`));
|
|
2649
2649
|
}
|
|
2650
2650
|
async function loadEmbedder(options = {}) {
|
|
2651
2651
|
if (options.local) return null;
|
|
@@ -2659,7 +2659,7 @@ async function loadEmbedder(options = {}) {
|
|
|
2659
2659
|
if (!mod?.FlagEmbedding) return fail("not-installed");
|
|
2660
2660
|
const cache = modelCacheDir();
|
|
2661
2661
|
try {
|
|
2662
|
-
|
|
2662
|
+
fs19.mkdirSync(cache, { recursive: true });
|
|
2663
2663
|
} catch (e) {
|
|
2664
2664
|
return fail(isPermissionError(e) ? "no-permission" : "init-failed");
|
|
2665
2665
|
}
|
|
@@ -2674,7 +2674,7 @@ async function loadEmbedder(options = {}) {
|
|
|
2674
2674
|
"embedding model init timed out"
|
|
2675
2675
|
);
|
|
2676
2676
|
try {
|
|
2677
|
-
|
|
2677
|
+
fs19.writeFileSync(modelReadyMarker(modelId), (/* @__PURE__ */ new Date(0)).toISOString());
|
|
2678
2678
|
} catch {
|
|
2679
2679
|
}
|
|
2680
2680
|
return {
|
|
@@ -2772,7 +2772,7 @@ function cosine(a, b) {
|
|
|
2772
2772
|
var EMBED_CHUNK = 256;
|
|
2773
2773
|
var CACHE_WRITE_INTERVAL_MS = 1500;
|
|
2774
2774
|
function vectorCachePath(root, modelId) {
|
|
2775
|
-
return
|
|
2775
|
+
return path19.join(cacheDir(root), `embeddings-${safe(modelId)}.json`);
|
|
2776
2776
|
}
|
|
2777
2777
|
function countPending(graph, root, modelId) {
|
|
2778
2778
|
const entries = readCacheEntries(vectorCachePath(root, modelId), modelId);
|
|
@@ -2787,9 +2787,9 @@ function countPending(graph, root, modelId) {
|
|
|
2787
2787
|
return pending;
|
|
2788
2788
|
}
|
|
2789
2789
|
function readCacheEntries(file, modelId) {
|
|
2790
|
-
if (!
|
|
2790
|
+
if (!fs19.existsSync(file)) return {};
|
|
2791
2791
|
try {
|
|
2792
|
-
const loaded = JSON.parse(
|
|
2792
|
+
const loaded = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
2793
2793
|
if (loaded.model === modelId && loaded.entries) return loaded.entries;
|
|
2794
2794
|
} catch {
|
|
2795
2795
|
}
|
|
@@ -2798,9 +2798,9 @@ function readCacheEntries(file, modelId) {
|
|
|
2798
2798
|
async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
2799
2799
|
const file = vectorCachePath(root, embedder.id);
|
|
2800
2800
|
let cache = { model: embedder.id, entries: {} };
|
|
2801
|
-
if (
|
|
2801
|
+
if (fs19.existsSync(file)) {
|
|
2802
2802
|
try {
|
|
2803
|
-
const loaded = JSON.parse(
|
|
2803
|
+
const loaded = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
2804
2804
|
if (loaded.model === embedder.id && loaded.entries) cache = loaded;
|
|
2805
2805
|
} catch {
|
|
2806
2806
|
}
|
|
@@ -2818,10 +2818,10 @@ async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
|
2818
2818
|
}
|
|
2819
2819
|
const persist = () => {
|
|
2820
2820
|
try {
|
|
2821
|
-
|
|
2821
|
+
fs19.mkdirSync(cacheDir(root), { recursive: true });
|
|
2822
2822
|
const tmp = `${file}.tmp.${process.pid}`;
|
|
2823
|
-
|
|
2824
|
-
|
|
2823
|
+
fs19.writeFileSync(tmp, JSON.stringify(cache));
|
|
2824
|
+
fs19.renameSync(tmp, file);
|
|
2825
2825
|
} catch {
|
|
2826
2826
|
}
|
|
2827
2827
|
};
|
|
@@ -2859,21 +2859,21 @@ function safe(id) {
|
|
|
2859
2859
|
}
|
|
2860
2860
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2861
2861
|
function snapshotPath(root) {
|
|
2862
|
-
return
|
|
2862
|
+
return path19.join(cacheDir(root), "freshness.json");
|
|
2863
2863
|
}
|
|
2864
2864
|
function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
|
|
2865
2865
|
const files = {};
|
|
2866
2866
|
for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
|
|
2867
2867
|
const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
|
|
2868
2868
|
try {
|
|
2869
|
-
|
|
2870
|
-
|
|
2869
|
+
fs19.mkdirSync(cacheDir(root), { recursive: true });
|
|
2870
|
+
fs19.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2871
2871
|
} catch {
|
|
2872
2872
|
}
|
|
2873
2873
|
}
|
|
2874
2874
|
function loadSnapshot(root) {
|
|
2875
2875
|
try {
|
|
2876
|
-
const loaded = JSON.parse(
|
|
2876
|
+
const loaded = JSON.parse(fs19.readFileSync(snapshotPath(root), "utf8"));
|
|
2877
2877
|
if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
|
|
2878
2878
|
return { ...loaded, scope: loaded.scope ?? {} };
|
|
2879
2879
|
}
|
|
@@ -2909,7 +2909,7 @@ function probeFreshness(root) {
|
|
|
2909
2909
|
}
|
|
2910
2910
|
let stat;
|
|
2911
2911
|
try {
|
|
2912
|
-
stat =
|
|
2912
|
+
stat = fs19.statSync(file.abs);
|
|
2913
2913
|
} catch {
|
|
2914
2914
|
drift.removed.push(file.rel);
|
|
2915
2915
|
continue;
|
|
@@ -2917,7 +2917,7 @@ function probeFreshness(root) {
|
|
|
2917
2917
|
if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
|
|
2918
2918
|
let hash = "";
|
|
2919
2919
|
try {
|
|
2920
|
-
hash = hashBytes(
|
|
2920
|
+
hash = hashBytes(fs19.readFileSync(file.abs));
|
|
2921
2921
|
} catch {
|
|
2922
2922
|
drift.removed.push(file.rel);
|
|
2923
2923
|
continue;
|
|
@@ -2934,7 +2934,7 @@ function probeFreshness(root) {
|
|
|
2934
2934
|
}
|
|
2935
2935
|
if (absorbed && !hasDrift(drift)) {
|
|
2936
2936
|
try {
|
|
2937
|
-
|
|
2937
|
+
fs19.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2938
2938
|
} catch {
|
|
2939
2939
|
}
|
|
2940
2940
|
}
|
|
@@ -3005,6 +3005,15 @@ function push2(map, key, value) {
|
|
|
3005
3005
|
if (list) list.push(value);
|
|
3006
3006
|
else map.set(key, [value]);
|
|
3007
3007
|
}
|
|
3008
|
+
var INDEX_CACHE = /* @__PURE__ */ new WeakMap();
|
|
3009
|
+
function indexFor(graph) {
|
|
3010
|
+
let idx = INDEX_CACHE.get(graph);
|
|
3011
|
+
if (!idx) {
|
|
3012
|
+
idx = new GraphIndex(graph);
|
|
3013
|
+
INDEX_CACHE.set(graph, idx);
|
|
3014
|
+
}
|
|
3015
|
+
return idx;
|
|
3016
|
+
}
|
|
3008
3017
|
|
|
3009
3018
|
// src/engine/query.ts
|
|
3010
3019
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
@@ -3055,7 +3064,7 @@ function queryGraph(graph, question, options = {}) {
|
|
|
3055
3064
|
const limit = options.limit ?? 12;
|
|
3056
3065
|
const terms = tokenize(question);
|
|
3057
3066
|
const weightOf = termWeights(graph, terms);
|
|
3058
|
-
const index =
|
|
3067
|
+
const index = indexFor(graph);
|
|
3059
3068
|
const scored = [];
|
|
3060
3069
|
for (const node of graph.nodes) {
|
|
3061
3070
|
if (node.kind === "file" || node.kind === "external") continue;
|
|
@@ -3072,7 +3081,7 @@ async function queryGraphSemantic(graph, question, options) {
|
|
|
3072
3081
|
const limit = options.limit ?? 12;
|
|
3073
3082
|
const terms = tokenize(question);
|
|
3074
3083
|
const weightOf = termWeights(graph, terms);
|
|
3075
|
-
const index =
|
|
3084
|
+
const index = indexFor(graph);
|
|
3076
3085
|
const queryVec = await options.embedder.embedQuery(question);
|
|
3077
3086
|
const lexRaw = /* @__PURE__ */ new Map();
|
|
3078
3087
|
let lexMax = 0;
|
|
@@ -3215,7 +3224,7 @@ function round2(x) {
|
|
|
3215
3224
|
}
|
|
3216
3225
|
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
3217
3226
|
function refreshLockPath(root) {
|
|
3218
|
-
return
|
|
3227
|
+
return path19.join(cacheDir(root), "refresh.lock");
|
|
3219
3228
|
}
|
|
3220
3229
|
async function refreshIfStale(root, opts = {}) {
|
|
3221
3230
|
const probe = probeFreshness(root);
|
|
@@ -3246,8 +3255,8 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
3246
3255
|
const dir = vibgrateDir(root);
|
|
3247
3256
|
writeArtifacts(result.graph, {
|
|
3248
3257
|
root,
|
|
3249
|
-
report:
|
|
3250
|
-
html:
|
|
3258
|
+
report: fs19.existsSync(path19.join(dir, "GRAPH_REPORT.md")),
|
|
3259
|
+
html: fs19.existsSync(path19.join(dir, "graph.html"))
|
|
3251
3260
|
});
|
|
3252
3261
|
}
|
|
3253
3262
|
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
@@ -3266,6 +3275,157 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
3266
3275
|
releaseLock(lock);
|
|
3267
3276
|
}
|
|
3268
3277
|
}
|
|
3278
|
+
var LEDGER = "savings.jsonl";
|
|
3279
|
+
var PER_FILE_TOKENS = 400;
|
|
3280
|
+
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node"]);
|
|
3281
|
+
var CLI_TOOL_ALIASES = {
|
|
3282
|
+
ask: "query_graph",
|
|
3283
|
+
show: "get_node",
|
|
3284
|
+
impact: "impact_of",
|
|
3285
|
+
path: "find_path",
|
|
3286
|
+
hubs: "list_hubs",
|
|
3287
|
+
areas: "list_areas",
|
|
3288
|
+
tree: "tree"
|
|
3289
|
+
};
|
|
3290
|
+
function sanitizeClient(name) {
|
|
3291
|
+
if (typeof name !== "string") return "unknown";
|
|
3292
|
+
const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
3293
|
+
return cleaned || "unknown";
|
|
3294
|
+
}
|
|
3295
|
+
function recordCliCall(root, entry, now) {
|
|
3296
|
+
recordSaving(
|
|
3297
|
+
root,
|
|
3298
|
+
{
|
|
3299
|
+
tool: entry.tool,
|
|
3300
|
+
source: "cli",
|
|
3301
|
+
client: sanitizeClient(entry.client),
|
|
3302
|
+
outcome: entry.outcome,
|
|
3303
|
+
vgTokens: entry.vgTokens ?? 0,
|
|
3304
|
+
baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
|
|
3305
|
+
},
|
|
3306
|
+
now
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
function ledgerPath(root) {
|
|
3310
|
+
return path19.join(cacheDir(root), LEDGER);
|
|
3311
|
+
}
|
|
3312
|
+
function savingsRecorded(root) {
|
|
3313
|
+
return fs19.existsSync(ledgerPath(root));
|
|
3314
|
+
}
|
|
3315
|
+
function recordSaving(root, entry, now) {
|
|
3316
|
+
try {
|
|
3317
|
+
fs19.mkdirSync(cacheDir(root), { recursive: true });
|
|
3318
|
+
const line = JSON.stringify({ ts: now, ...entry });
|
|
3319
|
+
fs19.appendFileSync(ledgerPath(root), line + "\n");
|
|
3320
|
+
} catch {
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
var DEFAULT_RATE_PER_M = 3;
|
|
3324
|
+
var DEFAULT_RATE_LABEL = "input @ $3/1M";
|
|
3325
|
+
function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
3326
|
+
const file = ledgerPath(root);
|
|
3327
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
3328
|
+
let queries = 0;
|
|
3329
|
+
let vgTokens = 0;
|
|
3330
|
+
let baselineTokens = 0;
|
|
3331
|
+
if (fs19.existsSync(file)) {
|
|
3332
|
+
for (const line of fs19.readFileSync(file, "utf8").split("\n")) {
|
|
3333
|
+
if (!line.trim()) continue;
|
|
3334
|
+
try {
|
|
3335
|
+
const e = JSON.parse(line);
|
|
3336
|
+
if (e.ts < cutoff) continue;
|
|
3337
|
+
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
3338
|
+
queries++;
|
|
3339
|
+
vgTokens += e.vgTokens;
|
|
3340
|
+
baselineTokens += e.baselineTokens;
|
|
3341
|
+
} catch {
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
const estCostVg = vgTokens / 1e6 * ratePerM;
|
|
3346
|
+
const estCostBaseline = baselineTokens / 1e6 * ratePerM;
|
|
3347
|
+
return {
|
|
3348
|
+
enabled: savingsRecorded(root),
|
|
3349
|
+
days,
|
|
3350
|
+
queries,
|
|
3351
|
+
vgTokens,
|
|
3352
|
+
baselineTokens,
|
|
3353
|
+
ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
|
|
3354
|
+
estCostVg: round22(estCostVg),
|
|
3355
|
+
estCostBaseline: round22(estCostBaseline),
|
|
3356
|
+
saved: round22(estCostBaseline - estCostVg),
|
|
3357
|
+
rateLabel: DEFAULT_RATE_LABEL
|
|
3358
|
+
};
|
|
3359
|
+
}
|
|
3360
|
+
function round22(x) {
|
|
3361
|
+
return Math.round(x * 100) / 100;
|
|
3362
|
+
}
|
|
3363
|
+
function toDimensionStats(byKey) {
|
|
3364
|
+
return [...byKey.entries()].map(([key, r]) => {
|
|
3365
|
+
const calls = r.complete + r.partial + r.miss;
|
|
3366
|
+
return {
|
|
3367
|
+
key,
|
|
3368
|
+
calls,
|
|
3369
|
+
complete: r.complete,
|
|
3370
|
+
partial: r.partial,
|
|
3371
|
+
miss: r.miss,
|
|
3372
|
+
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
3373
|
+
};
|
|
3374
|
+
}).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
|
|
3375
|
+
}
|
|
3376
|
+
function readUsage(root, days, now) {
|
|
3377
|
+
const file = ledgerPath(root);
|
|
3378
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
3379
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
3380
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
3381
|
+
const byClient = /* @__PURE__ */ new Map();
|
|
3382
|
+
const bump2 = (m, key, outcome) => {
|
|
3383
|
+
const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
|
|
3384
|
+
row[outcome]++;
|
|
3385
|
+
m.set(key, row);
|
|
3386
|
+
};
|
|
3387
|
+
if (fs19.existsSync(file)) {
|
|
3388
|
+
for (const line of fs19.readFileSync(file, "utf8").split("\n")) {
|
|
3389
|
+
if (!line.trim()) continue;
|
|
3390
|
+
try {
|
|
3391
|
+
const e = JSON.parse(line);
|
|
3392
|
+
if (e.ts < cutoff) continue;
|
|
3393
|
+
const outcome = e.outcome ?? "complete";
|
|
3394
|
+
bump2(byTool, e.tool, outcome);
|
|
3395
|
+
bump2(bySource, e.source ?? "mcp", outcome);
|
|
3396
|
+
bump2(byClient, e.client ?? "unknown", outcome);
|
|
3397
|
+
} catch {
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
const commands = toDimensionStats(byTool).map((d) => ({
|
|
3402
|
+
tool: d.key,
|
|
3403
|
+
calls: d.calls,
|
|
3404
|
+
complete: d.complete,
|
|
3405
|
+
partial: d.partial,
|
|
3406
|
+
miss: d.miss,
|
|
3407
|
+
successPct: d.successPct
|
|
3408
|
+
}));
|
|
3409
|
+
const totals = commands.reduce(
|
|
3410
|
+
(t, c) => ({
|
|
3411
|
+
calls: t.calls + c.calls,
|
|
3412
|
+
complete: t.complete + c.complete,
|
|
3413
|
+
partial: t.partial + c.partial,
|
|
3414
|
+
miss: t.miss + c.miss
|
|
3415
|
+
}),
|
|
3416
|
+
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
3417
|
+
);
|
|
3418
|
+
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
3419
|
+
return {
|
|
3420
|
+
enabled: savingsRecorded(root),
|
|
3421
|
+
days,
|
|
3422
|
+
commands,
|
|
3423
|
+
sources: toDimensionStats(bySource),
|
|
3424
|
+
clients: toDimensionStats(byClient),
|
|
3425
|
+
totals,
|
|
3426
|
+
avgSuccessPct
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3269
3429
|
|
|
3270
3430
|
// src/engine/lookup.ts
|
|
3271
3431
|
function findNodes(graph, query) {
|
|
@@ -3342,7 +3502,7 @@ function shortestPath(graph, srcId, dstId) {
|
|
|
3342
3502
|
var DEPEND_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
|
|
3343
3503
|
function impactOf(graph, rootId, opts = {}) {
|
|
3344
3504
|
const maxDepth = Math.max(1, opts.depth ?? 4);
|
|
3345
|
-
const index =
|
|
3505
|
+
const index = indexFor(graph);
|
|
3346
3506
|
const root = index.node(rootId);
|
|
3347
3507
|
const affected = /* @__PURE__ */ new Map();
|
|
3348
3508
|
let minEdgeConfidence = 1;
|
|
@@ -3389,7 +3549,7 @@ function round3(x) {
|
|
|
3389
3549
|
return Math.round(x * 1e6) / 1e6;
|
|
3390
3550
|
}
|
|
3391
3551
|
function coveringTests(graph, node, index) {
|
|
3392
|
-
const idx = index ??
|
|
3552
|
+
const idx = index ?? indexFor(graph);
|
|
3393
3553
|
const byId = idx.nodeById;
|
|
3394
3554
|
const out = /* @__PURE__ */ new Map();
|
|
3395
3555
|
for (const e of idx.in(node.id, "test")) {
|
|
@@ -3403,7 +3563,7 @@ function coveringTests(graph, node, index) {
|
|
|
3403
3563
|
return [...out.values()].sort((a, b) => b.confidence - a.confidence || a.file.localeCompare(b.file));
|
|
3404
3564
|
}
|
|
3405
3565
|
function testsToRun(graph, rootId, depth = 4) {
|
|
3406
|
-
const idx =
|
|
3566
|
+
const idx = indexFor(graph);
|
|
3407
3567
|
const impact = impactOf(graph, rootId, { depth });
|
|
3408
3568
|
const affectedIds = /* @__PURE__ */ new Set([rootId, ...impact.affected.map((a) => a.id)]);
|
|
3409
3569
|
const testFiles = /* @__PURE__ */ new Set();
|
|
@@ -3423,10 +3583,10 @@ function testsToRun(graph, rootId, depth = 4) {
|
|
|
3423
3583
|
};
|
|
3424
3584
|
}
|
|
3425
3585
|
function detectRunner(root, lang) {
|
|
3426
|
-
const pkgPath =
|
|
3427
|
-
if (
|
|
3586
|
+
const pkgPath = path19.join(root, "package.json");
|
|
3587
|
+
if (fs19.existsSync(pkgPath)) {
|
|
3428
3588
|
try {
|
|
3429
|
-
const pkg = JSON.parse(
|
|
3589
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf8"));
|
|
3430
3590
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3431
3591
|
if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
|
|
3432
3592
|
if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
|
|
@@ -3434,10 +3594,10 @@ function detectRunner(root, lang) {
|
|
|
3434
3594
|
} catch {
|
|
3435
3595
|
}
|
|
3436
3596
|
}
|
|
3437
|
-
if (lang === "py" ||
|
|
3597
|
+
if (lang === "py" || fs19.existsSync(path19.join(root, "pyproject.toml")) || fs19.existsSync(path19.join(root, "pytest.ini"))) {
|
|
3438
3598
|
return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
|
|
3439
3599
|
}
|
|
3440
|
-
if (lang === "go" ||
|
|
3600
|
+
if (lang === "go" || fs19.existsSync(path19.join(root, "go.mod"))) {
|
|
3441
3601
|
return { name: "go test", command: () => "go test ./..." };
|
|
3442
3602
|
}
|
|
3443
3603
|
if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
|
|
@@ -3448,7 +3608,7 @@ function detectRunner(root, lang) {
|
|
|
3448
3608
|
}
|
|
3449
3609
|
function hasFile(root, re) {
|
|
3450
3610
|
try {
|
|
3451
|
-
return
|
|
3611
|
+
return fs19.readdirSync(root).some((f) => re.test(f));
|
|
3452
3612
|
} catch {
|
|
3453
3613
|
return false;
|
|
3454
3614
|
}
|
|
@@ -3520,7 +3680,7 @@ function findManifests(root) {
|
|
|
3520
3680
|
if (depth > 8 || scanned > MAX_ENTRIES) return;
|
|
3521
3681
|
let entries;
|
|
3522
3682
|
try {
|
|
3523
|
-
entries =
|
|
3683
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3524
3684
|
} catch {
|
|
3525
3685
|
return;
|
|
3526
3686
|
}
|
|
@@ -3528,11 +3688,11 @@ function findManifests(root) {
|
|
|
3528
3688
|
scanned++;
|
|
3529
3689
|
if (scanned > MAX_ENTRIES) break;
|
|
3530
3690
|
if (e.isDirectory()) {
|
|
3531
|
-
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(
|
|
3691
|
+
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(path19.join(dir, e.name), depth + 1);
|
|
3532
3692
|
continue;
|
|
3533
3693
|
}
|
|
3534
3694
|
const eco = MANIFEST_BY_FILE[e.name] ?? (/\.(cs|fs)proj$/i.test(e.name) ? "dotnet" : void 0);
|
|
3535
|
-
if (eco) set[eco].push(
|
|
3695
|
+
if (eco) set[eco].push(path19.join(dir, e.name));
|
|
3536
3696
|
}
|
|
3537
3697
|
};
|
|
3538
3698
|
walk2(root, 0);
|
|
@@ -3544,11 +3704,11 @@ function npmDeps(files) {
|
|
|
3544
3704
|
for (const file of files) {
|
|
3545
3705
|
let pkg;
|
|
3546
3706
|
try {
|
|
3547
|
-
pkg = JSON.parse(
|
|
3707
|
+
pkg = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
3548
3708
|
} catch {
|
|
3549
3709
|
continue;
|
|
3550
3710
|
}
|
|
3551
|
-
const dir =
|
|
3711
|
+
const dir = path19.dirname(file);
|
|
3552
3712
|
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3553
3713
|
for (const [name, declared] of Object.entries(all)) {
|
|
3554
3714
|
out.push({ name, ecosystem: "npm", declared, installed: installedNpmVersion(dir, name) });
|
|
@@ -3559,12 +3719,12 @@ function npmDeps(files) {
|
|
|
3559
3719
|
function installedNpmVersion(dir, name) {
|
|
3560
3720
|
let cur = dir;
|
|
3561
3721
|
for (let i = 0; i < 12; i++) {
|
|
3562
|
-
const p =
|
|
3722
|
+
const p = path19.join(cur, "node_modules", name, "package.json");
|
|
3563
3723
|
try {
|
|
3564
|
-
return JSON.parse(
|
|
3724
|
+
return JSON.parse(fs19.readFileSync(p, "utf8")).version;
|
|
3565
3725
|
} catch {
|
|
3566
3726
|
}
|
|
3567
|
-
const parent =
|
|
3727
|
+
const parent = path19.dirname(cur);
|
|
3568
3728
|
if (parent === cur) break;
|
|
3569
3729
|
cur = parent;
|
|
3570
3730
|
}
|
|
@@ -3581,21 +3741,21 @@ function normalizePep503(name) {
|
|
|
3581
3741
|
function sitePackagesDirs(base) {
|
|
3582
3742
|
const out = [];
|
|
3583
3743
|
try {
|
|
3584
|
-
for (const d of
|
|
3585
|
-
if (d.startsWith("python")) out.push(
|
|
3744
|
+
for (const d of fs19.readdirSync(path19.join(base, "lib"))) {
|
|
3745
|
+
if (d.startsWith("python")) out.push(path19.join(base, "lib", d, "site-packages"));
|
|
3586
3746
|
}
|
|
3587
3747
|
} catch {
|
|
3588
3748
|
}
|
|
3589
|
-
out.push(
|
|
3749
|
+
out.push(path19.join(base, "Lib", "site-packages"));
|
|
3590
3750
|
return out;
|
|
3591
3751
|
}
|
|
3592
3752
|
function installedPypiVersion(root, name) {
|
|
3593
3753
|
const target = normalizePep503(name);
|
|
3594
3754
|
for (const venv of [".venv", "venv", "env", ".tox"]) {
|
|
3595
|
-
for (const sp of sitePackagesDirs(
|
|
3755
|
+
for (const sp of sitePackagesDirs(path19.join(root, venv))) {
|
|
3596
3756
|
let entries;
|
|
3597
3757
|
try {
|
|
3598
|
-
entries =
|
|
3758
|
+
entries = fs19.readdirSync(sp);
|
|
3599
3759
|
} catch {
|
|
3600
3760
|
continue;
|
|
3601
3761
|
}
|
|
@@ -3610,7 +3770,7 @@ function installedPypiVersion(root, name) {
|
|
|
3610
3770
|
function installedPhpVersion(root, name) {
|
|
3611
3771
|
let data;
|
|
3612
3772
|
try {
|
|
3613
|
-
data = JSON.parse(
|
|
3773
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "vendor", "composer", "installed.json"), "utf8"));
|
|
3614
3774
|
} catch {
|
|
3615
3775
|
return void 0;
|
|
3616
3776
|
}
|
|
@@ -3629,11 +3789,11 @@ function pypiDeps(files) {
|
|
|
3629
3789
|
for (const file of files) {
|
|
3630
3790
|
let text;
|
|
3631
3791
|
try {
|
|
3632
|
-
text =
|
|
3792
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3633
3793
|
} catch {
|
|
3634
3794
|
continue;
|
|
3635
3795
|
}
|
|
3636
|
-
if (
|
|
3796
|
+
if (path19.basename(file) === "requirements.txt") {
|
|
3637
3797
|
for (const line of text.split("\n")) {
|
|
3638
3798
|
const t = line.trim();
|
|
3639
3799
|
if (!t || t.startsWith("#") || t.startsWith("-")) continue;
|
|
@@ -3717,7 +3877,7 @@ function goDeps(files) {
|
|
|
3717
3877
|
for (const mod of files) {
|
|
3718
3878
|
let text;
|
|
3719
3879
|
try {
|
|
3720
|
-
text =
|
|
3880
|
+
text = fs19.readFileSync(mod, "utf8");
|
|
3721
3881
|
} catch {
|
|
3722
3882
|
continue;
|
|
3723
3883
|
}
|
|
@@ -3735,7 +3895,7 @@ function cargoDeps(files) {
|
|
|
3735
3895
|
for (const file of files) {
|
|
3736
3896
|
let text;
|
|
3737
3897
|
try {
|
|
3738
|
-
text =
|
|
3898
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3739
3899
|
} catch {
|
|
3740
3900
|
continue;
|
|
3741
3901
|
}
|
|
@@ -3767,7 +3927,7 @@ function rubyDeps(files) {
|
|
|
3767
3927
|
for (const file of files) {
|
|
3768
3928
|
let text;
|
|
3769
3929
|
try {
|
|
3770
|
-
text =
|
|
3930
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3771
3931
|
} catch {
|
|
3772
3932
|
continue;
|
|
3773
3933
|
}
|
|
@@ -3782,7 +3942,7 @@ function phpDeps(files) {
|
|
|
3782
3942
|
for (const file of files) {
|
|
3783
3943
|
let pkg;
|
|
3784
3944
|
try {
|
|
3785
|
-
pkg = JSON.parse(
|
|
3945
|
+
pkg = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
3786
3946
|
} catch {
|
|
3787
3947
|
continue;
|
|
3788
3948
|
}
|
|
@@ -3799,7 +3959,7 @@ function dotnetDeps(files) {
|
|
|
3799
3959
|
for (const file of files) {
|
|
3800
3960
|
let text;
|
|
3801
3961
|
try {
|
|
3802
|
-
text =
|
|
3962
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3803
3963
|
} catch {
|
|
3804
3964
|
continue;
|
|
3805
3965
|
}
|
|
@@ -3819,7 +3979,7 @@ function swiftDeps(files) {
|
|
|
3819
3979
|
for (const file of files) {
|
|
3820
3980
|
let text;
|
|
3821
3981
|
try {
|
|
3822
|
-
text =
|
|
3982
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3823
3983
|
} catch {
|
|
3824
3984
|
continue;
|
|
3825
3985
|
}
|
|
@@ -3841,7 +4001,7 @@ function dartDeps(files) {
|
|
|
3841
4001
|
for (const file of files) {
|
|
3842
4002
|
let text;
|
|
3843
4003
|
try {
|
|
3844
|
-
text =
|
|
4004
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3845
4005
|
} catch {
|
|
3846
4006
|
continue;
|
|
3847
4007
|
}
|
|
@@ -3866,11 +4026,11 @@ function javaDeps(files) {
|
|
|
3866
4026
|
for (const file of files) {
|
|
3867
4027
|
let text;
|
|
3868
4028
|
try {
|
|
3869
|
-
text =
|
|
4029
|
+
text = fs19.readFileSync(file, "utf8");
|
|
3870
4030
|
} catch {
|
|
3871
4031
|
continue;
|
|
3872
4032
|
}
|
|
3873
|
-
if (
|
|
4033
|
+
if (path19.basename(file) === "pom.xml") {
|
|
3874
4034
|
for (const block of text.match(/<dependency>[\s\S]*?<\/dependency>/g) ?? []) {
|
|
3875
4035
|
const g = /<groupId>([^<]+)<\/groupId>/.exec(block);
|
|
3876
4036
|
const a = /<artifactId>([^<]+)<\/artifactId>/.exec(block);
|
|
@@ -3916,10 +4076,10 @@ function discoverModels(home = os.homedir()) {
|
|
|
3916
4076
|
);
|
|
3917
4077
|
}
|
|
3918
4078
|
function ollama(home) {
|
|
3919
|
-
const base =
|
|
4079
|
+
const base = path19.join(home, ".ollama", "models", "manifests");
|
|
3920
4080
|
const out = [];
|
|
3921
4081
|
walk(base, 4, (file) => {
|
|
3922
|
-
const rel2 =
|
|
4082
|
+
const rel2 = path19.relative(base, file).split(path19.sep);
|
|
3923
4083
|
if (rel2.length >= 2) {
|
|
3924
4084
|
const tag = rel2[rel2.length - 1];
|
|
3925
4085
|
const model = rel2[rel2.length - 2];
|
|
@@ -3929,24 +4089,24 @@ function ollama(home) {
|
|
|
3929
4089
|
return dedupe(out);
|
|
3930
4090
|
}
|
|
3931
4091
|
function lmStudio(home) {
|
|
3932
|
-
const bases = [
|
|
4092
|
+
const bases = [path19.join(home, ".lmstudio", "models"), path19.join(home, ".cache", "lm-studio", "models")];
|
|
3933
4093
|
const out = [];
|
|
3934
4094
|
for (const base of bases) {
|
|
3935
4095
|
walk(base, 5, (file) => {
|
|
3936
4096
|
if (!file.endsWith(".gguf")) return;
|
|
3937
|
-
const rel2 =
|
|
4097
|
+
const rel2 = path19.relative(base, file).replace(/\\/g, "/");
|
|
3938
4098
|
out.push({ runtime: "lm-studio", name: rel2, path: file });
|
|
3939
4099
|
});
|
|
3940
4100
|
}
|
|
3941
4101
|
return dedupe(out);
|
|
3942
4102
|
}
|
|
3943
4103
|
function looseGguf(home) {
|
|
3944
|
-
const bases = [
|
|
4104
|
+
const bases = [path19.join(home, "models"), path19.join(home, ".cache", "huggingface")];
|
|
3945
4105
|
const out = [];
|
|
3946
4106
|
for (const base of bases) {
|
|
3947
4107
|
walk(base, 4, (file) => {
|
|
3948
4108
|
if (!file.endsWith(".gguf")) return;
|
|
3949
|
-
out.push({ runtime: "gguf", name:
|
|
4109
|
+
out.push({ runtime: "gguf", name: path19.basename(file), path: file });
|
|
3950
4110
|
});
|
|
3951
4111
|
}
|
|
3952
4112
|
return dedupe(out);
|
|
@@ -3955,12 +4115,12 @@ function walk(dir, depth, onFile) {
|
|
|
3955
4115
|
if (depth < 0) return;
|
|
3956
4116
|
let entries;
|
|
3957
4117
|
try {
|
|
3958
|
-
entries =
|
|
4118
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3959
4119
|
} catch {
|
|
3960
4120
|
return;
|
|
3961
4121
|
}
|
|
3962
4122
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3963
|
-
const abs =
|
|
4123
|
+
const abs = path19.join(dir, e.name);
|
|
3964
4124
|
if (e.isDirectory()) walk(abs, depth - 1, onFile);
|
|
3965
4125
|
else if (e.isFile()) onFile(abs);
|
|
3966
4126
|
}
|
|
@@ -3991,7 +4151,7 @@ function lockfileVersion(root, ecosystem, name) {
|
|
|
3991
4151
|
function gradleLock(root, name) {
|
|
3992
4152
|
let text;
|
|
3993
4153
|
try {
|
|
3994
|
-
text =
|
|
4154
|
+
text = fs19.readFileSync(path19.join(root, "gradle.lockfile"), "utf8");
|
|
3995
4155
|
} catch {
|
|
3996
4156
|
return void 0;
|
|
3997
4157
|
}
|
|
@@ -4001,7 +4161,7 @@ function gradleLock(root, name) {
|
|
|
4001
4161
|
function packageResolved(root, name) {
|
|
4002
4162
|
let data;
|
|
4003
4163
|
try {
|
|
4004
|
-
data = JSON.parse(
|
|
4164
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "Package.resolved"), "utf8"));
|
|
4005
4165
|
} catch {
|
|
4006
4166
|
return void 0;
|
|
4007
4167
|
}
|
|
@@ -4017,7 +4177,7 @@ function packageResolved(root, name) {
|
|
|
4017
4177
|
function pubspecLock(root, name) {
|
|
4018
4178
|
let text;
|
|
4019
4179
|
try {
|
|
4020
|
-
text =
|
|
4180
|
+
text = fs19.readFileSync(path19.join(root, "pubspec.lock"), "utf8");
|
|
4021
4181
|
} catch {
|
|
4022
4182
|
return void 0;
|
|
4023
4183
|
}
|
|
@@ -4041,7 +4201,7 @@ function escapeRegExp(s) {
|
|
|
4041
4201
|
function gemfileLock(root, name) {
|
|
4042
4202
|
let text;
|
|
4043
4203
|
try {
|
|
4044
|
-
text =
|
|
4204
|
+
text = fs19.readFileSync(path19.join(root, "Gemfile.lock"), "utf8");
|
|
4045
4205
|
} catch {
|
|
4046
4206
|
return void 0;
|
|
4047
4207
|
}
|
|
@@ -4051,7 +4211,7 @@ function gemfileLock(root, name) {
|
|
|
4051
4211
|
function composerLock(root, name) {
|
|
4052
4212
|
let data;
|
|
4053
4213
|
try {
|
|
4054
|
-
data = JSON.parse(
|
|
4214
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "composer.lock"), "utf8"));
|
|
4055
4215
|
} catch {
|
|
4056
4216
|
return void 0;
|
|
4057
4217
|
}
|
|
@@ -4066,7 +4226,7 @@ function composerLock(root, name) {
|
|
|
4066
4226
|
function packagesLock(root, name) {
|
|
4067
4227
|
let data;
|
|
4068
4228
|
try {
|
|
4069
|
-
data = JSON.parse(
|
|
4229
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "packages.lock.json"), "utf8"));
|
|
4070
4230
|
} catch {
|
|
4071
4231
|
return void 0;
|
|
4072
4232
|
}
|
|
@@ -4091,7 +4251,7 @@ function pypiLockVersion(root, name) {
|
|
|
4091
4251
|
function tomlPackageLock(root, file, name, normalize3) {
|
|
4092
4252
|
let text;
|
|
4093
4253
|
try {
|
|
4094
|
-
text =
|
|
4254
|
+
text = fs19.readFileSync(path19.join(root, file), "utf8");
|
|
4095
4255
|
} catch {
|
|
4096
4256
|
return void 0;
|
|
4097
4257
|
}
|
|
@@ -4108,7 +4268,7 @@ function tomlPackageLock(root, file, name, normalize3) {
|
|
|
4108
4268
|
function pipfileLock(root, name) {
|
|
4109
4269
|
let data;
|
|
4110
4270
|
try {
|
|
4111
|
-
data = JSON.parse(
|
|
4271
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "Pipfile.lock"), "utf8"));
|
|
4112
4272
|
} catch {
|
|
4113
4273
|
return void 0;
|
|
4114
4274
|
}
|
|
@@ -4127,7 +4287,7 @@ function pipfileLock(root, name) {
|
|
|
4127
4287
|
function packageLockVersion(root, name) {
|
|
4128
4288
|
let data;
|
|
4129
4289
|
try {
|
|
4130
|
-
data = JSON.parse(
|
|
4290
|
+
data = JSON.parse(fs19.readFileSync(path19.join(root, "package-lock.json"), "utf8"));
|
|
4131
4291
|
} catch {
|
|
4132
4292
|
return void 0;
|
|
4133
4293
|
}
|
|
@@ -4139,7 +4299,7 @@ function packageLockVersion(root, name) {
|
|
|
4139
4299
|
function yarnLockVersion(root, name) {
|
|
4140
4300
|
let text;
|
|
4141
4301
|
try {
|
|
4142
|
-
text =
|
|
4302
|
+
text = fs19.readFileSync(path19.join(root, "yarn.lock"), "utf8");
|
|
4143
4303
|
} catch {
|
|
4144
4304
|
return void 0;
|
|
4145
4305
|
}
|
|
@@ -4167,19 +4327,19 @@ function headerNamesPackage(header, name) {
|
|
|
4167
4327
|
// src/engine/lib.ts
|
|
4168
4328
|
var LIB_SCHEMA = "vg-lib/1.0";
|
|
4169
4329
|
function catalogPath(root) {
|
|
4170
|
-
return
|
|
4330
|
+
return path19.join(root, "vibgrate.lib.json");
|
|
4171
4331
|
}
|
|
4172
4332
|
function libDir(root) {
|
|
4173
|
-
return
|
|
4333
|
+
return path19.join(root, ".vibgrate", "lib");
|
|
4174
4334
|
}
|
|
4175
4335
|
function libId(name) {
|
|
4176
4336
|
return name.trim().toLowerCase().replace(/^@/, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
4177
4337
|
}
|
|
4178
4338
|
function loadCatalog(root) {
|
|
4179
4339
|
const file = catalogPath(root);
|
|
4180
|
-
if (
|
|
4340
|
+
if (fs19.existsSync(file)) {
|
|
4181
4341
|
try {
|
|
4182
|
-
const data = JSON.parse(
|
|
4342
|
+
const data = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
4183
4343
|
if (data.schemaVersion === LIB_SCHEMA && data.libraries) return data;
|
|
4184
4344
|
} catch {
|
|
4185
4345
|
}
|
|
@@ -4187,7 +4347,7 @@ function loadCatalog(root) {
|
|
|
4187
4347
|
return { schemaVersion: LIB_SCHEMA, libraries: {} };
|
|
4188
4348
|
}
|
|
4189
4349
|
function saveCatalog(root, catalog) {
|
|
4190
|
-
|
|
4350
|
+
fs19.writeFileSync(catalogPath(root), `${stableStringify(catalog, 2)}
|
|
4191
4351
|
`);
|
|
4192
4352
|
}
|
|
4193
4353
|
function resolveLib(catalog, name) {
|
|
@@ -4263,23 +4423,23 @@ async function addLibrary(source, opts) {
|
|
|
4263
4423
|
content = await res.text();
|
|
4264
4424
|
type = source.endsWith("llms.txt") ? "llms.txt" : source.endsWith(".json") ? "openapi" : "website";
|
|
4265
4425
|
} else {
|
|
4266
|
-
const abs =
|
|
4267
|
-
if (!
|
|
4426
|
+
const abs = path19.resolve(root, source);
|
|
4427
|
+
if (!fs19.existsSync(abs)) throw new Error(`source not found: ${source}`);
|
|
4268
4428
|
content = readLocal(abs);
|
|
4269
4429
|
type = "local";
|
|
4270
4430
|
}
|
|
4271
4431
|
const name = opts.name ?? inferName(source);
|
|
4272
4432
|
const id = libId(name);
|
|
4273
4433
|
const version = opts.version ?? installedVersion(root, name) ?? "*";
|
|
4274
|
-
|
|
4275
|
-
const docFileAbs =
|
|
4276
|
-
|
|
4434
|
+
fs19.mkdirSync(libDir(root), { recursive: true });
|
|
4435
|
+
const docFileAbs = path19.join(libDir(root), `${id}.md`);
|
|
4436
|
+
fs19.writeFileSync(docFileAbs, content);
|
|
4277
4437
|
const entry = {
|
|
4278
4438
|
id,
|
|
4279
4439
|
name,
|
|
4280
4440
|
version,
|
|
4281
4441
|
source: { type, location: source },
|
|
4282
|
-
docFile:
|
|
4442
|
+
docFile: path19.relative(root, docFileAbs).split(path19.sep).join("/"),
|
|
4283
4443
|
docHash: hashString(content),
|
|
4284
4444
|
bytes: Buffer.byteLength(content, "utf8")
|
|
4285
4445
|
};
|
|
@@ -4289,15 +4449,15 @@ async function addLibrary(source, opts) {
|
|
|
4289
4449
|
return entry;
|
|
4290
4450
|
}
|
|
4291
4451
|
function readDoc(root, entry) {
|
|
4292
|
-
const abs =
|
|
4293
|
-
return
|
|
4452
|
+
const abs = path19.resolve(root, entry.docFile);
|
|
4453
|
+
return fs19.existsSync(abs) ? fs19.readFileSync(abs, "utf8") : "";
|
|
4294
4454
|
}
|
|
4295
4455
|
function npmPackageDir(root, name) {
|
|
4296
4456
|
let cur = root;
|
|
4297
4457
|
for (let i = 0; i < 12; i++) {
|
|
4298
|
-
const dir =
|
|
4299
|
-
if (
|
|
4300
|
-
const parent =
|
|
4458
|
+
const dir = path19.join(cur, "node_modules", name);
|
|
4459
|
+
if (fs19.existsSync(path19.join(dir, "package.json"))) return dir;
|
|
4460
|
+
const parent = path19.dirname(cur);
|
|
4301
4461
|
if (parent === cur) break;
|
|
4302
4462
|
cur = parent;
|
|
4303
4463
|
}
|
|
@@ -4308,24 +4468,24 @@ function localPackageDocs(root, name) {
|
|
|
4308
4468
|
if (!dir) return void 0;
|
|
4309
4469
|
const version = resolveVersion(root, name).served;
|
|
4310
4470
|
for (const f of ["llms.txt", "README.md", "README.mdx", "README", "readme.md"]) {
|
|
4311
|
-
const p =
|
|
4471
|
+
const p = path19.join(dir, f);
|
|
4312
4472
|
try {
|
|
4313
|
-
const docs =
|
|
4473
|
+
const docs = fs19.readFileSync(p, "utf8");
|
|
4314
4474
|
if (docs.trim()) {
|
|
4315
|
-
return { docs, version, source:
|
|
4475
|
+
return { docs, version, source: path19.relative(root, p).split(path19.sep).join("/") };
|
|
4316
4476
|
}
|
|
4317
4477
|
} catch {
|
|
4318
4478
|
}
|
|
4319
4479
|
}
|
|
4320
4480
|
try {
|
|
4321
|
-
const pkg = JSON.parse(
|
|
4481
|
+
const pkg = JSON.parse(fs19.readFileSync(path19.join(dir, "package.json"), "utf8"));
|
|
4322
4482
|
if (pkg.description?.trim()) {
|
|
4323
4483
|
return {
|
|
4324
4484
|
docs: `# ${name}
|
|
4325
4485
|
|
|
4326
4486
|
${pkg.description}`,
|
|
4327
4487
|
version: version ?? pkg.version,
|
|
4328
|
-
source:
|
|
4488
|
+
source: path19.relative(root, path19.join(dir, "package.json")).split(path19.sep).join("/")
|
|
4329
4489
|
};
|
|
4330
4490
|
}
|
|
4331
4491
|
} catch {
|
|
@@ -4334,17 +4494,17 @@ ${pkg.description}`,
|
|
|
4334
4494
|
}
|
|
4335
4495
|
function dtsEntry(dir) {
|
|
4336
4496
|
try {
|
|
4337
|
-
const pkg = JSON.parse(
|
|
4497
|
+
const pkg = JSON.parse(fs19.readFileSync(path19.join(dir, "package.json"), "utf8"));
|
|
4338
4498
|
const rel2 = pkg.types ?? pkg.typings;
|
|
4339
4499
|
if (typeof rel2 === "string") {
|
|
4340
|
-
const p =
|
|
4341
|
-
if (
|
|
4500
|
+
const p = path19.join(dir, rel2);
|
|
4501
|
+
if (fs19.existsSync(p)) return p;
|
|
4342
4502
|
}
|
|
4343
4503
|
} catch {
|
|
4344
4504
|
}
|
|
4345
4505
|
for (const c of ["index.d.ts", "dist/index.d.ts", "lib/index.d.ts", "types/index.d.ts"]) {
|
|
4346
|
-
const p =
|
|
4347
|
-
if (
|
|
4506
|
+
const p = path19.join(dir, c);
|
|
4507
|
+
if (fs19.existsSync(p)) return p;
|
|
4348
4508
|
}
|
|
4349
4509
|
return void 0;
|
|
4350
4510
|
}
|
|
@@ -4377,7 +4537,7 @@ function localApiSurface(root, name) {
|
|
|
4377
4537
|
if (!dts) return void 0;
|
|
4378
4538
|
let src;
|
|
4379
4539
|
try {
|
|
4380
|
-
src =
|
|
4540
|
+
src = fs19.readFileSync(dts, "utf8");
|
|
4381
4541
|
} catch {
|
|
4382
4542
|
return void 0;
|
|
4383
4543
|
}
|
|
@@ -4385,36 +4545,36 @@ function localApiSurface(root, name) {
|
|
|
4385
4545
|
return decls.length ? decls.join("\n\n") : void 0;
|
|
4386
4546
|
}
|
|
4387
4547
|
function cloneAndReadGit(url) {
|
|
4388
|
-
const dir =
|
|
4548
|
+
const dir = fs19.mkdtempSync(path19.join(os.tmpdir(), "vg-lib-git-"));
|
|
4389
4549
|
try {
|
|
4390
4550
|
execFileSync("git", ["clone", "--depth", "1", "--quiet", url, dir], {
|
|
4391
4551
|
stdio: ["ignore", "ignore", "pipe"]
|
|
4392
4552
|
});
|
|
4393
4553
|
} catch (err) {
|
|
4394
|
-
|
|
4554
|
+
fs19.rmSync(dir, { recursive: true, force: true });
|
|
4395
4555
|
const detail = err instanceof Error && err.message ? `: ${err.message.split("\n")[0]}` : "";
|
|
4396
4556
|
throw new Error(`git clone failed for ${url}${detail}`);
|
|
4397
4557
|
}
|
|
4398
4558
|
try {
|
|
4399
4559
|
return readGitDocs(dir);
|
|
4400
4560
|
} finally {
|
|
4401
|
-
|
|
4561
|
+
fs19.rmSync(dir, { recursive: true, force: true });
|
|
4402
4562
|
}
|
|
4403
4563
|
}
|
|
4404
4564
|
function readGitDocs(dir) {
|
|
4405
|
-
const docsDir =
|
|
4406
|
-
if (
|
|
4407
|
-
const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) =>
|
|
4408
|
-
if (readme) return
|
|
4565
|
+
const docsDir = path19.join(dir, "docs");
|
|
4566
|
+
if (fs19.existsSync(docsDir) && fs19.statSync(docsDir).isDirectory()) return readLocal(docsDir);
|
|
4567
|
+
const readme = ["README.md", "README.mdx", "README.txt", "README.rst", "readme.md"].map((f) => path19.join(dir, f)).find((p) => fs19.existsSync(p));
|
|
4568
|
+
if (readme) return fs19.readFileSync(readme, "utf8");
|
|
4409
4569
|
return readLocal(dir);
|
|
4410
4570
|
}
|
|
4411
4571
|
function readLocal(abs) {
|
|
4412
|
-
const stat =
|
|
4413
|
-
if (stat.isFile()) return
|
|
4572
|
+
const stat = fs19.statSync(abs);
|
|
4573
|
+
if (stat.isFile()) return fs19.readFileSync(abs, "utf8");
|
|
4414
4574
|
const parts = [];
|
|
4415
|
-
const files =
|
|
4575
|
+
const files = fs19.readdirSync(abs, { withFileTypes: true }).filter((e) => e.isFile() && /\.(md|mdx|txt|rst)$/i.test(e.name)).map((e) => e.name).sort();
|
|
4416
4576
|
for (const f of files) parts.push(`<!-- ${f} -->
|
|
4417
|
-
${
|
|
4577
|
+
${fs19.readFileSync(path19.join(abs, f), "utf8")}`);
|
|
4418
4578
|
return parts.join("\n\n");
|
|
4419
4579
|
}
|
|
4420
4580
|
function inferName(source) {
|
|
@@ -4423,10 +4583,10 @@ function inferName(source) {
|
|
|
4423
4583
|
return base.replace(/\.(md|mdx|txt|rst|json)$/i, "").replace(/^llms$/, "docs");
|
|
4424
4584
|
}
|
|
4425
4585
|
function findGitRoot(startDir = process.cwd()) {
|
|
4426
|
-
let dir =
|
|
4586
|
+
let dir = path19.resolve(startDir);
|
|
4427
4587
|
for (; ; ) {
|
|
4428
|
-
if (
|
|
4429
|
-
const parent =
|
|
4588
|
+
if (fs19.existsSync(path19.join(dir, ".git"))) return dir;
|
|
4589
|
+
const parent = path19.dirname(dir);
|
|
4430
4590
|
if (parent === dir) return null;
|
|
4431
4591
|
dir = parent;
|
|
4432
4592
|
}
|
|
@@ -4437,19 +4597,19 @@ function normalizeEntry(line) {
|
|
|
4437
4597
|
function ensureGitignored(entry, startDir = process.cwd()) {
|
|
4438
4598
|
const root = findGitRoot(startDir);
|
|
4439
4599
|
if (!root) return { status: "not-a-repo", entry };
|
|
4440
|
-
const gitignorePath =
|
|
4600
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
4441
4601
|
const target = normalizeEntry(entry);
|
|
4442
4602
|
let existing = "";
|
|
4443
4603
|
let fileExisted = true;
|
|
4444
4604
|
try {
|
|
4445
|
-
existing =
|
|
4605
|
+
existing = fs19.readFileSync(gitignorePath, "utf8");
|
|
4446
4606
|
} catch {
|
|
4447
4607
|
fileExisted = false;
|
|
4448
4608
|
}
|
|
4449
4609
|
const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
|
|
4450
4610
|
if (alreadyPresent) return { status: "present", entry, gitignorePath };
|
|
4451
4611
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
4452
|
-
|
|
4612
|
+
fs19.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
|
|
4453
4613
|
`, "utf8");
|
|
4454
4614
|
return { status: fileExisted ? "added" : "created", entry, gitignorePath };
|
|
4455
4615
|
}
|
|
@@ -4603,14 +4763,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
4603
4763
|
console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
4604
4764
|
}
|
|
4605
4765
|
if (opts.write) {
|
|
4606
|
-
const writePath =
|
|
4766
|
+
const writePath = path19.resolve(opts.write);
|
|
4607
4767
|
await writeTextFile(writePath, dsn + "\n");
|
|
4608
4768
|
console.log("");
|
|
4609
4769
|
console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
4610
|
-
const root = findGitRoot(
|
|
4611
|
-
const rel2 = root ?
|
|
4770
|
+
const root = findGitRoot(path19.dirname(writePath));
|
|
4771
|
+
const rel2 = root ? path19.relative(root, writePath).split(path19.sep).join("/") : "";
|
|
4612
4772
|
if (root && rel2 && !rel2.startsWith("..")) {
|
|
4613
|
-
const res = ensureGitignored(rel2,
|
|
4773
|
+
const res = ensureGitignored(rel2, path19.dirname(writePath));
|
|
4614
4774
|
if (res.status === "created") {
|
|
4615
4775
|
console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
|
|
4616
4776
|
} else if (res.status === "added") {
|
|
@@ -4628,30 +4788,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
4628
4788
|
var STORE_DIRNAME = ".vibgrate";
|
|
4629
4789
|
var STORE_FILENAME = "credentials.json";
|
|
4630
4790
|
function homeCredentialsPath() {
|
|
4631
|
-
return
|
|
4791
|
+
return path19.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
|
|
4632
4792
|
}
|
|
4633
4793
|
function projectCredentialsPath(cwd = process.cwd()) {
|
|
4634
4794
|
const root = findGitRoot(cwd) ?? cwd;
|
|
4635
|
-
return
|
|
4795
|
+
return path19.join(root, STORE_DIRNAME, STORE_FILENAME);
|
|
4636
4796
|
}
|
|
4637
4797
|
function credentialsPath(opts = {}) {
|
|
4638
4798
|
const override = process.env.VIBGRATE_CREDENTIALS?.trim();
|
|
4639
|
-
if (override) return
|
|
4799
|
+
if (override) return path19.resolve(override);
|
|
4640
4800
|
const local = projectCredentialsPath(opts.cwd);
|
|
4641
4801
|
if (opts.local) return local;
|
|
4642
|
-
if (
|
|
4802
|
+
if (fs19.existsSync(local)) return local;
|
|
4643
4803
|
return homeCredentialsPath();
|
|
4644
4804
|
}
|
|
4645
4805
|
function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
|
|
4646
|
-
const rel2 =
|
|
4647
|
-
if (rel2 && !rel2.startsWith("..") && !
|
|
4648
|
-
return rel2.split(
|
|
4806
|
+
const rel2 = path19.relative(repoRoot, credsFile);
|
|
4807
|
+
if (rel2 && !rel2.startsWith("..") && !path19.isAbsolute(rel2)) {
|
|
4808
|
+
return rel2.split(path19.sep).join("/");
|
|
4649
4809
|
}
|
|
4650
4810
|
return `${STORE_DIRNAME}/${STORE_FILENAME}`;
|
|
4651
4811
|
}
|
|
4652
4812
|
function readStoredCredentials(opts = {}) {
|
|
4653
4813
|
try {
|
|
4654
|
-
const raw =
|
|
4814
|
+
const raw = fs19.readFileSync(credentialsPath(opts), "utf8");
|
|
4655
4815
|
const parsed = JSON.parse(raw);
|
|
4656
4816
|
return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
|
|
4657
4817
|
} catch {
|
|
@@ -4660,16 +4820,16 @@ function readStoredCredentials(opts = {}) {
|
|
|
4660
4820
|
}
|
|
4661
4821
|
function writeStoredCredentials(creds, opts = {}) {
|
|
4662
4822
|
const file = credentialsPath(opts);
|
|
4663
|
-
|
|
4664
|
-
|
|
4823
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
4824
|
+
fs19.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
4665
4825
|
try {
|
|
4666
|
-
|
|
4826
|
+
fs19.chmodSync(file, 384);
|
|
4667
4827
|
} catch {
|
|
4668
4828
|
}
|
|
4669
4829
|
}
|
|
4670
4830
|
function clearStoredCredentials(opts = {}) {
|
|
4671
4831
|
try {
|
|
4672
|
-
|
|
4832
|
+
fs19.rmSync(credentialsPath(opts));
|
|
4673
4833
|
return true;
|
|
4674
4834
|
} catch {
|
|
4675
4835
|
return false;
|
|
@@ -5000,7 +5160,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
5000
5160
|
if (opts.strict) process.exit(1);
|
|
5001
5161
|
return;
|
|
5002
5162
|
}
|
|
5003
|
-
const filePath =
|
|
5163
|
+
const filePath = path19.resolve(opts.file);
|
|
5004
5164
|
if (!await pathExists(filePath)) {
|
|
5005
5165
|
console.error(chalk.red(`Scan artifact not found: ${filePath}`));
|
|
5006
5166
|
console.error(chalk.dim('Run "vibgrate scan" first.'));
|
|
@@ -5057,8 +5217,8 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
5057
5217
|
});
|
|
5058
5218
|
function readScanArtifact(root) {
|
|
5059
5219
|
try {
|
|
5060
|
-
const file =
|
|
5061
|
-
return JSON.parse(
|
|
5220
|
+
const file = path19.join(root, ".vibgrate", "scan_result.json");
|
|
5221
|
+
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
5062
5222
|
} catch {
|
|
5063
5223
|
return null;
|
|
5064
5224
|
}
|
|
@@ -5570,14 +5730,14 @@ function scanFiles(root, needle, budget, seen, out) {
|
|
|
5570
5730
|
const dir = stack.pop();
|
|
5571
5731
|
let entries;
|
|
5572
5732
|
try {
|
|
5573
|
-
entries =
|
|
5733
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
5574
5734
|
} catch {
|
|
5575
5735
|
continue;
|
|
5576
5736
|
}
|
|
5577
5737
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
5578
5738
|
for (const e of entries) {
|
|
5579
5739
|
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
5580
|
-
const abs =
|
|
5740
|
+
const abs = path19.join(dir, e.name);
|
|
5581
5741
|
if (e.isDirectory()) {
|
|
5582
5742
|
stack.push(abs);
|
|
5583
5743
|
continue;
|
|
@@ -5585,13 +5745,13 @@ function scanFiles(root, needle, budget, seen, out) {
|
|
|
5585
5745
|
if (++scanned > MAX_FILES_SCANNED) return true;
|
|
5586
5746
|
let text;
|
|
5587
5747
|
try {
|
|
5588
|
-
if (
|
|
5589
|
-
text =
|
|
5748
|
+
if (fs19.statSync(abs).size > MAX_FILE_BYTES) continue;
|
|
5749
|
+
text = fs19.readFileSync(abs, "utf8");
|
|
5590
5750
|
} catch {
|
|
5591
5751
|
continue;
|
|
5592
5752
|
}
|
|
5593
5753
|
if (!text.toLowerCase().includes(lower)) continue;
|
|
5594
|
-
const rel2 =
|
|
5754
|
+
const rel2 = path19.relative(root, abs);
|
|
5595
5755
|
const lines = text.split("\n");
|
|
5596
5756
|
for (let i = 0; i < lines.length; i++) {
|
|
5597
5757
|
if (!lines[i].toLowerCase().includes(lower)) continue;
|
|
@@ -5787,7 +5947,7 @@ var TOOLS = [
|
|
|
5787
5947
|
handler: (graph, args, ctx) => {
|
|
5788
5948
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5789
5949
|
if (!node) return unresolved(candidates);
|
|
5790
|
-
const index =
|
|
5950
|
+
const index = indexFor(graph);
|
|
5791
5951
|
const edgeOk = edgeRecordFilter(args);
|
|
5792
5952
|
const calleeRecs = index.callees(node.id).filter(edgeOk);
|
|
5793
5953
|
const callerRecs = index.callers(node.id).filter(edgeOk);
|
|
@@ -6273,104 +6433,6 @@ function navigationToolsetConfig(serverName = "vibgrate") {
|
|
|
6273
6433
|
configs: Object.fromEntries(HOT_TOOLS.map((n) => [n, { defer_loading: false }]))
|
|
6274
6434
|
};
|
|
6275
6435
|
}
|
|
6276
|
-
var LEDGER = "savings.jsonl";
|
|
6277
|
-
var PER_FILE_TOKENS = 400;
|
|
6278
|
-
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node"]);
|
|
6279
|
-
function ledgerPath(root) {
|
|
6280
|
-
return path18.join(cacheDir(root), LEDGER);
|
|
6281
|
-
}
|
|
6282
|
-
function savingsRecorded(root) {
|
|
6283
|
-
return fs18.existsSync(ledgerPath(root));
|
|
6284
|
-
}
|
|
6285
|
-
function recordSaving(root, entry, now) {
|
|
6286
|
-
try {
|
|
6287
|
-
fs18.mkdirSync(cacheDir(root), { recursive: true });
|
|
6288
|
-
const line = JSON.stringify({ ts: now, ...entry });
|
|
6289
|
-
fs18.appendFileSync(ledgerPath(root), line + "\n");
|
|
6290
|
-
} catch {
|
|
6291
|
-
}
|
|
6292
|
-
}
|
|
6293
|
-
var DEFAULT_RATE_PER_M = 3;
|
|
6294
|
-
var DEFAULT_RATE_LABEL = "input @ $3/1M";
|
|
6295
|
-
function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
6296
|
-
const file = ledgerPath(root);
|
|
6297
|
-
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
6298
|
-
let queries = 0;
|
|
6299
|
-
let vgTokens = 0;
|
|
6300
|
-
let baselineTokens = 0;
|
|
6301
|
-
if (fs18.existsSync(file)) {
|
|
6302
|
-
for (const line of fs18.readFileSync(file, "utf8").split("\n")) {
|
|
6303
|
-
if (!line.trim()) continue;
|
|
6304
|
-
try {
|
|
6305
|
-
const e = JSON.parse(line);
|
|
6306
|
-
if (e.ts < cutoff) continue;
|
|
6307
|
-
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
6308
|
-
queries++;
|
|
6309
|
-
vgTokens += e.vgTokens;
|
|
6310
|
-
baselineTokens += e.baselineTokens;
|
|
6311
|
-
} catch {
|
|
6312
|
-
}
|
|
6313
|
-
}
|
|
6314
|
-
}
|
|
6315
|
-
const estCostVg = vgTokens / 1e6 * ratePerM;
|
|
6316
|
-
const estCostBaseline = baselineTokens / 1e6 * ratePerM;
|
|
6317
|
-
return {
|
|
6318
|
-
enabled: savingsRecorded(root),
|
|
6319
|
-
days,
|
|
6320
|
-
queries,
|
|
6321
|
-
vgTokens,
|
|
6322
|
-
baselineTokens,
|
|
6323
|
-
ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
|
|
6324
|
-
estCostVg: round22(estCostVg),
|
|
6325
|
-
estCostBaseline: round22(estCostBaseline),
|
|
6326
|
-
saved: round22(estCostBaseline - estCostVg),
|
|
6327
|
-
rateLabel: DEFAULT_RATE_LABEL
|
|
6328
|
-
};
|
|
6329
|
-
}
|
|
6330
|
-
function round22(x) {
|
|
6331
|
-
return Math.round(x * 100) / 100;
|
|
6332
|
-
}
|
|
6333
|
-
function readUsage(root, days, now) {
|
|
6334
|
-
const file = ledgerPath(root);
|
|
6335
|
-
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
6336
|
-
const byTool = /* @__PURE__ */ new Map();
|
|
6337
|
-
if (fs18.existsSync(file)) {
|
|
6338
|
-
for (const line of fs18.readFileSync(file, "utf8").split("\n")) {
|
|
6339
|
-
if (!line.trim()) continue;
|
|
6340
|
-
try {
|
|
6341
|
-
const e = JSON.parse(line);
|
|
6342
|
-
if (e.ts < cutoff) continue;
|
|
6343
|
-
const outcome = e.outcome ?? "complete";
|
|
6344
|
-
const row = byTool.get(e.tool) ?? { complete: 0, partial: 0, miss: 0 };
|
|
6345
|
-
row[outcome]++;
|
|
6346
|
-
byTool.set(e.tool, row);
|
|
6347
|
-
} catch {
|
|
6348
|
-
}
|
|
6349
|
-
}
|
|
6350
|
-
}
|
|
6351
|
-
const commands = [...byTool.entries()].map(([tool, r]) => {
|
|
6352
|
-
const calls = r.complete + r.partial + r.miss;
|
|
6353
|
-
return {
|
|
6354
|
-
tool,
|
|
6355
|
-
calls,
|
|
6356
|
-
complete: r.complete,
|
|
6357
|
-
partial: r.partial,
|
|
6358
|
-
miss: r.miss,
|
|
6359
|
-
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
6360
|
-
};
|
|
6361
|
-
}).sort((a, b) => b.calls - a.calls || a.tool.localeCompare(b.tool));
|
|
6362
|
-
const totals = commands.reduce(
|
|
6363
|
-
(t, c) => ({
|
|
6364
|
-
calls: t.calls + c.calls,
|
|
6365
|
-
complete: t.complete + c.complete,
|
|
6366
|
-
partial: t.partial + c.partial,
|
|
6367
|
-
miss: t.miss + c.miss
|
|
6368
|
-
}),
|
|
6369
|
-
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
6370
|
-
);
|
|
6371
|
-
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
6372
|
-
return { enabled: savingsRecorded(root), days, commands, totals, avgSuccessPct };
|
|
6373
|
-
}
|
|
6374
6436
|
var PROBE_INTERVAL_MS = 2e3;
|
|
6375
6437
|
var MAX_PROBE_INTERVAL_MS = 3e4;
|
|
6376
6438
|
var PROBE_DUTY_FACTOR = 20;
|
|
@@ -6381,7 +6443,7 @@ var GraphSource = class {
|
|
|
6381
6443
|
this.graphPath = graphPath;
|
|
6382
6444
|
this.refresh = refresh;
|
|
6383
6445
|
this.tuning = tuning;
|
|
6384
|
-
this.root =
|
|
6446
|
+
this.root = path19.dirname(path19.dirname(graphPath));
|
|
6385
6447
|
}
|
|
6386
6448
|
graphPath;
|
|
6387
6449
|
refresh;
|
|
@@ -6397,9 +6459,9 @@ var GraphSource = class {
|
|
|
6397
6459
|
/** Current graph: auto-refreshed if the tree drifted, reloaded if the file changed. */
|
|
6398
6460
|
async get() {
|
|
6399
6461
|
if (this.refresh) await this.maybeRefresh();
|
|
6400
|
-
const stat =
|
|
6462
|
+
const stat = fs19.statSync(this.graphPath);
|
|
6401
6463
|
if (stat.mtimeMs !== this.cachedMtimeMs || !this.cached) {
|
|
6402
|
-
this.cached = parseGraph(
|
|
6464
|
+
this.cached = parseGraph(fs19.readFileSync(this.graphPath, "utf8"));
|
|
6403
6465
|
this.cachedMtimeMs = stat.mtimeMs;
|
|
6404
6466
|
}
|
|
6405
6467
|
return this.cached;
|
|
@@ -6433,8 +6495,9 @@ var GraphSource = class {
|
|
|
6433
6495
|
}
|
|
6434
6496
|
};
|
|
6435
6497
|
function createServer(source, opts = {}) {
|
|
6436
|
-
const { savings = false, local = false, dedup = false } = opts;
|
|
6437
|
-
const
|
|
6498
|
+
const { savings = false, shareStats = false, local = false, dedup = false } = opts;
|
|
6499
|
+
const record = savings || shareStats;
|
|
6500
|
+
const root = path19.dirname(path19.dirname(source.graphPath));
|
|
6438
6501
|
const seen = /* @__PURE__ */ new Set();
|
|
6439
6502
|
const server = new Server(
|
|
6440
6503
|
{ name: "vg", version: VERSION },
|
|
@@ -6470,7 +6533,7 @@ function createServer(source, opts = {}) {
|
|
|
6470
6533
|
try {
|
|
6471
6534
|
const args = request.params.arguments ?? {};
|
|
6472
6535
|
const result = await tool.handler(graph, args, { root, local, dedup, seen });
|
|
6473
|
-
if (
|
|
6536
|
+
if (record) recordUsage(root, tool.name, result, detectClient(server));
|
|
6474
6537
|
return renderToolResult(result);
|
|
6475
6538
|
} catch (err) {
|
|
6476
6539
|
return errorResult(`tool "${tool.name}" failed: ${err.message}`);
|
|
@@ -6490,7 +6553,7 @@ function sleep(ms) {
|
|
|
6490
6553
|
timer.unref?.();
|
|
6491
6554
|
});
|
|
6492
6555
|
}
|
|
6493
|
-
function recordUsage(root, tool, result) {
|
|
6556
|
+
function recordUsage(root, tool, result, client) {
|
|
6494
6557
|
const outcome = classifyOutcome(result);
|
|
6495
6558
|
let vgTokens = 0;
|
|
6496
6559
|
let baselineTokens = 0;
|
|
@@ -6498,7 +6561,19 @@ function recordUsage(root, tool, result) {
|
|
|
6498
6561
|
vgTokens = countTokens(renderedText(renderToolResult(result)));
|
|
6499
6562
|
baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
|
|
6500
6563
|
}
|
|
6501
|
-
recordSaving(
|
|
6564
|
+
recordSaving(
|
|
6565
|
+
root,
|
|
6566
|
+
{ tool, outcome, vgTokens, baselineTokens, source: "mcp", client: sanitizeClient(client) },
|
|
6567
|
+
Date.now()
|
|
6568
|
+
);
|
|
6569
|
+
}
|
|
6570
|
+
function detectClient(server) {
|
|
6571
|
+
try {
|
|
6572
|
+
const info = server.getClientVersion?.();
|
|
6573
|
+
return typeof info?.name === "string" ? info.name : void 0;
|
|
6574
|
+
} catch {
|
|
6575
|
+
return void 0;
|
|
6576
|
+
}
|
|
6502
6577
|
}
|
|
6503
6578
|
function classifyOutcome(result) {
|
|
6504
6579
|
if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
|
|
@@ -6549,7 +6624,11 @@ function errorResult(message) {
|
|
|
6549
6624
|
// src/install/content.ts
|
|
6550
6625
|
var NUDGE_BEGIN = "<!-- vg:begin -->";
|
|
6551
6626
|
var NUDGE_END = "<!-- vg:end -->";
|
|
6552
|
-
function
|
|
6627
|
+
function clientFlag(client) {
|
|
6628
|
+
return client ? `--client=${client}` : "--client=<your-ai>";
|
|
6629
|
+
}
|
|
6630
|
+
function skillMarkdown(client) {
|
|
6631
|
+
const cf = clientFlag(client);
|
|
6553
6632
|
return `---
|
|
6554
6633
|
name: vg
|
|
6555
6634
|
description: Query the local code graph (vg) for structure, impact, and navigation instead of grepping/reading many files.
|
|
@@ -6560,13 +6639,26 @@ description: Query the local code graph (vg) for structure, impact, and navigati
|
|
|
6560
6639
|
This repo has a deterministic code graph built by \`vg\`. Prefer it over reading or
|
|
6561
6640
|
grepping many files \u2014 it is smaller, more relevant context, and free.
|
|
6562
6641
|
|
|
6563
|
-
##
|
|
6642
|
+
## Prefer the MCP tools
|
|
6643
|
+
|
|
6644
|
+
If the \`vg\` MCP server is registered (it is after \`vg install\`), call its
|
|
6645
|
+
read-only tools directly \u2014 they are the **fastest** path. The server keeps the
|
|
6646
|
+
map parsed, the relation index warm, and the embedding model loaded across calls,
|
|
6647
|
+
so each query is cheaper than spawning the CLI fresh. Use:
|
|
6648
|
+
\`query_graph\`, \`get_node\`, \`impact_of\`, \`find_path\`, \`list_hubs\`, \`list_areas\`,
|
|
6649
|
+
\`get_graph_summary\`, \`search_symbols\`. They are side-effect-free and
|
|
6650
|
+
auto-approvable, and the server records which client is calling automatically.
|
|
6651
|
+
|
|
6652
|
+
## If you use the \`vg\` CLI instead
|
|
6653
|
+
|
|
6654
|
+
When the MCP server isn't available, use the CLI \u2014 and **always pass \`${cf}\`**
|
|
6655
|
+
so your calls are counted (that's how the CLI-vs-MCP split is measured and the
|
|
6656
|
+
tools improved):
|
|
6564
6657
|
|
|
6565
|
-
- **Understand code:** \`vg "<question>"\`
|
|
6566
|
-
|
|
6567
|
-
- **
|
|
6568
|
-
- **
|
|
6569
|
-
- **Navigate:** \`vg path <A> <B>\`, \`vg tree <name>\`, \`vg hubs\`, \`vg areas\`.
|
|
6658
|
+
- **Understand code:** \`vg "<question>" ${cf}\` \u2014 a budget-bounded, fact-annotated context block.
|
|
6659
|
+
- **Find a symbol:** \`vg show <name> ${cf}\` \u2014 what it is, what it calls, what calls it.
|
|
6660
|
+
- **Before changing something:** \`vg impact <name> ${cf}\` \u2014 what breaks if you change it.
|
|
6661
|
+
- **Navigate:** \`vg path <A> <B> ${cf}\`, \`vg tree <name> ${cf}\`.
|
|
6570
6662
|
|
|
6571
6663
|
## Dependencies & library docs
|
|
6572
6664
|
|
|
@@ -6576,12 +6668,6 @@ grepping many files \u2014 it is smaller, more relevant context, and free.
|
|
|
6576
6668
|
- **Version-correct docs:** \`vg lib <name>\` returns drift-annotated, version-
|
|
6577
6669
|
specific usage docs for a library \u2014 inject these instead of guessing an API.
|
|
6578
6670
|
|
|
6579
|
-
## Via MCP
|
|
6580
|
-
|
|
6581
|
-
If the \`vg\` MCP server is registered, call its read-only tools directly:
|
|
6582
|
-
\`query_graph\`, \`get_node\`, \`impact_of\`, \`find_path\`, \`list_hubs\`, \`list_areas\`,
|
|
6583
|
-
\`get_graph_summary\`. They are side-effect-free and auto-approvable.
|
|
6584
|
-
|
|
6585
6671
|
## Keep it fresh
|
|
6586
6672
|
|
|
6587
6673
|
The map keeps itself fresh: \`vg ask\` and the MCP tools detect changed files
|
|
@@ -6591,28 +6677,33 @@ The map lives at \`.vibgrate/graph.json\` and is deterministic (byte-identical
|
|
|
6591
6677
|
across machines).
|
|
6592
6678
|
`;
|
|
6593
6679
|
}
|
|
6594
|
-
function nudgeMarkdown(smallRepo) {
|
|
6680
|
+
function nudgeMarkdown(smallRepo, client) {
|
|
6681
|
+
const cf = clientFlag(client);
|
|
6595
6682
|
if (smallRepo) {
|
|
6596
6683
|
return `${NUDGE_BEGIN}
|
|
6597
6684
|
## Code navigation (vg)
|
|
6598
6685
|
|
|
6599
6686
|
This repo is small enough that searching files directly is fine. Still, before a
|
|
6600
|
-
change, \`vg impact <name
|
|
6601
|
-
compact, cited context block when you want one.
|
|
6687
|
+
change, \`vg impact <name> ${cf}\` shows what it breaks, and \`vg "<question>" ${cf}\`
|
|
6688
|
+
gives a compact, cited context block when you want one. Prefer the \`vg\` MCP tools
|
|
6689
|
+
when the server is registered; when you use the CLI, pass \`${cf}\` so the call is
|
|
6690
|
+
counted.
|
|
6602
6691
|
${NUDGE_END}`;
|
|
6603
6692
|
}
|
|
6604
6693
|
return `${NUDGE_BEGIN}
|
|
6605
6694
|
## Code navigation (vg) \u2014 prefer the map
|
|
6606
6695
|
|
|
6607
6696
|
A deterministic code graph is available. It is usually the faster, cheaper path
|
|
6608
|
-
than reading or grepping many files
|
|
6697
|
+
than reading or grepping many files. Prefer the \`vg\` MCP tools when the server is
|
|
6698
|
+
registered (fastest \u2014 the map and model stay warm across calls). When you use the
|
|
6699
|
+
CLI instead, pass \`${cf}\` so the call is counted:
|
|
6609
6700
|
|
|
6610
|
-
- \`vg "<question>"\` \u2014 a compact, fact-annotated context block for a question.
|
|
6611
|
-
- \`vg show <name
|
|
6612
|
-
- \`vg impact <name
|
|
6701
|
+
- \`vg "<question>" ${cf}\` \u2014 a compact, fact-annotated context block for a question.
|
|
6702
|
+
- \`vg show <name> ${cf}\` \u2014 what a symbol is, its callers and callees.
|
|
6703
|
+
- \`vg impact <name> ${cf}\` \u2014 what breaks if you change it.
|
|
6613
6704
|
|
|
6614
|
-
|
|
6615
|
-
|
|
6705
|
+
This is advisory, not mandatory \u2014 remove it with \`vg uninstall\` (or delete this
|
|
6706
|
+
block).
|
|
6616
6707
|
${NUDGE_END}`;
|
|
6617
6708
|
}
|
|
6618
6709
|
function mcpServerEntry(launch = { command: "vg", args: ["serve"] }) {
|
|
@@ -6702,19 +6793,19 @@ function installAssistant(a, opts) {
|
|
|
6702
6793
|
const skipped = [];
|
|
6703
6794
|
let note;
|
|
6704
6795
|
if (a.skill) {
|
|
6705
|
-
writeFileEnsured(
|
|
6796
|
+
writeFileEnsured(path19.join(opts.root, a.skill), skillMarkdown(a.id));
|
|
6706
6797
|
wrote.push(a.skill);
|
|
6707
6798
|
}
|
|
6708
6799
|
if (a.mcp) {
|
|
6709
6800
|
const launch = opts.launch ?? detectServeLaunch();
|
|
6710
|
-
upsertMcp(
|
|
6801
|
+
upsertMcp(path19.join(opts.root, a.mcp.file), a.mcp, launch);
|
|
6711
6802
|
wrote.push(a.mcp.file);
|
|
6712
6803
|
note = launch.note;
|
|
6713
6804
|
} else {
|
|
6714
6805
|
skipped.push("mcp (host-specific setup)");
|
|
6715
6806
|
}
|
|
6716
6807
|
if (a.nudge && opts.hook !== false) {
|
|
6717
|
-
writeNudge(
|
|
6808
|
+
writeNudge(path19.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
|
|
6718
6809
|
wrote.push(a.nudge.file);
|
|
6719
6810
|
} else if (a.nudge) {
|
|
6720
6811
|
skipped.push("nudge (--no-hook)");
|
|
@@ -6724,40 +6815,40 @@ function installAssistant(a, opts) {
|
|
|
6724
6815
|
function uninstallAssistant(a, root, purge) {
|
|
6725
6816
|
const removed = [];
|
|
6726
6817
|
if (a.mcp) {
|
|
6727
|
-
const file =
|
|
6818
|
+
const file = path19.join(root, a.mcp.file);
|
|
6728
6819
|
if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
|
|
6729
6820
|
}
|
|
6730
6821
|
if (a.nudge) {
|
|
6731
|
-
const file =
|
|
6822
|
+
const file = path19.join(root, a.nudge.file);
|
|
6732
6823
|
if (a.nudge.kind === "block") {
|
|
6733
6824
|
if (removeBlock(file)) removed.push(a.nudge.file);
|
|
6734
|
-
} else if (
|
|
6735
|
-
|
|
6825
|
+
} else if (fs19.existsSync(file)) {
|
|
6826
|
+
fs19.rmSync(file);
|
|
6736
6827
|
removed.push(a.nudge.file);
|
|
6737
6828
|
}
|
|
6738
6829
|
}
|
|
6739
6830
|
if (purge && a.skill) {
|
|
6740
|
-
const file =
|
|
6741
|
-
if (
|
|
6742
|
-
|
|
6831
|
+
const file = path19.join(root, a.skill);
|
|
6832
|
+
if (fs19.existsSync(file)) {
|
|
6833
|
+
fs19.rmSync(file);
|
|
6743
6834
|
removed.push(a.skill);
|
|
6744
6835
|
}
|
|
6745
6836
|
}
|
|
6746
6837
|
return removed;
|
|
6747
6838
|
}
|
|
6748
6839
|
function writeFileEnsured(file, content) {
|
|
6749
|
-
|
|
6750
|
-
|
|
6840
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
6841
|
+
fs19.writeFileSync(file, content);
|
|
6751
6842
|
}
|
|
6752
6843
|
function writeNavigationConfig(root) {
|
|
6753
|
-
const rel2 =
|
|
6844
|
+
const rel2 = path19.join(".vibgrate", "mcp-navigation.json");
|
|
6754
6845
|
const doc = {
|
|
6755
6846
|
_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.",
|
|
6756
6847
|
hot_core: [...HOT_TOOLS],
|
|
6757
6848
|
deferred: deferredToolNames(),
|
|
6758
6849
|
toolset: navigationToolsetConfig()
|
|
6759
6850
|
};
|
|
6760
|
-
writeFileEnsured(
|
|
6851
|
+
writeFileEnsured(path19.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
6761
6852
|
`);
|
|
6762
6853
|
return rel2;
|
|
6763
6854
|
}
|
|
@@ -6772,7 +6863,7 @@ function upsertMcp(file, target, launch) {
|
|
|
6772
6863
|
`);
|
|
6773
6864
|
}
|
|
6774
6865
|
function removeMcp(file, target) {
|
|
6775
|
-
if (!
|
|
6866
|
+
if (!fs19.existsSync(file)) return false;
|
|
6776
6867
|
const config = readJson(file);
|
|
6777
6868
|
const bag = config[target.key];
|
|
6778
6869
|
if (!bag || !(bag.vg !== void 0)) return false;
|
|
@@ -6781,22 +6872,22 @@ function removeMcp(file, target) {
|
|
|
6781
6872
|
`);
|
|
6782
6873
|
return true;
|
|
6783
6874
|
}
|
|
6784
|
-
function writeNudge(file, target, smallRepo) {
|
|
6875
|
+
function writeNudge(file, target, smallRepo, client) {
|
|
6785
6876
|
if (target.kind === "file") {
|
|
6786
6877
|
const body = file.endsWith(".mdc") ? `---
|
|
6787
6878
|
description: vg code graph
|
|
6788
6879
|
alwaysApply: true
|
|
6789
6880
|
---
|
|
6790
|
-
${stripMarkers(nudgeMarkdown(smallRepo))}` : stripMarkers(nudgeMarkdown(smallRepo));
|
|
6881
|
+
${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(smallRepo, client));
|
|
6791
6882
|
writeFileEnsured(file, `${body}
|
|
6792
6883
|
`);
|
|
6793
6884
|
return;
|
|
6794
6885
|
}
|
|
6795
|
-
upsertBlock(file, nudgeMarkdown(smallRepo));
|
|
6886
|
+
upsertBlock(file, nudgeMarkdown(smallRepo, client));
|
|
6796
6887
|
}
|
|
6797
6888
|
function upsertBlock(file, block) {
|
|
6798
|
-
|
|
6799
|
-
let existing =
|
|
6889
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
6890
|
+
let existing = fs19.existsSync(file) ? fs19.readFileSync(file, "utf8") : "";
|
|
6800
6891
|
const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
|
|
6801
6892
|
if (re.test(existing)) existing = existing.replace(re, block);
|
|
6802
6893
|
else existing = existing.length ? `${existing.replace(/\s*$/, "")}
|
|
@@ -6804,20 +6895,20 @@ function upsertBlock(file, block) {
|
|
|
6804
6895
|
${block}
|
|
6805
6896
|
` : `${block}
|
|
6806
6897
|
`;
|
|
6807
|
-
|
|
6898
|
+
fs19.writeFileSync(file, existing);
|
|
6808
6899
|
}
|
|
6809
6900
|
function removeBlock(file) {
|
|
6810
|
-
if (!
|
|
6811
|
-
const existing =
|
|
6901
|
+
if (!fs19.existsSync(file)) return false;
|
|
6902
|
+
const existing = fs19.readFileSync(file, "utf8");
|
|
6812
6903
|
const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
|
|
6813
6904
|
if (!re.test(existing)) return false;
|
|
6814
|
-
|
|
6905
|
+
fs19.writeFileSync(file, existing.replace(re, "\n"));
|
|
6815
6906
|
return true;
|
|
6816
6907
|
}
|
|
6817
6908
|
function readJson(file) {
|
|
6818
|
-
if (!
|
|
6909
|
+
if (!fs19.existsSync(file)) return {};
|
|
6819
6910
|
try {
|
|
6820
|
-
const parsed = JSON.parse(
|
|
6911
|
+
const parsed = JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
6821
6912
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
6822
6913
|
return parsed;
|
|
6823
6914
|
}
|
|
@@ -7039,6 +7130,6 @@ function spdx(ctx) {
|
|
|
7039
7130
|
return JSON.stringify(doc, null, 2) + "\n";
|
|
7040
7131
|
}
|
|
7041
7132
|
|
|
7042
|
-
export { ASSISTANTS, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearStoredCredentials, cosine, countPending, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, epistemicBreakdown, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, ingestHostForRegionId, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, readUsage, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
7043
|
-
//# sourceMappingURL=chunk-
|
|
7044
|
-
//# sourceMappingURL=chunk-
|
|
7133
|
+
export { ASSISTANTS, CLI_TOOL_ALIASES, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_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, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, epistemicBreakdown, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, indexFor, ingestHostForRegionId, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, readUsage, recordCliCall, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
7134
|
+
//# sourceMappingURL=chunk-MPKMAERO.js.map
|
|
7135
|
+
//# sourceMappingURL=chunk-MPKMAERO.js.map
|