@vibgrate/cli 2026.704.1 → 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/dist/baseline-64EUPWUH.js +6 -0
- package/dist/{baseline-FESLZK5O.js.map → baseline-64EUPWUH.js.map} +1 -1
- package/dist/{chunk-YODVLV37.js → chunk-JL7FIC6W.js} +313 -102
- package/dist/chunk-JL7FIC6W.js.map +1 -0
- package/dist/{chunk-W4ANUENJ.js → chunk-WHQRV66O.js} +3 -3
- package/dist/{chunk-W4ANUENJ.js.map → chunk-WHQRV66O.js.map} +1 -1
- package/dist/{chunk-RJHYTD62.js → chunk-YN5OGRWU.js} +3 -3
- package/dist/{chunk-RJHYTD62.js.map → chunk-YN5OGRWU.js.map} +1 -1
- package/dist/cli.js +22 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/baseline-FESLZK5O.js +0 -6
- package/dist/chunk-YODVLV37.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"baseline-64EUPWUH.js"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { langById, shortId, canonicalize, grammarSetVersion,
|
|
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';
|
|
@@ -5409,33 +5409,144 @@ function boundList(items, max) {
|
|
|
5409
5409
|
return { items: items.slice(0, max), total: items.length };
|
|
5410
5410
|
}
|
|
5411
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
|
+
}
|
|
5412
5487
|
|
|
5413
5488
|
// src/mcp/tools.ts
|
|
5414
|
-
var
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
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
|
+
});
|
|
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);
|
|
5418
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
|
+
})();
|
|
5419
5531
|
var obj = (properties, required = []) => ({
|
|
5420
5532
|
type: "object",
|
|
5421
5533
|
properties,
|
|
5422
|
-
required,
|
|
5534
|
+
...required.length ? { required } : {},
|
|
5423
5535
|
additionalProperties: false
|
|
5424
5536
|
});
|
|
5537
|
+
var RESPONSE_FORMAT = { type: "string", enum: ["concise", "detailed"] };
|
|
5538
|
+
var isDetailed = (args) => args.response_format === "detailed";
|
|
5539
|
+
var CONCISE_MATCHES = 5;
|
|
5425
5540
|
var TOOLS = [
|
|
5426
|
-
{
|
|
5427
|
-
name: "get_graph_summary",
|
|
5428
|
-
description: "High-level summary of the code map: counts, languages, clustering, top areas and hubs.",
|
|
5429
|
-
inputSchema: obj({}),
|
|
5430
|
-
handler: (graph) => summarize(graph)
|
|
5431
|
-
},
|
|
5432
5541
|
{
|
|
5433
5542
|
name: "orient",
|
|
5434
|
-
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.",
|
|
5435
5544
|
inputSchema: obj(
|
|
5436
5545
|
{
|
|
5437
|
-
question: { type: "string",
|
|
5438
|
-
|
|
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
|
|
5439
5550
|
},
|
|
5440
5551
|
["question"]
|
|
5441
5552
|
),
|
|
@@ -5443,67 +5554,93 @@ var TOOLS = [
|
|
|
5443
5554
|
const question = String(args.question ?? "");
|
|
5444
5555
|
if (!question) return { error: "bad_request", message: "question is required" };
|
|
5445
5556
|
const budget = numOr(args.budget, 1500);
|
|
5446
|
-
const
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
q2 = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors });
|
|
5452
|
-
mode = `semantic (${embedder.id})`;
|
|
5453
|
-
} else {
|
|
5454
|
-
q2 = queryGraph(graph, question, { budget });
|
|
5455
|
-
}
|
|
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);
|
|
5456
5562
|
let topImpact = null;
|
|
5457
|
-
const top =
|
|
5458
|
-
if (top) {
|
|
5563
|
+
const top = scoped[0]?.node;
|
|
5564
|
+
if (detailed && top) {
|
|
5459
5565
|
const r = impactOf(graph, top.id, { depth: 3 });
|
|
5460
5566
|
topImpact = { node: top.qualifiedName, direct: r.direct, transitive: r.transitive, affected: r.affected.slice(0, 10).map(stripId) };
|
|
5461
5567
|
}
|
|
5568
|
+
const shown = detailed ? scoped : scoped.slice(0, CONCISE_MATCHES);
|
|
5462
5569
|
return {
|
|
5463
|
-
summary: summarize(graph),
|
|
5570
|
+
summary: summarize(graph, detailed ? 10 : CONCISE_MATCHES),
|
|
5464
5571
|
mode,
|
|
5465
|
-
context:
|
|
5466
|
-
|
|
5467
|
-
|
|
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 } : {}
|
|
5468
5581
|
};
|
|
5469
5582
|
}
|
|
5470
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
|
+
},
|
|
5471
5599
|
{
|
|
5472
5600
|
name: "query_graph",
|
|
5473
|
-
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.",
|
|
5474
5602
|
inputSchema: obj(
|
|
5475
5603
|
{
|
|
5476
|
-
question: { type: "string",
|
|
5477
|
-
|
|
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
|
|
5478
5609
|
},
|
|
5479
5610
|
["question"]
|
|
5480
5611
|
),
|
|
5481
5612
|
handler: async (graph, args, ctx) => {
|
|
5482
5613
|
const question = String(args.question ?? "");
|
|
5483
5614
|
const budget = numOr(args.budget, 2e3);
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
if (embedder) {
|
|
5488
|
-
const nodeVectors = await getNodeEmbeddings(graph, embedder, ctx.root);
|
|
5489
|
-
r = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors });
|
|
5490
|
-
mode = `semantic (${embedder.id})`;
|
|
5491
|
-
} else {
|
|
5492
|
-
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" };
|
|
5493
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);
|
|
5494
5622
|
return {
|
|
5495
5623
|
mode,
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
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 } : {}
|
|
5499
5632
|
};
|
|
5500
5633
|
}
|
|
5501
5634
|
},
|
|
5502
5635
|
{
|
|
5503
5636
|
name: "get_node",
|
|
5504
|
-
description: "
|
|
5637
|
+
description: "Inspect one symbol: signature, callers, callees, area. Accepts name, file:line, glob or id.",
|
|
5505
5638
|
inputSchema: obj(
|
|
5506
|
-
{
|
|
5639
|
+
{
|
|
5640
|
+
name: { type: "string" },
|
|
5641
|
+
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5642
|
+
response_format: RESPONSE_FORMAT
|
|
5643
|
+
},
|
|
5507
5644
|
["name"]
|
|
5508
5645
|
),
|
|
5509
5646
|
handler: (graph, args, ctx) => {
|
|
@@ -5523,8 +5660,9 @@ var TOOLS = [
|
|
|
5523
5660
|
if (ctx?.dedup && ctx.seen?.has(node.id)) {
|
|
5524
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 };
|
|
5525
5662
|
}
|
|
5526
|
-
const
|
|
5527
|
-
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);
|
|
5528
5666
|
ctx?.seen?.add(node.id);
|
|
5529
5667
|
return {
|
|
5530
5668
|
...base,
|
|
@@ -5537,13 +5675,13 @@ var TOOLS = [
|
|
|
5537
5675
|
},
|
|
5538
5676
|
{
|
|
5539
5677
|
name: "find_path",
|
|
5540
|
-
description: "Shortest connection
|
|
5678
|
+
description: "Shortest connection from a to b.",
|
|
5541
5679
|
inputSchema: obj(
|
|
5542
5680
|
{
|
|
5543
5681
|
a: { type: "string" },
|
|
5544
5682
|
b: { type: "string" },
|
|
5545
|
-
pick_a: { type: "number"
|
|
5546
|
-
pick_b: { type: "number"
|
|
5683
|
+
pick_a: { type: "number" },
|
|
5684
|
+
pick_b: { type: "number" }
|
|
5547
5685
|
},
|
|
5548
5686
|
["a", "b"]
|
|
5549
5687
|
),
|
|
@@ -5560,25 +5698,76 @@ var TOOLS = [
|
|
|
5560
5698
|
},
|
|
5561
5699
|
{
|
|
5562
5700
|
name: "impact_of",
|
|
5563
|
-
description: "
|
|
5701
|
+
description: "Blast radius of a change: what breaks if this symbol changes \u2014 dependents, files, covering tests, risk.",
|
|
5564
5702
|
inputSchema: obj(
|
|
5565
5703
|
{
|
|
5566
5704
|
name: { type: "string" },
|
|
5567
|
-
|
|
5568
|
-
|
|
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
|
|
5569
5709
|
},
|
|
5570
5710
|
["name"]
|
|
5571
5711
|
),
|
|
5572
5712
|
handler: (graph, args) => {
|
|
5573
5713
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5574
5714
|
if (!node) return unresolved(candidates);
|
|
5715
|
+
const changeType = ["modify", "delete", "rename", "add_dependency"].includes(String(args.change_type)) ? String(args.change_type) : "modify";
|
|
5575
5716
|
const r = impactOf(graph, node.id, { depth: numOr(args.depth, 4) });
|
|
5576
|
-
|
|
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
|
+
};
|
|
5734
|
+
}
|
|
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
|
+
};
|
|
5577
5760
|
}
|
|
5578
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
|
+
},
|
|
5579
5768
|
{
|
|
5580
5769
|
name: "list_areas",
|
|
5581
|
-
description: "
|
|
5770
|
+
description: "Code areas (communities) by size.",
|
|
5582
5771
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
5583
5772
|
// Strip `members` (raw content-hash id arrays): a model cannot use them and
|
|
5584
5773
|
// on a large repo they ballooned this result past 20k tokens.
|
|
@@ -5586,23 +5775,13 @@ var TOOLS = [
|
|
|
5586
5775
|
},
|
|
5587
5776
|
{
|
|
5588
5777
|
name: "list_hubs",
|
|
5589
|
-
description: "
|
|
5778
|
+
description: "Most-depended-on symbols.",
|
|
5590
5779
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
5591
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 }))
|
|
5592
5781
|
},
|
|
5593
|
-
{
|
|
5594
|
-
name: "tests_for",
|
|
5595
|
-
description: "Which tests cover a node (static call linkage + runtime coverage), with the linkage basis.",
|
|
5596
|
-
inputSchema: obj({ name: { type: "string" }, pick: { type: "number", description: "choose nth candidate when ambiguous" } }, ["name"]),
|
|
5597
|
-
handler: (graph, args) => {
|
|
5598
|
-
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5599
|
-
if (!node) return unresolved(candidates);
|
|
5600
|
-
return { node: node.qualifiedName, tested: node.tested, coverage: node.coverage ?? null, covers: coveringTests(graph, node) };
|
|
5601
|
-
}
|
|
5602
|
-
},
|
|
5603
5782
|
{
|
|
5604
5783
|
name: "get_facts",
|
|
5605
|
-
description: "
|
|
5784
|
+
description: "Deterministic facts for a node (contract/invariant); needs a --deep build.",
|
|
5606
5785
|
inputSchema: obj({ name: { type: "string" } }, ["name"]),
|
|
5607
5786
|
handler: (graph, args) => {
|
|
5608
5787
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
@@ -5613,7 +5792,7 @@ var TOOLS = [
|
|
|
5613
5792
|
},
|
|
5614
5793
|
{
|
|
5615
5794
|
name: "guide_node",
|
|
5616
|
-
description: "Cited
|
|
5795
|
+
description: "Cited standards/practices for a node (OWASP/CWE).",
|
|
5617
5796
|
inputSchema: obj({ name: { type: "string" } }, ["name"]),
|
|
5618
5797
|
handler: (graph, args) => {
|
|
5619
5798
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
@@ -5627,17 +5806,17 @@ var TOOLS = [
|
|
|
5627
5806
|
},
|
|
5628
5807
|
{
|
|
5629
5808
|
name: "check_drift",
|
|
5630
|
-
description:
|
|
5809
|
+
description: "Offline dependency inventory (npm/pypi/go); attribute:true adds git who-added attribution.",
|
|
5631
5810
|
inputSchema: obj(
|
|
5632
|
-
{ attribute: { type: "boolean"
|
|
5811
|
+
{ attribute: { type: "boolean" } },
|
|
5633
5812
|
[]
|
|
5634
5813
|
),
|
|
5635
5814
|
handler: (_graph, args, ctx) => attributedInventory(ctx.root, { attribute: args.attribute === true })
|
|
5636
5815
|
},
|
|
5637
5816
|
{
|
|
5638
5817
|
name: "vuln_attribution",
|
|
5639
|
-
description: "Who introduced each open vulnerability
|
|
5640
|
-
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" } }, []),
|
|
5641
5820
|
handler: (_graph, args, ctx) => {
|
|
5642
5821
|
const data = loadVulnerabilities(ctx.root);
|
|
5643
5822
|
if (!data) {
|
|
@@ -5666,9 +5845,9 @@ var TOOLS = [
|
|
|
5666
5845
|
},
|
|
5667
5846
|
{
|
|
5668
5847
|
name: "list_vulnerabilities",
|
|
5669
|
-
description: "Known vulnerabilities
|
|
5848
|
+
description: "Known vulnerabilities from the last `vg scan --vulns`: id/CVE, severity, CVSS, fixed version.",
|
|
5670
5849
|
inputSchema: obj(
|
|
5671
|
-
{ severity: { type: "string", enum: ["low", "moderate", "high", "critical"], description: "
|
|
5850
|
+
{ severity: { type: "string", enum: ["low", "moderate", "high", "critical"], description: "minimum severity" } },
|
|
5672
5851
|
[]
|
|
5673
5852
|
),
|
|
5674
5853
|
handler: (_graph, args, ctx) => {
|
|
@@ -5704,11 +5883,11 @@ var TOOLS = [
|
|
|
5704
5883
|
},
|
|
5705
5884
|
{
|
|
5706
5885
|
name: "upgrade_impact",
|
|
5707
|
-
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).",
|
|
5708
5887
|
inputSchema: obj(
|
|
5709
5888
|
{
|
|
5710
|
-
package: { type: "string"
|
|
5711
|
-
changelog: { type: "boolean"
|
|
5889
|
+
package: { type: "string" },
|
|
5890
|
+
changelog: { type: "boolean" }
|
|
5712
5891
|
},
|
|
5713
5892
|
["package"]
|
|
5714
5893
|
),
|
|
@@ -5724,19 +5903,19 @@ var TOOLS = [
|
|
|
5724
5903
|
},
|
|
5725
5904
|
{
|
|
5726
5905
|
name: "list_models",
|
|
5727
|
-
description: "
|
|
5906
|
+
description: "Local models on disk (Ollama / LM Studio / gguf).",
|
|
5728
5907
|
inputSchema: obj({}),
|
|
5729
5908
|
handler: () => ({ models: discoverModels() })
|
|
5730
5909
|
},
|
|
5731
5910
|
{
|
|
5732
5911
|
name: "resolve_library",
|
|
5733
|
-
description: "Resolve a library
|
|
5912
|
+
description: "Resolve a library to its canonical id and the version YOUR project uses (drift-annotated).",
|
|
5734
5913
|
// Canonical §4: `query` (+ optional `context_project`). `name` kept as a back-compat alias.
|
|
5735
5914
|
inputSchema: obj(
|
|
5736
5915
|
{
|
|
5737
|
-
query: { type: "string", description: "library name or
|
|
5738
|
-
name: { type: "string"
|
|
5739
|
-
context_project: { type: "string"
|
|
5916
|
+
query: { type: "string", description: "library name or query" },
|
|
5917
|
+
name: { type: "string" },
|
|
5918
|
+
context_project: { type: "string" }
|
|
5740
5919
|
},
|
|
5741
5920
|
[]
|
|
5742
5921
|
),
|
|
@@ -5779,19 +5958,19 @@ var TOOLS = [
|
|
|
5779
5958
|
},
|
|
5780
5959
|
{
|
|
5781
5960
|
name: "library_docs",
|
|
5782
|
-
description: "Version-correct
|
|
5961
|
+
description: "Version-correct usage docs for a library, sliced to a token budget.",
|
|
5783
5962
|
// Canonical §4: `targetId`/`query`, `verbosity`, `max_tokens`. `name`/`tokens` kept as aliases.
|
|
5784
5963
|
inputSchema: obj(
|
|
5785
5964
|
{
|
|
5786
|
-
targetId: { type: "string", description: "
|
|
5965
|
+
targetId: { type: "string", description: "id from resolve_library" },
|
|
5787
5966
|
query: { type: "string", description: "library name or query" },
|
|
5788
|
-
name: { type: "string"
|
|
5789
|
-
context_project: { type: "string"
|
|
5790
|
-
verbosity: { type: "string", enum: ["concise", "balanced", "exhaustive"]
|
|
5791
|
-
max_tokens: { type: "number", description: "
|
|
5792
|
-
tokens: { type: "number"
|
|
5793
|
-
enterprise_strict: { type: "boolean"
|
|
5794
|
-
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" }
|
|
5795
5974
|
},
|
|
5796
5975
|
[]
|
|
5797
5976
|
),
|
|
@@ -5886,15 +6065,15 @@ var TOOLS = [
|
|
|
5886
6065
|
}
|
|
5887
6066
|
];
|
|
5888
6067
|
var VERBOSITY_BUDGET = { concise: 1500, balanced: 4e3, exhaustive: 12e3 };
|
|
5889
|
-
function summarize(graph) {
|
|
6068
|
+
function summarize(graph, top = 10) {
|
|
5890
6069
|
return {
|
|
5891
6070
|
counts: graph.meta.counts,
|
|
5892
6071
|
languages: graph.meta.languages,
|
|
5893
6072
|
cluster: graph.meta.cluster,
|
|
5894
6073
|
resolver: graph.provenance.resolver,
|
|
5895
6074
|
generatedAt: graph.generatedAt,
|
|
5896
|
-
topAreas: [...graph.areas].sort((a, b) => b.size - a.size).slice(0,
|
|
5897
|
-
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 }))
|
|
5898
6077
|
};
|
|
5899
6078
|
}
|
|
5900
6079
|
function stripId(item) {
|
|
@@ -5925,6 +6104,19 @@ function numOrU(v) {
|
|
|
5925
6104
|
function uniqueNames(xs) {
|
|
5926
6105
|
return [...new Set(xs)];
|
|
5927
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
|
+
}
|
|
5928
6120
|
var LEDGER = "savings.jsonl";
|
|
5929
6121
|
var PER_FILE_TOKENS = 400;
|
|
5930
6122
|
function ledgerPath(root) {
|
|
@@ -6047,7 +6239,13 @@ function createServer(source, opts = {}) {
|
|
|
6047
6239
|
const seen = /* @__PURE__ */ new Set();
|
|
6048
6240
|
const server = new Server(
|
|
6049
6241
|
{ name: "vg", version: VERSION },
|
|
6050
|
-
{
|
|
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
|
+
}
|
|
6051
6249
|
);
|
|
6052
6250
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
6053
6251
|
tools: TOOLS.map((t) => ({
|
|
@@ -6086,6 +6284,7 @@ function createServer(source, opts = {}) {
|
|
|
6086
6284
|
async function serveStdio(graphPath, opts = {}) {
|
|
6087
6285
|
const source = new GraphSource(graphPath, opts.refresh !== false);
|
|
6088
6286
|
const server = createServer(source, opts);
|
|
6287
|
+
warmEmbedderInBackground(opts.local);
|
|
6089
6288
|
await server.connect(new StdioServerTransport());
|
|
6090
6289
|
}
|
|
6091
6290
|
function sleep(ms) {
|
|
@@ -6310,6 +6509,18 @@ function writeFileEnsured(file, content) {
|
|
|
6310
6509
|
fs18.mkdirSync(path18.dirname(file), { recursive: true });
|
|
6311
6510
|
fs18.writeFileSync(file, content);
|
|
6312
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
|
+
}
|
|
6313
6524
|
function upsertMcp(file, target, launch) {
|
|
6314
6525
|
const config = readJson(file);
|
|
6315
6526
|
const entry = mcpServerEntry(launch);
|
|
@@ -6524,6 +6735,6 @@ function spdx(ctx) {
|
|
|
6524
6735
|
return JSON.stringify(doc, null, 2) + "\n";
|
|
6525
6736
|
}
|
|
6526
6737
|
|
|
6527
|
-
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, writeSnapshot, writeStoredCredentials };
|
|
6528
|
-
//# sourceMappingURL=chunk-
|
|
6529
|
-
//# 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
|