@vibgrate/cli 2026.703.7 → 2026.704.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +22 -0
- package/dist/baseline-64EUPWUH.js +6 -0
- package/dist/{baseline-HBRBXJHJ.js.map → baseline-64EUPWUH.js.map} +1 -1
- package/dist/{chunk-UPPWLMVD.js → chunk-GI6W53LM.js} +3 -3
- package/dist/chunk-GI6W53LM.js.map +1 -0
- package/dist/{chunk-WGXDYBAN.js → chunk-JL7FIC6W.js} +579 -132
- package/dist/chunk-JL7FIC6W.js.map +1 -0
- package/dist/{chunk-ZNVLRKWK.js → chunk-WHQRV66O.js} +3 -3
- package/dist/{chunk-ZNVLRKWK.js.map → chunk-WHQRV66O.js.map} +1 -1
- package/dist/{chunk-QWS7M7DG.js → chunk-YN5OGRWU.js} +12 -12
- package/dist/{chunk-QWS7M7DG.js.map → chunk-YN5OGRWU.js.map} +1 -1
- package/dist/cli.js +115 -50
- package/dist/cli.js.map +1 -1
- package/dist/fs-IPY4FJKH.js +3 -0
- package/dist/fs-IPY4FJKH.js.map +1 -0
- package/dist/index.d.ts +48 -2
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/dist/baseline-HBRBXJHJ.js +0 -6
- package/dist/chunk-UPPWLMVD.js.map +0 -1
- package/dist/chunk-WGXDYBAN.js.map +0 -1
- package/dist/fs-DSQZWBO5.js +0 -3
- package/dist/fs-DSQZWBO5.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { langById, shortId, canonicalize, grammarSetVersion, hashBytes,
|
|
2
|
-
import { writeTextFile, pathExists, readJsonFile,
|
|
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-YN5OGRWU.js';
|
|
3
3
|
import * as fs18 from 'fs';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import * as path18 from 'path';
|
|
6
6
|
import ignore from 'ignore';
|
|
7
|
+
import * as v8 from 'v8';
|
|
7
8
|
import Graph from 'graphology';
|
|
8
9
|
import pagerank from 'graphology-metrics/centrality/pagerank.js';
|
|
9
10
|
import betweenness from 'graphology-metrics/centrality/betweenness.js';
|
|
@@ -45,47 +46,174 @@ function isOwnBinary(binPath) {
|
|
|
45
46
|
return false;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
function isEphemeralNpxBinary(binPath) {
|
|
50
|
+
const NPX_SEGMENT = /[\\/]_npx[\\/]/;
|
|
51
|
+
if (NPX_SEGMENT.test(binPath)) return true;
|
|
52
|
+
try {
|
|
53
|
+
return NPX_SEGMENT.test(fs18.realpathSync(binPath));
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isInstalledOwnBinary(binPath) {
|
|
59
|
+
return isOwnBinary(binPath) && !isEphemeralNpxBinary(binPath);
|
|
60
|
+
}
|
|
48
61
|
var cached;
|
|
49
62
|
function resolveCliInvocation(which) {
|
|
50
63
|
if (!which && cached !== void 0) return cached;
|
|
51
64
|
const lookup = which ?? whichOnPath;
|
|
52
65
|
const vg = lookup("vg");
|
|
53
66
|
let result;
|
|
54
|
-
if (vg &&
|
|
67
|
+
if (vg && isInstalledOwnBinary(vg)) {
|
|
55
68
|
result = "vg";
|
|
56
69
|
} else {
|
|
57
70
|
const vibgrate = lookup("vibgrate");
|
|
58
|
-
result = vibgrate &&
|
|
71
|
+
result = vibgrate && isInstalledOwnBinary(vibgrate) ? "vibgrate" : NPX_INVOCATION;
|
|
59
72
|
}
|
|
60
73
|
if (!which) cached = result;
|
|
61
74
|
return result;
|
|
62
75
|
}
|
|
63
76
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
77
|
+
// Version control, IDE & tool metadata
|
|
64
78
|
".git",
|
|
65
|
-
"
|
|
79
|
+
".svn",
|
|
80
|
+
".hg",
|
|
81
|
+
".idea",
|
|
82
|
+
".vscode",
|
|
83
|
+
".vs",
|
|
66
84
|
".vibgrate",
|
|
85
|
+
".wrangler",
|
|
86
|
+
".turbo",
|
|
87
|
+
".cache",
|
|
88
|
+
// JavaScript / Node — installed dependency trees
|
|
89
|
+
"node_modules",
|
|
90
|
+
"bower_components",
|
|
91
|
+
"jspm_packages",
|
|
92
|
+
"web_modules",
|
|
93
|
+
".pnpm-store",
|
|
94
|
+
".yarn",
|
|
95
|
+
// JS/TS meta-framework & bundler build output
|
|
96
|
+
".next",
|
|
97
|
+
".nuxt",
|
|
98
|
+
".output",
|
|
99
|
+
".svelte-kit",
|
|
100
|
+
".astro",
|
|
101
|
+
".vercel",
|
|
102
|
+
".netlify",
|
|
103
|
+
".angular",
|
|
104
|
+
".parcel-cache",
|
|
105
|
+
".docusaurus",
|
|
106
|
+
"storybook-static",
|
|
107
|
+
// Generic build / dist / coverage output
|
|
67
108
|
"dist",
|
|
68
109
|
"build",
|
|
69
110
|
"out",
|
|
70
111
|
"coverage",
|
|
71
|
-
|
|
72
|
-
".nuxt",
|
|
73
|
-
".svelte-kit",
|
|
74
|
-
".turbo",
|
|
75
|
-
".cache",
|
|
112
|
+
// Vendored third-party source trees
|
|
76
113
|
"vendor",
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"obj",
|
|
81
|
-
// .NET
|
|
82
|
-
"__pycache__",
|
|
114
|
+
"Pods",
|
|
115
|
+
"Carthage",
|
|
116
|
+
// Python — virtualenvs & tool caches
|
|
83
117
|
".venv",
|
|
84
118
|
"venv",
|
|
119
|
+
"virtualenv",
|
|
85
120
|
".tox",
|
|
121
|
+
".nox",
|
|
122
|
+
"__pycache__",
|
|
123
|
+
".mypy_cache",
|
|
124
|
+
".pytest_cache",
|
|
125
|
+
".ruff_cache",
|
|
126
|
+
".eggs",
|
|
127
|
+
".ipynb_checkpoints",
|
|
128
|
+
// Ruby
|
|
129
|
+
".bundle",
|
|
130
|
+
// Rust / Maven / sbt / Clojure — all build into target/
|
|
131
|
+
"target",
|
|
132
|
+
// JVM (Gradle / Maven) & .NET build output
|
|
86
133
|
".gradle",
|
|
87
|
-
"
|
|
88
|
-
"
|
|
134
|
+
"bin",
|
|
135
|
+
"obj",
|
|
136
|
+
"TestResults",
|
|
137
|
+
// Swift / Xcode
|
|
138
|
+
".build",
|
|
139
|
+
".swiftpm",
|
|
140
|
+
"DerivedData",
|
|
141
|
+
// Dart / Flutter
|
|
142
|
+
".dart_tool",
|
|
143
|
+
// Elixir / Erlang
|
|
144
|
+
"_build",
|
|
145
|
+
"deps",
|
|
146
|
+
// Haskell (Cabal / Stack)
|
|
147
|
+
"dist-newstyle",
|
|
148
|
+
".stack-work",
|
|
149
|
+
// Infrastructure-as-code build/state
|
|
150
|
+
".terraform",
|
|
151
|
+
".serverless",
|
|
152
|
+
"cdk.out",
|
|
153
|
+
".aws-sam"
|
|
154
|
+
]);
|
|
155
|
+
var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
156
|
+
// JavaScript / Node
|
|
157
|
+
"package-lock.json",
|
|
158
|
+
"npm-shrinkwrap.json",
|
|
159
|
+
"yarn.lock",
|
|
160
|
+
"pnpm-lock.yaml",
|
|
161
|
+
"bun.lockb",
|
|
162
|
+
"bun.lock",
|
|
163
|
+
"deno.lock",
|
|
164
|
+
".pnp.cjs",
|
|
165
|
+
".pnp.data.json",
|
|
166
|
+
".pnp.loader.mjs",
|
|
167
|
+
// Python
|
|
168
|
+
"poetry.lock",
|
|
169
|
+
"pipfile.lock",
|
|
170
|
+
"pdm.lock",
|
|
171
|
+
"uv.lock",
|
|
172
|
+
"pylock.toml",
|
|
173
|
+
"conda-lock.yml",
|
|
174
|
+
// Ruby
|
|
175
|
+
"gemfile.lock",
|
|
176
|
+
// PHP
|
|
177
|
+
"composer.lock",
|
|
178
|
+
// Rust
|
|
179
|
+
"cargo.lock",
|
|
180
|
+
// Go
|
|
181
|
+
"go.sum",
|
|
182
|
+
"go.work.sum",
|
|
183
|
+
"gopkg.lock",
|
|
184
|
+
"glide.lock",
|
|
185
|
+
// .NET
|
|
186
|
+
"packages.lock.json",
|
|
187
|
+
// Java / JVM (Gradle)
|
|
188
|
+
"gradle.lockfile",
|
|
189
|
+
// Swift / Xcode
|
|
190
|
+
"package.resolved",
|
|
191
|
+
// Dart / Flutter
|
|
192
|
+
"pubspec.lock",
|
|
193
|
+
// Elixir
|
|
194
|
+
"mix.lock",
|
|
195
|
+
// Haskell (Cabal / Stack)
|
|
196
|
+
"cabal.project.freeze",
|
|
197
|
+
"stack.yaml.lock",
|
|
198
|
+
// Julia
|
|
199
|
+
"manifest.toml",
|
|
200
|
+
// R
|
|
201
|
+
"renv.lock",
|
|
202
|
+
// Perl (Carton)
|
|
203
|
+
"cpanfile.snapshot",
|
|
204
|
+
// CocoaPods / Carthage
|
|
205
|
+
"podfile.lock",
|
|
206
|
+
"cartfile.resolved",
|
|
207
|
+
// Terraform / IaC
|
|
208
|
+
".terraform.lock.hcl",
|
|
209
|
+
// Helm
|
|
210
|
+
"chart.lock",
|
|
211
|
+
// C / C++ (Conan)
|
|
212
|
+
"conan.lock",
|
|
213
|
+
// Bazel
|
|
214
|
+
"module.bazel.lock",
|
|
215
|
+
// Nix
|
|
216
|
+
"flake.lock"
|
|
89
217
|
]);
|
|
90
218
|
function toPosix(p) {
|
|
91
219
|
return p.split(path18.sep).join("/");
|
|
@@ -115,6 +243,7 @@ function discover(options) {
|
|
|
115
243
|
const rel2 = toPosix(path18.relative(root, abs));
|
|
116
244
|
if (rel2.startsWith("..")) return;
|
|
117
245
|
if (rel2 === "" || rootIg.ignores(rel2)) return;
|
|
246
|
+
if (SKIP_FILES.has(path18.basename(abs).toLowerCase())) return;
|
|
118
247
|
const lang = langForExtension(path18.extname(abs));
|
|
119
248
|
if (!lang || !allowLang(lang)) return;
|
|
120
249
|
found.set(rel2, { rel: rel2, abs, lang });
|
|
@@ -152,6 +281,58 @@ var UsageError = class extends Error {
|
|
|
152
281
|
this.name = "UsageError";
|
|
153
282
|
}
|
|
154
283
|
};
|
|
284
|
+
var MIB = 1024 * 1024;
|
|
285
|
+
var DEFAULT_MAX_FILE_BYTES = 2 * MIB;
|
|
286
|
+
var DEFAULT_MAX_FILES = 1e5;
|
|
287
|
+
var DEFAULT_TSC_MAX_FILES = 1e4;
|
|
288
|
+
var HEAP_GUARD_FRACTION = 0.9;
|
|
289
|
+
var ResourceLimitError = class extends Error {
|
|
290
|
+
isResourceLimitError = true;
|
|
291
|
+
constructor(message) {
|
|
292
|
+
super(message);
|
|
293
|
+
this.name = "ResourceLimitError";
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
function envInt(name, fallback) {
|
|
297
|
+
const raw = process.env[name];
|
|
298
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
299
|
+
const n = Number(raw);
|
|
300
|
+
return Number.isInteger(n) && n >= 0 ? n : fallback;
|
|
301
|
+
}
|
|
302
|
+
function envIntOptional(name) {
|
|
303
|
+
const n = envInt(name, 0);
|
|
304
|
+
return n > 0 ? n : void 0;
|
|
305
|
+
}
|
|
306
|
+
function defaultMemoryBudgetMb() {
|
|
307
|
+
return Math.floor(v8.getHeapStatistics().heap_size_limit / MIB * HEAP_GUARD_FRACTION);
|
|
308
|
+
}
|
|
309
|
+
function resolveLimits(overrides = {}) {
|
|
310
|
+
return {
|
|
311
|
+
maxFileBytes: overrides.maxFileBytes ?? envInt("VG_MAX_FILE_BYTES", DEFAULT_MAX_FILE_BYTES),
|
|
312
|
+
maxFiles: overrides.maxFiles ?? envInt("VG_MAX_FILES", DEFAULT_MAX_FILES),
|
|
313
|
+
tscMaxFiles: overrides.tscMaxFiles ?? envInt("VG_TSC_MAX_FILES", DEFAULT_TSC_MAX_FILES),
|
|
314
|
+
memoryBudgetMb: overrides.memoryBudgetMb ?? envInt("VG_MEMORY_BUDGET_MB", defaultMemoryBudgetMb())
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function envJobs() {
|
|
318
|
+
return envIntOptional("VG_JOBS");
|
|
319
|
+
}
|
|
320
|
+
function envWorkerHeapMb() {
|
|
321
|
+
return envIntOptional("VG_WORKER_HEAP_MB");
|
|
322
|
+
}
|
|
323
|
+
function checkMemoryBudget(phase, budgetMb) {
|
|
324
|
+
if (budgetMb <= 0) return;
|
|
325
|
+
const usedMb = process.memoryUsage().heapUsed / MIB;
|
|
326
|
+
if (usedMb <= budgetMb) return;
|
|
327
|
+
throw new ResourceLimitError(
|
|
328
|
+
`graph build stopped during ${phase}: heap use ${Math.round(usedMb)} MiB exceeds the ${budgetMb} MiB budget. Narrow the build (scope to sub-paths, add --exclude globs, or --only <langs>), raise the Node heap (NODE_OPTIONS=--max-old-space-size=8192), or set VG_MEMORY_BUDGET_MB (0 disables this safeguard).`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
function formatBytes(n) {
|
|
332
|
+
if (n >= MIB) return `${(n / MIB).toFixed(1)} MiB`;
|
|
333
|
+
if (n >= 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
334
|
+
return `${n} B`;
|
|
335
|
+
}
|
|
155
336
|
var JS_TS_EXT = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
156
337
|
var OTHER_EXT = [
|
|
157
338
|
".py",
|
|
@@ -1293,18 +1474,18 @@ var DEFAULT_INLINE_THRESHOLD = 24;
|
|
|
1293
1474
|
async function parseFiles(files, options = {}) {
|
|
1294
1475
|
const threshold = options.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;
|
|
1295
1476
|
const cores = Math.max(1, os.cpus()?.length ?? 1);
|
|
1296
|
-
const jobs = Math.max(1, options.jobs ?? (Math.min(cores - 1, files.length) || 1));
|
|
1477
|
+
const jobs = Math.max(1, options.jobs ?? envJobs() ?? (Math.min(cores - 1, files.length) || 1));
|
|
1297
1478
|
const workerFile = resolveWorkerFile();
|
|
1298
1479
|
const useInline = options.inline === true || jobs <= 1 || files.length < threshold || workerFile === null;
|
|
1299
1480
|
if (useInline) {
|
|
1300
1481
|
if (options.grammarsDir) setGrammarsOverride(options.grammarsDir);
|
|
1301
|
-
return sortByRel(await parseInline(files, options
|
|
1482
|
+
return sortByRel(await parseInline(files, options));
|
|
1302
1483
|
}
|
|
1303
|
-
return sortByRel(
|
|
1304
|
-
await parsePooled(files, jobs, workerFile, options.onProgress, options.grammarsDir)
|
|
1305
|
-
);
|
|
1484
|
+
return sortByRel(await parsePooled(files, jobs, workerFile, options));
|
|
1306
1485
|
}
|
|
1307
|
-
|
|
1486
|
+
var MEM_CHECK_EVERY = 64;
|
|
1487
|
+
async function parseInline(files, options) {
|
|
1488
|
+
const { onProgress, memoryBudgetMb = 0 } = options;
|
|
1308
1489
|
const out = [];
|
|
1309
1490
|
onProgress?.(0, files.length);
|
|
1310
1491
|
for (const file of files) {
|
|
@@ -1315,12 +1496,20 @@ async function parseInline(files, onProgress) {
|
|
|
1315
1496
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
1316
1497
|
}
|
|
1317
1498
|
onProgress?.(out.length, files.length);
|
|
1499
|
+
if (out.length % MEM_CHECK_EVERY === 0) checkMemoryBudget("parse", memoryBudgetMb);
|
|
1318
1500
|
}
|
|
1319
1501
|
return out;
|
|
1320
1502
|
}
|
|
1321
|
-
async function parsePooled(files, jobs, workerFile,
|
|
1503
|
+
async function parsePooled(files, jobs, workerFile, options) {
|
|
1504
|
+
const { onProgress, grammarsDir, memoryBudgetMb = 0 } = options;
|
|
1322
1505
|
const { default: Tinypool } = await import('tinypool');
|
|
1323
|
-
const
|
|
1506
|
+
const workerHeapMb = envWorkerHeapMb();
|
|
1507
|
+
const pool = new Tinypool({
|
|
1508
|
+
filename: workerFile,
|
|
1509
|
+
maxThreads: jobs,
|
|
1510
|
+
minThreads: 1,
|
|
1511
|
+
...workerHeapMb ? { resourceLimits: { maxOldGenerationSizeMb: workerHeapMb } } : {}
|
|
1512
|
+
});
|
|
1324
1513
|
try {
|
|
1325
1514
|
const total = files.length;
|
|
1326
1515
|
const buckets = chunk(
|
|
@@ -1334,15 +1523,27 @@ async function parsePooled(files, jobs, workerFile, onProgress, grammarsDir) {
|
|
|
1334
1523
|
(b) => pool.run({ tasks: b, grammarsDir }).then((r) => {
|
|
1335
1524
|
done += b.length;
|
|
1336
1525
|
onProgress?.(done, total);
|
|
1526
|
+
checkMemoryBudget("parse", memoryBudgetMb);
|
|
1337
1527
|
return r;
|
|
1338
1528
|
})
|
|
1339
1529
|
)
|
|
1340
1530
|
);
|
|
1341
1531
|
return results.flat();
|
|
1532
|
+
} catch (err) {
|
|
1533
|
+
if (isWorkerOom(err)) {
|
|
1534
|
+
throw new ResourceLimitError(
|
|
1535
|
+
`graph build stopped: a parse worker exceeded its ${workerHeapMb ?? "?"} MiB heap cap (VG_WORKER_HEAP_MB). Raise the cap, exclude the offending files (--exclude), or run single-threaded with --jobs 1.`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
throw err;
|
|
1342
1539
|
} finally {
|
|
1343
1540
|
await pool.destroy();
|
|
1344
1541
|
}
|
|
1345
1542
|
}
|
|
1543
|
+
function isWorkerOom(err) {
|
|
1544
|
+
const e = err;
|
|
1545
|
+
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1546
|
+
}
|
|
1346
1547
|
function resolveWorkerFile() {
|
|
1347
1548
|
const here = path18.dirname(fileURLToPath(import.meta.url));
|
|
1348
1549
|
const candidate = path18.join(here, "parse-worker.js");
|
|
@@ -1845,6 +2046,13 @@ async function buildGraph(options) {
|
|
|
1845
2046
|
exclude: options.exclude,
|
|
1846
2047
|
paths: options.paths
|
|
1847
2048
|
});
|
|
2049
|
+
const limits = resolveLimits(options.limits);
|
|
2050
|
+
if (limits.maxFiles > 0 && files.length > limits.maxFiles) {
|
|
2051
|
+
throw new ResourceLimitError(
|
|
2052
|
+
`graph build stopped: ${files.length.toLocaleString()} files exceed the ${limits.maxFiles.toLocaleString()}-file limit. Scope the build (pass sub-paths, add --exclude globs, or --only <langs>), or set VG_MAX_FILES to raise the limit (0 disables it).`
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
checkMemoryBudget("discovery", limits.memoryBudgetMb);
|
|
1848
2056
|
const grammars = grammarSetVersion();
|
|
1849
2057
|
const cache = loadCache(root, {
|
|
1850
2058
|
toolVersion: VERSION,
|
|
@@ -1855,11 +2063,28 @@ async function buildGraph(options) {
|
|
|
1855
2063
|
const fileStats = [];
|
|
1856
2064
|
const toParse = [];
|
|
1857
2065
|
const reused = [];
|
|
2066
|
+
const buildWarnings = [];
|
|
1858
2067
|
for (const file of files) {
|
|
1859
|
-
let hash = "";
|
|
1860
2068
|
let stat;
|
|
1861
2069
|
try {
|
|
1862
2070
|
stat = fs18.statSync(file.abs);
|
|
2071
|
+
} catch {
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
if (limits.maxFileBytes > 0 && stat.size > limits.maxFileBytes) {
|
|
2075
|
+
fileStats.push({
|
|
2076
|
+
rel: file.rel,
|
|
2077
|
+
size: stat.size,
|
|
2078
|
+
mtimeMs: stat.mtimeMs,
|
|
2079
|
+
hash: hashString(`vg:oversize:${stat.size}`)
|
|
2080
|
+
});
|
|
2081
|
+
buildWarnings.push(
|
|
2082
|
+
`${file.rel}: skipped \u2014 ${formatBytes(stat.size)} exceeds the ${formatBytes(limits.maxFileBytes)} per-file limit (set VG_MAX_FILE_BYTES to raise it, 0 to disable)`
|
|
2083
|
+
);
|
|
2084
|
+
continue;
|
|
2085
|
+
}
|
|
2086
|
+
let hash = "";
|
|
2087
|
+
try {
|
|
1863
2088
|
hash = hashBytes(fs18.readFileSync(file.abs));
|
|
1864
2089
|
} catch {
|
|
1865
2090
|
continue;
|
|
@@ -1874,8 +2099,10 @@ async function buildGraph(options) {
|
|
|
1874
2099
|
jobs: options.jobs,
|
|
1875
2100
|
inline: options.inline,
|
|
1876
2101
|
onProgress: options.onParseProgress,
|
|
1877
|
-
grammarsDir: options.grammarsDir
|
|
2102
|
+
grammarsDir: options.grammarsDir,
|
|
2103
|
+
memoryBudgetMb: limits.memoryBudgetMb
|
|
1878
2104
|
});
|
|
2105
|
+
checkMemoryBudget("parse", limits.memoryBudgetMb);
|
|
1879
2106
|
for (const p of parsedNew) cache.set(p.rel, p);
|
|
1880
2107
|
const currentRels = new Set(files.map((f) => f.rel));
|
|
1881
2108
|
cache.prune(currentRels);
|
|
@@ -1883,14 +2110,21 @@ async function buildGraph(options) {
|
|
|
1883
2110
|
const parses = [...reused, ...parsedNew].sort(
|
|
1884
2111
|
(a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0
|
|
1885
2112
|
);
|
|
1886
|
-
const warnings = parses.flatMap((p) => p.warnings ?? []);
|
|
2113
|
+
const warnings = [...buildWarnings, ...parses.flatMap((p) => p.warnings ?? [])];
|
|
1887
2114
|
const moduleResolver = buildModuleResolver(root, new Set(parses.map((p) => p.rel)));
|
|
1888
2115
|
const resolved = resolve4(parses, moduleResolver);
|
|
2116
|
+
checkMemoryBudget("resolve", limits.memoryBudgetMb);
|
|
1889
2117
|
const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
|
|
1890
2118
|
let edges = resolved.edges;
|
|
1891
2119
|
const resolvers = [...resolved.stats.resolvers];
|
|
1892
2120
|
let tscStats;
|
|
1893
|
-
|
|
2121
|
+
let tsFiles = options.noTsc ? [] : files.filter((f) => (f.lang.id === "ts" || f.lang.id === "tsx" || f.lang.id === "js") && hashes.has(f.rel)).map((f) => ({ rel: f.rel, abs: f.abs }));
|
|
2122
|
+
if (limits.tscMaxFiles > 0 && tsFiles.length > limits.tscMaxFiles) {
|
|
2123
|
+
warnings.push(
|
|
2124
|
+
`typescript resolver skipped \u2014 ${tsFiles.length.toLocaleString()} TS/JS files exceed the ${limits.tscMaxFiles.toLocaleString()}-file limit; calls use the heuristic resolver (set VG_TSC_MAX_FILES to raise it, 0 to disable)`
|
|
2125
|
+
);
|
|
2126
|
+
tsFiles = [];
|
|
2127
|
+
}
|
|
1894
2128
|
if (tsFiles.length) {
|
|
1895
2129
|
const res = tsResolveEdges(root, tsFiles, resolved.nodes);
|
|
1896
2130
|
if (res.stats.files > 0) {
|
|
@@ -1898,6 +2132,7 @@ async function buildGraph(options) {
|
|
|
1898
2132
|
if (!resolvers.includes("tsc")) resolvers.unshift("tsc");
|
|
1899
2133
|
tscStats = res.stats;
|
|
1900
2134
|
}
|
|
2135
|
+
checkMemoryBudget("typescript resolution", limits.memoryBudgetMb);
|
|
1901
2136
|
}
|
|
1902
2137
|
const scip = options.noScip ? null : loadScipIndex(root, options.scip);
|
|
1903
2138
|
let scipStats;
|
|
@@ -1912,6 +2147,7 @@ async function buildGraph(options) {
|
|
|
1912
2147
|
const coverage = options.noCoverage ? null : loadCoverage(root, options.coverage);
|
|
1913
2148
|
if (coverage) nodes = applyCoverage(nodes, coverage);
|
|
1914
2149
|
const analysis = analyze(nodes, linked.edges, { cluster: options.cluster });
|
|
2150
|
+
checkMemoryBudget("analysis", limits.memoryBudgetMb);
|
|
1915
2151
|
const languages = [...new Set(parses.map((p) => p.lang))].sort();
|
|
1916
2152
|
const edgeKinds = [...new Set(analysis.edges.map((e) => e.kind))].sort();
|
|
1917
2153
|
const corpusHash = computeCorpusHash(parses, hashes);
|
|
@@ -5173,33 +5409,144 @@ function boundList(items, max) {
|
|
|
5173
5409
|
return { items: items.slice(0, max), total: items.length };
|
|
5174
5410
|
}
|
|
5175
5411
|
var NODE_EDGE_CAP = 50;
|
|
5412
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set([".git", ".vibgrate", "node_modules", "dist", "build", "out", "target", "vendor", "__pycache__"]);
|
|
5413
|
+
var MAX_FILE_BYTES = 1e6;
|
|
5414
|
+
var MAX_FILES_SCANNED = 2e4;
|
|
5415
|
+
var PREVIEW_CHARS = 120;
|
|
5416
|
+
function searchSymbols(graph, root, query, limit) {
|
|
5417
|
+
const q2 = query.trim();
|
|
5418
|
+
if (!q2) return { matches: [], moreAvailable: false, hint: "query is required" };
|
|
5419
|
+
const nodes = findNodes(graph, q2).filter((n) => n.kind !== "file");
|
|
5420
|
+
const symbolHits = nodes.slice(0, limit).map((n, i) => ({
|
|
5421
|
+
kind: n.kind,
|
|
5422
|
+
name: n.qualifiedName,
|
|
5423
|
+
file: n.file,
|
|
5424
|
+
line: n.span.start,
|
|
5425
|
+
score: Math.round((1 - i / Math.max(nodes.length, 1)) * 100) / 100
|
|
5426
|
+
}));
|
|
5427
|
+
const spare = limit - symbolHits.length;
|
|
5428
|
+
const textHits = [];
|
|
5429
|
+
let truncatedScan = false;
|
|
5430
|
+
if (spare > 0) {
|
|
5431
|
+
const seen = new Set(symbolHits.map((h) => `${h.file}:${h.line}`));
|
|
5432
|
+
truncatedScan = scanFiles(root, q2, spare, seen, textHits);
|
|
5433
|
+
}
|
|
5434
|
+
const matches = [...symbolHits, ...textHits];
|
|
5435
|
+
if (matches.length === 0) {
|
|
5436
|
+
return {
|
|
5437
|
+
matches,
|
|
5438
|
+
moreAvailable: false,
|
|
5439
|
+
hint: "no symbol or text match \u2014 for meaning-level questions (symptoms, relationships, what-breaks-if) use query_graph"
|
|
5440
|
+
};
|
|
5441
|
+
}
|
|
5442
|
+
return { matches, moreAvailable: nodes.length > symbolHits.length || truncatedScan };
|
|
5443
|
+
}
|
|
5444
|
+
function scanFiles(root, needle, budget, seen, out) {
|
|
5445
|
+
const lower = needle.toLowerCase();
|
|
5446
|
+
let scanned = 0;
|
|
5447
|
+
const stack = [root];
|
|
5448
|
+
while (stack.length) {
|
|
5449
|
+
const dir = stack.pop();
|
|
5450
|
+
let entries;
|
|
5451
|
+
try {
|
|
5452
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
5453
|
+
} catch {
|
|
5454
|
+
continue;
|
|
5455
|
+
}
|
|
5456
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
5457
|
+
for (const e of entries) {
|
|
5458
|
+
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
5459
|
+
const abs = path18.join(dir, e.name);
|
|
5460
|
+
if (e.isDirectory()) {
|
|
5461
|
+
stack.push(abs);
|
|
5462
|
+
continue;
|
|
5463
|
+
}
|
|
5464
|
+
if (++scanned > MAX_FILES_SCANNED) return true;
|
|
5465
|
+
let text;
|
|
5466
|
+
try {
|
|
5467
|
+
if (fs18.statSync(abs).size > MAX_FILE_BYTES) continue;
|
|
5468
|
+
text = fs18.readFileSync(abs, "utf8");
|
|
5469
|
+
} catch {
|
|
5470
|
+
continue;
|
|
5471
|
+
}
|
|
5472
|
+
if (!text.toLowerCase().includes(lower)) continue;
|
|
5473
|
+
const rel2 = path18.relative(root, abs);
|
|
5474
|
+
const lines = text.split("\n");
|
|
5475
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5476
|
+
if (!lines[i].toLowerCase().includes(lower)) continue;
|
|
5477
|
+
const key = `${rel2}:${i + 1}`;
|
|
5478
|
+
if (seen.has(key)) continue;
|
|
5479
|
+
seen.add(key);
|
|
5480
|
+
out.push({ kind: "text", file: rel2, line: i + 1, preview: lines[i].trim().slice(0, PREVIEW_CHARS) });
|
|
5481
|
+
if (out.length >= budget) return true;
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5485
|
+
return false;
|
|
5486
|
+
}
|
|
5176
5487
|
|
|
5177
5488
|
// src/mcp/tools.ts
|
|
5178
|
-
var
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
return
|
|
5489
|
+
var warmEmbedder = null;
|
|
5490
|
+
var bgWarmStarted = false;
|
|
5491
|
+
function warmEmbedderInBackground(local) {
|
|
5492
|
+
if (local || bgWarmStarted || isModelReady()) return;
|
|
5493
|
+
bgWarmStarted = true;
|
|
5494
|
+
loadEmbedder({}).then((e) => {
|
|
5495
|
+
if (e) warmEmbedder = Promise.resolve(e);
|
|
5496
|
+
}).catch(() => {
|
|
5497
|
+
bgWarmStarted = false;
|
|
5498
|
+
});
|
|
5182
5499
|
}
|
|
5500
|
+
function readyEmbedder(local) {
|
|
5501
|
+
if (warmEmbedder) return warmEmbedder;
|
|
5502
|
+
if (local) return warmEmbedder = loadEmbedder({ local, noDownload: true });
|
|
5503
|
+
if (isModelReady()) return warmEmbedder = loadEmbedder({ noDownload: true });
|
|
5504
|
+
warmEmbedderInBackground(local);
|
|
5505
|
+
return Promise.resolve(null);
|
|
5506
|
+
}
|
|
5507
|
+
async function retrieve(graph, question, budget, ctx) {
|
|
5508
|
+
let mode = "lexical";
|
|
5509
|
+
try {
|
|
5510
|
+
const q2 = await withTimeout(
|
|
5511
|
+
(async () => {
|
|
5512
|
+
const embedder = await readyEmbedder(ctx.local);
|
|
5513
|
+
if (!embedder) return null;
|
|
5514
|
+
const nodeVectors = await getNodeEmbeddings(graph, embedder, ctx.root);
|
|
5515
|
+
const r = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors });
|
|
5516
|
+
mode = `semantic (${embedder.id})`;
|
|
5517
|
+
return r;
|
|
5518
|
+
})(),
|
|
5519
|
+
SEMANTIC_BUDGET_MS,
|
|
5520
|
+
"semantic path over budget"
|
|
5521
|
+
);
|
|
5522
|
+
if (q2) return { q: q2, mode };
|
|
5523
|
+
} catch {
|
|
5524
|
+
}
|
|
5525
|
+
return { q: queryGraph(graph, question, { budget }), mode: "lexical" };
|
|
5526
|
+
}
|
|
5527
|
+
var SEMANTIC_BUDGET_MS = (() => {
|
|
5528
|
+
const n = Number(process.env.VG_SEMANTIC_BUDGET_MS);
|
|
5529
|
+
return Number.isFinite(n) && n > 0 ? n : 4e3;
|
|
5530
|
+
})();
|
|
5183
5531
|
var obj = (properties, required = []) => ({
|
|
5184
5532
|
type: "object",
|
|
5185
5533
|
properties,
|
|
5186
|
-
required,
|
|
5534
|
+
...required.length ? { required } : {},
|
|
5187
5535
|
additionalProperties: false
|
|
5188
5536
|
});
|
|
5537
|
+
var RESPONSE_FORMAT = { type: "string", enum: ["concise", "detailed"] };
|
|
5538
|
+
var isDetailed = (args) => args.response_format === "detailed";
|
|
5539
|
+
var CONCISE_MATCHES = 5;
|
|
5189
5540
|
var TOOLS = [
|
|
5190
|
-
{
|
|
5191
|
-
name: "get_graph_summary",
|
|
5192
|
-
description: "High-level summary of the code map: counts, languages, clustering, top areas and hubs.",
|
|
5193
|
-
inputSchema: obj({}),
|
|
5194
|
-
handler: (graph) => summarize(graph)
|
|
5195
|
-
},
|
|
5196
5541
|
{
|
|
5197
5542
|
name: "orient",
|
|
5198
|
-
description: "
|
|
5543
|
+
description: "Start here: one call returns the map overview, ranked matches for your question, and the top hit\u2019s blast radius \u2014 replaces summary+query+impact round-trips.",
|
|
5199
5544
|
inputSchema: obj(
|
|
5200
5545
|
{
|
|
5201
|
-
question: { type: "string",
|
|
5202
|
-
|
|
5546
|
+
question: { type: "string", maxLength: 300 },
|
|
5547
|
+
scope: { type: "string", description: "path prefix to orient within" },
|
|
5548
|
+
budget: { type: "number", description: "token budget in detailed mode (default 1500)" },
|
|
5549
|
+
response_format: RESPONSE_FORMAT
|
|
5203
5550
|
},
|
|
5204
5551
|
["question"]
|
|
5205
5552
|
),
|
|
@@ -5207,67 +5554,93 @@ var TOOLS = [
|
|
|
5207
5554
|
const question = String(args.question ?? "");
|
|
5208
5555
|
if (!question) return { error: "bad_request", message: "question is required" };
|
|
5209
5556
|
const budget = numOr(args.budget, 1500);
|
|
5210
|
-
const
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
q2 = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors });
|
|
5216
|
-
mode = `semantic (${embedder.id})`;
|
|
5217
|
-
} else {
|
|
5218
|
-
q2 = queryGraph(graph, question, { budget });
|
|
5219
|
-
}
|
|
5557
|
+
const { q: q2, mode } = await retrieve(graph, question, budget, ctx);
|
|
5558
|
+
const rawScope = String(args.scope ?? "").trim();
|
|
5559
|
+
const scope = rawScope === "." || rawScope === "./" ? "" : rawScope.replace(/^\.\//, "");
|
|
5560
|
+
const scoped = scope ? q2.matches.filter((m) => m.node.file.startsWith(scope)) : q2.matches;
|
|
5561
|
+
const detailed = isDetailed(args);
|
|
5220
5562
|
let topImpact = null;
|
|
5221
|
-
const top =
|
|
5222
|
-
if (top) {
|
|
5563
|
+
const top = scoped[0]?.node;
|
|
5564
|
+
if (detailed && top) {
|
|
5223
5565
|
const r = impactOf(graph, top.id, { depth: 3 });
|
|
5224
5566
|
topImpact = { node: top.qualifiedName, direct: r.direct, transitive: r.transitive, affected: r.affected.slice(0, 10).map(stripId) };
|
|
5225
5567
|
}
|
|
5568
|
+
const shown = detailed ? scoped : scoped.slice(0, CONCISE_MATCHES);
|
|
5226
5569
|
return {
|
|
5227
|
-
summary: summarize(graph),
|
|
5570
|
+
summary: summarize(graph, detailed ? 10 : CONCISE_MATCHES),
|
|
5228
5571
|
mode,
|
|
5229
|
-
context:
|
|
5230
|
-
|
|
5231
|
-
|
|
5572
|
+
// The fact-annotated context block is detail: concise callers navigate
|
|
5573
|
+
// by the ranked matches and fetch nodes on demand (plan P2).
|
|
5574
|
+
...detailed ? { context: q2.context } : {},
|
|
5575
|
+
matches: shown.map((m) => ({ name: m.node.qualifiedName, kind: m.node.kind, file: m.node.file, line: m.node.span.start, score: m.score })),
|
|
5576
|
+
// A neutral availability flag, not a directive to keep querying: a
|
|
5577
|
+
// retrieval tool that coaches "narrow the question / see more" nudges the
|
|
5578
|
+
// over-navigation that re-bills the whole context on every extra step.
|
|
5579
|
+
...scoped.length > shown.length ? { moreAvailable: true } : {},
|
|
5580
|
+
...detailed ? { topImpact } : {}
|
|
5232
5581
|
};
|
|
5233
5582
|
}
|
|
5234
5583
|
},
|
|
5584
|
+
{
|
|
5585
|
+
name: "search_symbols",
|
|
5586
|
+
description: "Find a known name or string fast: ranked symbol lookup with literal file-search fallthrough. Use first for most discovery; use query_graph for meaning.",
|
|
5587
|
+
inputSchema: obj(
|
|
5588
|
+
{
|
|
5589
|
+
query: { type: "string", maxLength: 120 },
|
|
5590
|
+
limit: { type: "number", description: "default 8, max 20" }
|
|
5591
|
+
},
|
|
5592
|
+
["query"]
|
|
5593
|
+
),
|
|
5594
|
+
handler: (graph, args, ctx) => {
|
|
5595
|
+
const limit = Math.min(20, numOr(args.limit, 8));
|
|
5596
|
+
return searchSymbols(graph, ctx.root, String(args.query ?? ""), limit);
|
|
5597
|
+
}
|
|
5598
|
+
},
|
|
5235
5599
|
{
|
|
5236
5600
|
name: "query_graph",
|
|
5237
|
-
description: "
|
|
5601
|
+
description: "Find code by meaning when you don\u2019t know the name: symptoms, relationships, what-breaks-if. For a known name or literal string use search_symbols.",
|
|
5238
5602
|
inputSchema: obj(
|
|
5239
5603
|
{
|
|
5240
|
-
question: { type: "string",
|
|
5241
|
-
|
|
5604
|
+
question: { type: "string", maxLength: 300 },
|
|
5605
|
+
limit: { type: "number", description: "ranked matches to return (default 5)" },
|
|
5606
|
+
offset: { type: "number" },
|
|
5607
|
+
budget: { type: "number", description: "context-block token budget in detailed mode (default 2000)" },
|
|
5608
|
+
response_format: RESPONSE_FORMAT
|
|
5242
5609
|
},
|
|
5243
5610
|
["question"]
|
|
5244
5611
|
),
|
|
5245
5612
|
handler: async (graph, args, ctx) => {
|
|
5246
5613
|
const question = String(args.question ?? "");
|
|
5247
5614
|
const budget = numOr(args.budget, 2e3);
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
if (embedder) {
|
|
5252
|
-
const nodeVectors = await getNodeEmbeddings(graph, embedder, ctx.root);
|
|
5253
|
-
r = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors });
|
|
5254
|
-
mode = `semantic (${embedder.id})`;
|
|
5255
|
-
} else {
|
|
5256
|
-
r = queryGraph(graph, question, { budget });
|
|
5615
|
+
const { q: r, mode } = await retrieve(graph, question, budget, ctx);
|
|
5616
|
+
if (r.matches.length === 0) {
|
|
5617
|
+
return { mode, matches: [], hint: "no semantic match \u2014 for a known name or literal string use search_symbols; otherwise rephrase around the behaviour you observe" };
|
|
5257
5618
|
}
|
|
5619
|
+
const limit = numOr(args.limit, CONCISE_MATCHES);
|
|
5620
|
+
const offset = Math.max(0, numOrU(args.offset) ?? 0);
|
|
5621
|
+
const page = r.matches.slice(offset, offset + limit);
|
|
5258
5622
|
return {
|
|
5259
5623
|
mode,
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5624
|
+
summary: `${r.matches.length} match${r.matches.length === 1 ? "" : "es"}; top: ${r.matches[0].node.qualifiedName}`,
|
|
5625
|
+
// The fact-annotated context block is detail (plan P2): ranked matches
|
|
5626
|
+
// first, fetch nodes on demand.
|
|
5627
|
+
...isDetailed(args) ? { context: r.context, tokensEstimate: r.tokensEstimate } : {},
|
|
5628
|
+
matches: page.map((m) => ({ name: m.node.qualifiedName, kind: m.node.kind, file: m.node.file, line: m.node.span.start, score: m.score })),
|
|
5629
|
+
// Neutral pagination fact (nextOffset if the model genuinely needs more),
|
|
5630
|
+
// not a "narrow the question" nudge that invites another discovery round.
|
|
5631
|
+
...offset + page.length < r.matches.length ? { moreAvailable: true, nextOffset: offset + page.length } : {}
|
|
5263
5632
|
};
|
|
5264
5633
|
}
|
|
5265
5634
|
},
|
|
5266
5635
|
{
|
|
5267
5636
|
name: "get_node",
|
|
5268
|
-
description: "
|
|
5637
|
+
description: "Inspect one symbol: signature, callers, callees, area. Accepts name, file:line, glob or id.",
|
|
5269
5638
|
inputSchema: obj(
|
|
5270
|
-
{
|
|
5639
|
+
{
|
|
5640
|
+
name: { type: "string" },
|
|
5641
|
+
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5642
|
+
response_format: RESPONSE_FORMAT
|
|
5643
|
+
},
|
|
5271
5644
|
["name"]
|
|
5272
5645
|
),
|
|
5273
5646
|
handler: (graph, args, ctx) => {
|
|
@@ -5287,8 +5660,9 @@ var TOOLS = [
|
|
|
5287
5660
|
if (ctx?.dedup && ctx.seen?.has(node.id)) {
|
|
5288
5661
|
return { ...base, repeat: true, callsTotal: uniqueNames(index.callees(node.id).map((x) => x.node.qualifiedName)).length, calledByTotal: uniqueNames(index.callers(node.id).map((x) => x.node.qualifiedName)).length };
|
|
5289
5662
|
}
|
|
5290
|
-
const
|
|
5291
|
-
const
|
|
5663
|
+
const edgeCap = isDetailed(args) ? NODE_EDGE_CAP : CONCISE_MATCHES;
|
|
5664
|
+
const calls = boundList(uniqueNames(index.callees(node.id).map((x) => x.node.qualifiedName)), edgeCap);
|
|
5665
|
+
const calledBy = boundList(uniqueNames(index.callers(node.id).map((x) => x.node.qualifiedName)), edgeCap);
|
|
5292
5666
|
ctx?.seen?.add(node.id);
|
|
5293
5667
|
return {
|
|
5294
5668
|
...base,
|
|
@@ -5301,13 +5675,13 @@ var TOOLS = [
|
|
|
5301
5675
|
},
|
|
5302
5676
|
{
|
|
5303
5677
|
name: "find_path",
|
|
5304
|
-
description: "Shortest connection
|
|
5678
|
+
description: "Shortest connection from a to b.",
|
|
5305
5679
|
inputSchema: obj(
|
|
5306
5680
|
{
|
|
5307
5681
|
a: { type: "string" },
|
|
5308
5682
|
b: { type: "string" },
|
|
5309
|
-
pick_a: { type: "number"
|
|
5310
|
-
pick_b: { type: "number"
|
|
5683
|
+
pick_a: { type: "number" },
|
|
5684
|
+
pick_b: { type: "number" }
|
|
5311
5685
|
},
|
|
5312
5686
|
["a", "b"]
|
|
5313
5687
|
),
|
|
@@ -5324,25 +5698,76 @@ var TOOLS = [
|
|
|
5324
5698
|
},
|
|
5325
5699
|
{
|
|
5326
5700
|
name: "impact_of",
|
|
5327
|
-
description: "
|
|
5701
|
+
description: "Blast radius of a change: what breaks if this symbol changes \u2014 dependents, files, covering tests, risk.",
|
|
5328
5702
|
inputSchema: obj(
|
|
5329
5703
|
{
|
|
5330
5704
|
name: { type: "string" },
|
|
5331
|
-
|
|
5332
|
-
|
|
5705
|
+
change_type: { type: "string", enum: ["modify", "delete", "rename", "add_dependency"] },
|
|
5706
|
+
depth: { type: "number", description: "default 4" },
|
|
5707
|
+
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5708
|
+
response_format: RESPONSE_FORMAT
|
|
5333
5709
|
},
|
|
5334
5710
|
["name"]
|
|
5335
5711
|
),
|
|
5336
5712
|
handler: (graph, args) => {
|
|
5337
5713
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5338
5714
|
if (!node) return unresolved(candidates);
|
|
5715
|
+
const changeType = ["modify", "delete", "rename", "add_dependency"].includes(String(args.change_type)) ? String(args.change_type) : "modify";
|
|
5339
5716
|
const r = impactOf(graph, node.id, { depth: numOr(args.depth, 4) });
|
|
5340
|
-
|
|
5717
|
+
const tests = coveringTests(graph, node);
|
|
5718
|
+
const filesAffected = [...new Set(r.affected.map((a) => a.file))];
|
|
5719
|
+
const heavyChange = changeType === "delete" || changeType === "rename";
|
|
5720
|
+
const riskLevel = node.isHub || r.direct >= 10 || r.transitive >= 50 || heavyChange && r.direct >= 3 ? "high" : r.direct >= 3 || r.transitive >= 10 ? "medium" : "low";
|
|
5721
|
+
const summary = `${changeType} ${r.root.name}: ${r.direct} direct dependent${r.direct === 1 ? "" : "s"}, ${r.transitive} transitive across ${filesAffected.length} file${filesAffected.length === 1 ? "" : "s"}; ${tests.length} covering test${tests.length === 1 ? "" : "s"} \u2014 ${riskLevel} risk.`;
|
|
5722
|
+
return {
|
|
5723
|
+
root: r.root.name,
|
|
5724
|
+
changeType,
|
|
5725
|
+
directCallers: r.direct,
|
|
5726
|
+
transitiveCount: r.transitive,
|
|
5727
|
+
filesAffected: filesAffected.slice(0, 20),
|
|
5728
|
+
testsAffected: tests.length,
|
|
5729
|
+
riskLevel,
|
|
5730
|
+
summary,
|
|
5731
|
+
// The full row set is detail; concise callers act on the contract above.
|
|
5732
|
+
...isDetailed(args) ? { affected: r.affected.slice(0, 100).map(stripId) } : r.affected.length > 0 ? { hint: 'response_format:"detailed" lists the affected nodes' } : {}
|
|
5733
|
+
};
|
|
5341
5734
|
}
|
|
5342
5735
|
},
|
|
5736
|
+
{
|
|
5737
|
+
name: "tests_for",
|
|
5738
|
+
description: "Which tests cover this symbol \u2014 is a change here tested?",
|
|
5739
|
+
inputSchema: obj(
|
|
5740
|
+
{
|
|
5741
|
+
name: { type: "string" },
|
|
5742
|
+
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5743
|
+
response_format: RESPONSE_FORMAT
|
|
5744
|
+
},
|
|
5745
|
+
["name"]
|
|
5746
|
+
),
|
|
5747
|
+
handler: (graph, args) => {
|
|
5748
|
+
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5749
|
+
if (!node) return unresolved(candidates);
|
|
5750
|
+
const covers = coveringTests(graph, node);
|
|
5751
|
+
return {
|
|
5752
|
+
node: node.qualifiedName,
|
|
5753
|
+
tested: node.tested,
|
|
5754
|
+
coverage: node.coverage ?? null,
|
|
5755
|
+
testCount: covers.length,
|
|
5756
|
+
covers: isDetailed(args) ? covers : covers.slice(0, 10),
|
|
5757
|
+
...covers.length > 10 && !isDetailed(args) ? { moreAvailable: true } : {},
|
|
5758
|
+
...covers.length === 0 ? { hint: "no covering tests found \u2014 a change here lands untested" } : {}
|
|
5759
|
+
};
|
|
5760
|
+
}
|
|
5761
|
+
},
|
|
5762
|
+
{
|
|
5763
|
+
name: "get_graph_summary",
|
|
5764
|
+
description: "Code map overview: counts, languages, top areas and hubs.",
|
|
5765
|
+
inputSchema: obj({}),
|
|
5766
|
+
handler: (graph) => summarize(graph)
|
|
5767
|
+
},
|
|
5343
5768
|
{
|
|
5344
5769
|
name: "list_areas",
|
|
5345
|
-
description: "
|
|
5770
|
+
description: "Code areas (communities) by size.",
|
|
5346
5771
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
5347
5772
|
// Strip `members` (raw content-hash id arrays): a model cannot use them and
|
|
5348
5773
|
// on a large repo they ballooned this result past 20k tokens.
|
|
@@ -5350,23 +5775,13 @@ var TOOLS = [
|
|
|
5350
5775
|
},
|
|
5351
5776
|
{
|
|
5352
5777
|
name: "list_hubs",
|
|
5353
|
-
description: "
|
|
5778
|
+
description: "Most-depended-on symbols.",
|
|
5354
5779
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
5355
5780
|
handler: (graph, args) => graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance).slice(0, numOr(args.limit, 20)).map((n) => ({ name: n.qualifiedName, kind: n.kind, file: n.file, line: n.span.start, importance: n.importance, isHub: n.isHub }))
|
|
5356
5781
|
},
|
|
5357
|
-
{
|
|
5358
|
-
name: "tests_for",
|
|
5359
|
-
description: "Which tests cover a node (static call linkage + runtime coverage), with the linkage basis.",
|
|
5360
|
-
inputSchema: obj({ name: { type: "string" }, pick: { type: "number", description: "choose nth candidate when ambiguous" } }, ["name"]),
|
|
5361
|
-
handler: (graph, args) => {
|
|
5362
|
-
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5363
|
-
if (!node) return unresolved(candidates);
|
|
5364
|
-
return { node: node.qualifiedName, tested: node.tested, coverage: node.coverage ?? null, covers: coveringTests(graph, node) };
|
|
5365
|
-
}
|
|
5366
|
-
},
|
|
5367
5782
|
{
|
|
5368
5783
|
name: "get_facts",
|
|
5369
|
-
description: "
|
|
5784
|
+
description: "Deterministic facts for a node (contract/invariant); needs a --deep build.",
|
|
5370
5785
|
inputSchema: obj({ name: { type: "string" } }, ["name"]),
|
|
5371
5786
|
handler: (graph, args) => {
|
|
5372
5787
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
@@ -5377,7 +5792,7 @@ var TOOLS = [
|
|
|
5377
5792
|
},
|
|
5378
5793
|
{
|
|
5379
5794
|
name: "guide_node",
|
|
5380
|
-
description: "Cited
|
|
5795
|
+
description: "Cited standards/practices for a node (OWASP/CWE).",
|
|
5381
5796
|
inputSchema: obj({ name: { type: "string" } }, ["name"]),
|
|
5382
5797
|
handler: (graph, args) => {
|
|
5383
5798
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
@@ -5391,17 +5806,17 @@ var TOOLS = [
|
|
|
5391
5806
|
},
|
|
5392
5807
|
{
|
|
5393
5808
|
name: "check_drift",
|
|
5394
|
-
description:
|
|
5809
|
+
description: "Offline dependency inventory (npm/pypi/go); attribute:true adds git who-added attribution.",
|
|
5395
5810
|
inputSchema: obj(
|
|
5396
|
-
{ attribute: { type: "boolean"
|
|
5811
|
+
{ attribute: { type: "boolean" } },
|
|
5397
5812
|
[]
|
|
5398
5813
|
),
|
|
5399
5814
|
handler: (_graph, args, ctx) => attributedInventory(ctx.root, { attribute: args.attribute === true })
|
|
5400
5815
|
},
|
|
5401
5816
|
{
|
|
5402
5817
|
name: "vuln_attribution",
|
|
5403
|
-
description: "Who introduced each open vulnerability
|
|
5404
|
-
inputSchema: obj({ package: { type: "string", description: "
|
|
5818
|
+
description: "Who introduced each open vulnerability, exposure windows, and CRA remediation metrics (MTTR, SLA breaches). Reads the last `vg scan --vulns`.",
|
|
5819
|
+
inputSchema: obj({ package: { type: "string", description: "restrict to one package" } }, []),
|
|
5405
5820
|
handler: (_graph, args, ctx) => {
|
|
5406
5821
|
const data = loadVulnerabilities(ctx.root);
|
|
5407
5822
|
if (!data) {
|
|
@@ -5430,9 +5845,9 @@ var TOOLS = [
|
|
|
5430
5845
|
},
|
|
5431
5846
|
{
|
|
5432
5847
|
name: "list_vulnerabilities",
|
|
5433
|
-
description: "Known vulnerabilities
|
|
5848
|
+
description: "Known vulnerabilities from the last `vg scan --vulns`: id/CVE, severity, CVSS, fixed version.",
|
|
5434
5849
|
inputSchema: obj(
|
|
5435
|
-
{ severity: { type: "string", enum: ["low", "moderate", "high", "critical"], description: "
|
|
5850
|
+
{ severity: { type: "string", enum: ["low", "moderate", "high", "critical"], description: "minimum severity" } },
|
|
5436
5851
|
[]
|
|
5437
5852
|
),
|
|
5438
5853
|
handler: (_graph, args, ctx) => {
|
|
@@ -5468,11 +5883,11 @@ var TOOLS = [
|
|
|
5468
5883
|
},
|
|
5469
5884
|
{
|
|
5470
5885
|
name: "upgrade_impact",
|
|
5471
|
-
description:
|
|
5886
|
+
description: "What breaks if you upgrade a package: major distance, import blast radius, vulns fixed, recommended posture. changelog:true adds GitHub breaking-change signals (online).",
|
|
5472
5887
|
inputSchema: obj(
|
|
5473
5888
|
{
|
|
5474
|
-
package: { type: "string"
|
|
5475
|
-
changelog: { type: "boolean"
|
|
5889
|
+
package: { type: "string" },
|
|
5890
|
+
changelog: { type: "boolean" }
|
|
5476
5891
|
},
|
|
5477
5892
|
["package"]
|
|
5478
5893
|
),
|
|
@@ -5488,19 +5903,19 @@ var TOOLS = [
|
|
|
5488
5903
|
},
|
|
5489
5904
|
{
|
|
5490
5905
|
name: "list_models",
|
|
5491
|
-
description: "
|
|
5906
|
+
description: "Local models on disk (Ollama / LM Studio / gguf).",
|
|
5492
5907
|
inputSchema: obj({}),
|
|
5493
5908
|
handler: () => ({ models: discoverModels() })
|
|
5494
5909
|
},
|
|
5495
5910
|
{
|
|
5496
5911
|
name: "resolve_library",
|
|
5497
|
-
description: "Resolve a library
|
|
5912
|
+
description: "Resolve a library to its canonical id and the version YOUR project uses (drift-annotated).",
|
|
5498
5913
|
// Canonical §4: `query` (+ optional `context_project`). `name` kept as a back-compat alias.
|
|
5499
5914
|
inputSchema: obj(
|
|
5500
5915
|
{
|
|
5501
|
-
query: { type: "string", description: "library name or
|
|
5502
|
-
name: { type: "string"
|
|
5503
|
-
context_project: { type: "string"
|
|
5916
|
+
query: { type: "string", description: "library name or query" },
|
|
5917
|
+
name: { type: "string" },
|
|
5918
|
+
context_project: { type: "string" }
|
|
5504
5919
|
},
|
|
5505
5920
|
[]
|
|
5506
5921
|
),
|
|
@@ -5543,19 +5958,19 @@ var TOOLS = [
|
|
|
5543
5958
|
},
|
|
5544
5959
|
{
|
|
5545
5960
|
name: "library_docs",
|
|
5546
|
-
description: "Version-correct
|
|
5961
|
+
description: "Version-correct usage docs for a library, sliced to a token budget.",
|
|
5547
5962
|
// Canonical §4: `targetId`/`query`, `verbosity`, `max_tokens`. `name`/`tokens` kept as aliases.
|
|
5548
5963
|
inputSchema: obj(
|
|
5549
5964
|
{
|
|
5550
|
-
targetId: { type: "string", description: "
|
|
5965
|
+
targetId: { type: "string", description: "id from resolve_library" },
|
|
5551
5966
|
query: { type: "string", description: "library name or query" },
|
|
5552
|
-
name: { type: "string"
|
|
5553
|
-
context_project: { type: "string"
|
|
5554
|
-
verbosity: { type: "string", enum: ["concise", "balanced", "exhaustive"]
|
|
5555
|
-
max_tokens: { type: "number", description: "
|
|
5556
|
-
tokens: { type: "number"
|
|
5557
|
-
enterprise_strict: { type: "boolean"
|
|
5558
|
-
follow_up: { type: "boolean"
|
|
5967
|
+
name: { type: "string" },
|
|
5968
|
+
context_project: { type: "string" },
|
|
5969
|
+
verbosity: { type: "string", enum: ["concise", "balanced", "exhaustive"] },
|
|
5970
|
+
max_tokens: { type: "number", description: "token budget" },
|
|
5971
|
+
tokens: { type: "number" },
|
|
5972
|
+
enterprise_strict: { type: "boolean" },
|
|
5973
|
+
follow_up: { type: "boolean" }
|
|
5559
5974
|
},
|
|
5560
5975
|
[]
|
|
5561
5976
|
),
|
|
@@ -5650,15 +6065,15 @@ var TOOLS = [
|
|
|
5650
6065
|
}
|
|
5651
6066
|
];
|
|
5652
6067
|
var VERBOSITY_BUDGET = { concise: 1500, balanced: 4e3, exhaustive: 12e3 };
|
|
5653
|
-
function summarize(graph) {
|
|
6068
|
+
function summarize(graph, top = 10) {
|
|
5654
6069
|
return {
|
|
5655
6070
|
counts: graph.meta.counts,
|
|
5656
6071
|
languages: graph.meta.languages,
|
|
5657
6072
|
cluster: graph.meta.cluster,
|
|
5658
6073
|
resolver: graph.provenance.resolver,
|
|
5659
6074
|
generatedAt: graph.generatedAt,
|
|
5660
|
-
topAreas: [...graph.areas].sort((a, b) => b.size - a.size).slice(0,
|
|
5661
|
-
topHubs: graph.nodes.filter((n) => n.isHub).sort((a, b) => b.importance - a.importance).slice(0,
|
|
6075
|
+
topAreas: [...graph.areas].sort((a, b) => b.size - a.size).slice(0, top).map((a) => ({ id: a.id, label: a.label, size: a.size })),
|
|
6076
|
+
topHubs: graph.nodes.filter((n) => n.isHub).sort((a, b) => b.importance - a.importance).slice(0, top).map((n) => ({ name: n.qualifiedName, file: n.file, importance: n.importance }))
|
|
5662
6077
|
};
|
|
5663
6078
|
}
|
|
5664
6079
|
function stripId(item) {
|
|
@@ -5689,6 +6104,19 @@ function numOrU(v) {
|
|
|
5689
6104
|
function uniqueNames(xs) {
|
|
5690
6105
|
return [...new Set(xs)];
|
|
5691
6106
|
}
|
|
6107
|
+
var HOT_TOOLS = ["orient", "search_symbols", "query_graph", "get_node"];
|
|
6108
|
+
function deferredToolNames() {
|
|
6109
|
+
const hot = new Set(HOT_TOOLS);
|
|
6110
|
+
return TOOLS.map((t) => t.name).filter((n) => !hot.has(n));
|
|
6111
|
+
}
|
|
6112
|
+
function navigationToolsetConfig(serverName = "vibgrate") {
|
|
6113
|
+
return {
|
|
6114
|
+
type: "mcp_toolset",
|
|
6115
|
+
mcp_server_name: serverName,
|
|
6116
|
+
default_config: { defer_loading: true },
|
|
6117
|
+
configs: Object.fromEntries(HOT_TOOLS.map((n) => [n, { defer_loading: false }]))
|
|
6118
|
+
};
|
|
6119
|
+
}
|
|
5692
6120
|
var LEDGER = "savings.jsonl";
|
|
5693
6121
|
var PER_FILE_TOKENS = 400;
|
|
5694
6122
|
function ledgerPath(root) {
|
|
@@ -5811,7 +6239,13 @@ function createServer(source, opts = {}) {
|
|
|
5811
6239
|
const seen = /* @__PURE__ */ new Set();
|
|
5812
6240
|
const server = new Server(
|
|
5813
6241
|
{ name: "vg", version: VERSION },
|
|
5814
|
-
{
|
|
6242
|
+
{
|
|
6243
|
+
capabilities: { tools: {} },
|
|
6244
|
+
// Routing guidance once at the server level (hosts that surface
|
|
6245
|
+
// `instructions` get it at zero per-step schema cost): the flashlight
|
|
6246
|
+
// vs the map.
|
|
6247
|
+
instructions: 'vg is a code map. Use search_symbols to find a known name or literal string fast. Use orient/query_graph for meaning: symptoms, relationships, and what-breaks-if. Responses are concise by default; pass response_format:"detailed" only when a node proves load-bearing. Navigate as little as possible: one good search/query usually locates the code. As soon as you have the file and line, read that file and make the edit \u2014 do not call further graph tools unless the edit fails or the match was wrong.'
|
|
6248
|
+
}
|
|
5815
6249
|
);
|
|
5816
6250
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
5817
6251
|
tools: TOOLS.map((t) => ({
|
|
@@ -5850,6 +6284,7 @@ function createServer(source, opts = {}) {
|
|
|
5850
6284
|
async function serveStdio(graphPath, opts = {}) {
|
|
5851
6285
|
const source = new GraphSource(graphPath, opts.refresh !== false);
|
|
5852
6286
|
const server = createServer(source, opts);
|
|
6287
|
+
warmEmbedderInBackground(opts.local);
|
|
5853
6288
|
await server.connect(new StdioServerTransport());
|
|
5854
6289
|
}
|
|
5855
6290
|
function sleep(ms) {
|
|
@@ -6007,9 +6442,9 @@ function assistantById(id) {
|
|
|
6007
6442
|
}
|
|
6008
6443
|
function detectServeLaunch(which = whichOnPath) {
|
|
6009
6444
|
const vg = which("vg");
|
|
6010
|
-
if (vg &&
|
|
6445
|
+
if (vg && isInstalledOwnBinary(vg)) return { command: "vg", args: ["serve"] };
|
|
6011
6446
|
const vibgrate = which("vibgrate");
|
|
6012
|
-
if (vibgrate &&
|
|
6447
|
+
if (vibgrate && isInstalledOwnBinary(vibgrate)) {
|
|
6013
6448
|
return {
|
|
6014
6449
|
command: "vibgrate",
|
|
6015
6450
|
args: ["serve"],
|
|
@@ -6074,6 +6509,18 @@ function writeFileEnsured(file, content) {
|
|
|
6074
6509
|
fs18.mkdirSync(path18.dirname(file), { recursive: true });
|
|
6075
6510
|
fs18.writeFileSync(file, content);
|
|
6076
6511
|
}
|
|
6512
|
+
function writeNavigationConfig(root) {
|
|
6513
|
+
const rel2 = path18.join(".vibgrate", "mcp-navigation.json");
|
|
6514
|
+
const doc = {
|
|
6515
|
+
_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.",
|
|
6516
|
+
hot_core: [...HOT_TOOLS],
|
|
6517
|
+
deferred: deferredToolNames(),
|
|
6518
|
+
toolset: navigationToolsetConfig()
|
|
6519
|
+
};
|
|
6520
|
+
writeFileEnsured(path18.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
6521
|
+
`);
|
|
6522
|
+
return rel2;
|
|
6523
|
+
}
|
|
6077
6524
|
function upsertMcp(file, target, launch) {
|
|
6078
6525
|
const config = readJson(file);
|
|
6079
6526
|
const entry = mcpServerEntry(launch);
|
|
@@ -6288,6 +6735,6 @@ function spdx(ctx) {
|
|
|
6288
6735
|
return JSON.stringify(doc, null, 2) + "\n";
|
|
6289
6736
|
}
|
|
6290
6737
|
|
|
6291
|
-
export { ASSISTANTS, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, SCHEMA_VERSION, SKIP_DIRS, 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, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, 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, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeSnapshot, writeStoredCredentials };
|
|
6292
|
-
//# sourceMappingURL=chunk-
|
|
6293
|
-
//# sourceMappingURL=chunk-
|
|
6738
|
+
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, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, 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, 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 };
|
|
6739
|
+
//# sourceMappingURL=chunk-JL7FIC6W.js.map
|
|
6740
|
+
//# sourceMappingURL=chunk-JL7FIC6W.js.map
|