@vibgrate/cli 2026.704.3 → 2026.708.2
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 +90 -4
- package/README.md +1 -1
- package/dist/baseline-AMQ7VEB5.js +6 -0
- package/dist/{baseline-64EUPWUH.js.map → baseline-AMQ7VEB5.js.map} +1 -1
- package/dist/{chunk-JL7FIC6W.js → chunk-AWEAREOE.js} +562 -258
- package/dist/chunk-AWEAREOE.js.map +1 -0
- package/dist/{chunk-WHQRV66O.js → chunk-GHLYQZWT.js} +3 -3
- package/dist/{chunk-WHQRV66O.js.map → chunk-GHLYQZWT.js.map} +1 -1
- package/dist/{chunk-YN5OGRWU.js → chunk-ICBBG6U3.js} +55 -22
- package/dist/chunk-ICBBG6U3.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +1680 -365
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +28 -4
- package/dist/index.js +2 -2
- package/dist/parse-worker.d.ts +1 -1
- package/dist/{types-C6ZyQpa9.d.ts → types-CXnp1uGj.d.ts} +33 -1
- package/package.json +3 -3
- package/dist/baseline-64EUPWUH.js +0 -6
- package/dist/chunk-JL7FIC6W.js.map +0 -1
- package/dist/chunk-YN5OGRWU.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hashString, langById, shortId, canonicalize, grammarSetVersion, hashBytes, langForExtension, setGrammarsOverride, parseSource } from './chunk-X5YT263H.js';
|
|
2
|
-
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-
|
|
2
|
+
import { writeTextFile, pathExists, readJsonFile, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-ICBBG6U3.js';
|
|
3
3
|
import * as fs18 from 'fs';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import * as path18 from 'path';
|
|
@@ -1624,6 +1624,7 @@ function resolve4(parses, resolver) {
|
|
|
1624
1624
|
}
|
|
1625
1625
|
}
|
|
1626
1626
|
const edges = new EdgeSet();
|
|
1627
|
+
const unresolved2 = /* @__PURE__ */ new Map();
|
|
1627
1628
|
const stats = {
|
|
1628
1629
|
callsResolved: 0,
|
|
1629
1630
|
callsUnresolved: 0,
|
|
@@ -1672,6 +1673,7 @@ function resolve4(parses, resolver) {
|
|
|
1672
1673
|
stats.callsResolved++;
|
|
1673
1674
|
} else {
|
|
1674
1675
|
stats.callsUnresolved++;
|
|
1676
|
+
bumpUnresolved(unresolved2, srcId, call.callee, "call", p.rel);
|
|
1675
1677
|
}
|
|
1676
1678
|
}
|
|
1677
1679
|
}
|
|
@@ -1683,6 +1685,7 @@ function resolve4(parses, resolver) {
|
|
|
1683
1685
|
if (!srcId) continue;
|
|
1684
1686
|
const target = resolveType(h.superName, p.rel, p.lang, imported, defsByName);
|
|
1685
1687
|
if (target) edges.add(h.kind, srcId, target.id, "heuristic", 0.85);
|
|
1688
|
+
else bumpUnresolved(unresolved2, srcId, h.superName, h.kind, p.rel);
|
|
1686
1689
|
}
|
|
1687
1690
|
}
|
|
1688
1691
|
const nodes = [...baseNodes.values()].map((n) => ({
|
|
@@ -1693,7 +1696,16 @@ function resolve4(parses, resolver) {
|
|
|
1693
1696
|
isHub: false,
|
|
1694
1697
|
tested: null
|
|
1695
1698
|
}));
|
|
1696
|
-
|
|
1699
|
+
const unresolvedList = [...unresolved2.values()].sort(
|
|
1700
|
+
(a, b) => a.from.localeCompare(b.from) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
|
|
1701
|
+
);
|
|
1702
|
+
return { nodes, edges: edges.toArray(), unresolved: unresolvedList, stats };
|
|
1703
|
+
}
|
|
1704
|
+
function bumpUnresolved(map, from, name, kind, fromRel) {
|
|
1705
|
+
const key = `${from}\0${kind}\0${name}`;
|
|
1706
|
+
const existing = map.get(key);
|
|
1707
|
+
if (existing) existing.count++;
|
|
1708
|
+
else map.set(key, { from, name, kind, count: 1, fromRel });
|
|
1697
1709
|
}
|
|
1698
1710
|
function addNode(map, n) {
|
|
1699
1711
|
if (!map.has(n.id)) map.set(n.id, n);
|
|
@@ -2036,6 +2048,30 @@ function loadCache(root, opts) {
|
|
|
2036
2048
|
};
|
|
2037
2049
|
}
|
|
2038
2050
|
|
|
2051
|
+
// src/engine/epistemic.ts
|
|
2052
|
+
var SEMANTIC_RESOLVERS = /* @__PURE__ */ new Set(["scip", "stackgraph", "tsc"]);
|
|
2053
|
+
function classifyEpistemic(edge, dstKind) {
|
|
2054
|
+
if (SEMANTIC_RESOLVERS.has(edge.resolution)) return "observed";
|
|
2055
|
+
switch (edge.kind) {
|
|
2056
|
+
case "contains":
|
|
2057
|
+
return "observed";
|
|
2058
|
+
case "import":
|
|
2059
|
+
return dstKind === "external" || dstKind === void 0 ? "declared" : "observed";
|
|
2060
|
+
case "test":
|
|
2061
|
+
case "coverage":
|
|
2062
|
+
return "declared";
|
|
2063
|
+
default:
|
|
2064
|
+
return "name-matched";
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
function epistemicBreakdown(edges) {
|
|
2068
|
+
const out = { observed: 0, "name-matched": 0, declared: 0 };
|
|
2069
|
+
for (const e of edges) {
|
|
2070
|
+
if (e.epistemic) out[e.epistemic]++;
|
|
2071
|
+
}
|
|
2072
|
+
return out;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2039
2075
|
// src/engine/build.ts
|
|
2040
2076
|
async function buildGraph(options) {
|
|
2041
2077
|
const start = nowMs();
|
|
@@ -2117,6 +2153,7 @@ async function buildGraph(options) {
|
|
|
2117
2153
|
const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
|
|
2118
2154
|
let edges = resolved.edges;
|
|
2119
2155
|
const resolvers = [...resolved.stats.resolvers];
|
|
2156
|
+
const preciseCoveredFiles = /* @__PURE__ */ new Set();
|
|
2120
2157
|
let tscStats;
|
|
2121
2158
|
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
2159
|
if (limits.tscMaxFiles > 0 && tsFiles.length > limits.tscMaxFiles) {
|
|
@@ -2129,6 +2166,7 @@ async function buildGraph(options) {
|
|
|
2129
2166
|
const res = tsResolveEdges(root, tsFiles, resolved.nodes);
|
|
2130
2167
|
if (res.stats.files > 0) {
|
|
2131
2168
|
edges = mergePreciseEdges(edges, res.edges, res.coveredFiles, nodeFileById);
|
|
2169
|
+
for (const f of res.coveredFiles) preciseCoveredFiles.add(f);
|
|
2132
2170
|
if (!resolvers.includes("tsc")) resolvers.unshift("tsc");
|
|
2133
2171
|
tscStats = res.stats;
|
|
2134
2172
|
}
|
|
@@ -2139,6 +2177,7 @@ async function buildGraph(options) {
|
|
|
2139
2177
|
if (scip) {
|
|
2140
2178
|
const res = scipEdges(scip.index, resolved.nodes, toRepoRel);
|
|
2141
2179
|
edges = mergePreciseEdges(edges, res.edges, res.coveredFiles, nodeFileById);
|
|
2180
|
+
for (const f of res.coveredFiles) preciseCoveredFiles.add(f);
|
|
2142
2181
|
if (!resolvers.includes("scip")) resolvers.unshift("scip");
|
|
2143
2182
|
scipStats = { ...res.stats, tool: scip.tool };
|
|
2144
2183
|
}
|
|
@@ -2151,6 +2190,11 @@ async function buildGraph(options) {
|
|
|
2151
2190
|
const languages = [...new Set(parses.map((p) => p.lang))].sort();
|
|
2152
2191
|
const edgeKinds = [...new Set(analysis.edges.map((e) => e.kind))].sort();
|
|
2153
2192
|
const corpusHash = computeCorpusHash(parses, hashes);
|
|
2193
|
+
const nodeKindById = new Map(analysis.nodes.map((n) => [n.id, n.kind]));
|
|
2194
|
+
for (const e of analysis.edges) {
|
|
2195
|
+
e.epistemic = classifyEpistemic(e, nodeKindById.get(e.dst));
|
|
2196
|
+
}
|
|
2197
|
+
const toolchain = computeToolchain(grammars, resolvers);
|
|
2154
2198
|
const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2155
2199
|
const testsCount = linked.testFiles.length;
|
|
2156
2200
|
const untestedCount = analysis.nodes.filter(
|
|
@@ -2165,7 +2209,8 @@ async function buildGraph(options) {
|
|
|
2165
2209
|
grammars: Object.fromEntries(languages.map((l) => [l, grammars])),
|
|
2166
2210
|
resolver: resolvers,
|
|
2167
2211
|
deep: options.deep ?? false,
|
|
2168
|
-
corpusHash
|
|
2212
|
+
corpusHash,
|
|
2213
|
+
toolchain
|
|
2169
2214
|
},
|
|
2170
2215
|
meta: {
|
|
2171
2216
|
root: path18.basename(root) === "" ? "." : ".",
|
|
@@ -2184,6 +2229,9 @@ async function buildGraph(options) {
|
|
|
2184
2229
|
edges: analysis.edges,
|
|
2185
2230
|
areas: analysis.areas
|
|
2186
2231
|
};
|
|
2232
|
+
const survivingIds = new Set(analysis.nodes.map((n) => n.id));
|
|
2233
|
+
const unknowns = resolved.unresolved.filter((u) => !preciseCoveredFiles.has(u.fromRel) && survivingIds.has(u.from)).map((u) => ({ from: u.from, name: u.name, kind: u.kind, count: u.count }));
|
|
2234
|
+
if (unknowns.length) graph.unknowns = unknowns;
|
|
2187
2235
|
if (options.deep) {
|
|
2188
2236
|
const facts = buildFacts(parses, analysis.nodes, analysis.edges);
|
|
2189
2237
|
if (facts.length) graph.facts = facts;
|
|
@@ -2242,6 +2290,23 @@ function mergePreciseEdges(base, precise, coveredFiles, nodeFileById) {
|
|
|
2242
2290
|
(a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
|
|
2243
2291
|
);
|
|
2244
2292
|
}
|
|
2293
|
+
function computeToolchain(grammars, resolvers) {
|
|
2294
|
+
const sortedResolvers = [...new Set(resolvers)].sort();
|
|
2295
|
+
return {
|
|
2296
|
+
schema: SCHEMA_VERSION,
|
|
2297
|
+
tool: VERSION,
|
|
2298
|
+
grammars,
|
|
2299
|
+
resolvers: sortedResolvers,
|
|
2300
|
+
fingerprint: shortId(
|
|
2301
|
+
canonicalize({
|
|
2302
|
+
schema: SCHEMA_VERSION,
|
|
2303
|
+
tool: VERSION,
|
|
2304
|
+
grammars,
|
|
2305
|
+
resolvers: sortedResolvers
|
|
2306
|
+
})
|
|
2307
|
+
)
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2245
2310
|
function computeCorpusHash(parses, hashes) {
|
|
2246
2311
|
const list = parses.map((p) => [p.rel, hashes.get(p.rel) ?? p.hash]).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
2247
2312
|
return hashString(canonicalize(list));
|
|
@@ -2249,6 +2314,224 @@ function computeCorpusHash(parses, hashes) {
|
|
|
2249
2314
|
function nowMs() {
|
|
2250
2315
|
return Number(process.hrtime.bigint() / 1000n) / 1e3;
|
|
2251
2316
|
}
|
|
2317
|
+
|
|
2318
|
+
// src/engine/report.ts
|
|
2319
|
+
function renderReport(graph) {
|
|
2320
|
+
const { meta, provenance, nodes, edges } = graph;
|
|
2321
|
+
const lines = [];
|
|
2322
|
+
lines.push("# Code Graph Report");
|
|
2323
|
+
lines.push("");
|
|
2324
|
+
lines.push(`Generated by \`vg\` ${provenance.version} \xB7 schema \`${graph.schemaVersion}\``);
|
|
2325
|
+
lines.push("");
|
|
2326
|
+
lines.push("## Summary");
|
|
2327
|
+
lines.push("");
|
|
2328
|
+
lines.push(`- **Nodes:** ${meta.counts.nodes.toLocaleString("en-US")}`);
|
|
2329
|
+
lines.push(`- **Edges:** ${meta.counts.edges.toLocaleString("en-US")}`);
|
|
2330
|
+
lines.push(`- **Areas:** ${meta.counts.areas.toLocaleString("en-US")}`);
|
|
2331
|
+
lines.push(`- **Languages:** ${meta.languages.join(", ") || "\u2014"}`);
|
|
2332
|
+
lines.push(`- **Clustering:** ${meta.cluster}`);
|
|
2333
|
+
lines.push(`- **Resolvers:** ${provenance.resolver.join(", ")}`);
|
|
2334
|
+
lines.push(`- **Edge kinds:** ${meta.edgeKinds.join(", ") || "\u2014"}`);
|
|
2335
|
+
lines.push("");
|
|
2336
|
+
const fileNodes = nodes.filter((n) => n.kind === "file");
|
|
2337
|
+
const defsByFile = /* @__PURE__ */ new Map();
|
|
2338
|
+
for (const n of nodes) {
|
|
2339
|
+
if (n.kind === "file" || n.kind === "external") continue;
|
|
2340
|
+
defsByFile.set(n.file, (defsByFile.get(n.file) ?? 0) + 1);
|
|
2341
|
+
}
|
|
2342
|
+
const topFiles = [...defsByFile.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 15);
|
|
2343
|
+
if (topFiles.length) {
|
|
2344
|
+
lines.push("## Largest files (by definitions)");
|
|
2345
|
+
lines.push("");
|
|
2346
|
+
lines.push("| File | Definitions |");
|
|
2347
|
+
lines.push("| --- | ---: |");
|
|
2348
|
+
for (const [file, count] of topFiles) lines.push(`| \`${file}\` | ${count} |`);
|
|
2349
|
+
lines.push("");
|
|
2350
|
+
}
|
|
2351
|
+
const topNodes = nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 15);
|
|
2352
|
+
if (topNodes.length) {
|
|
2353
|
+
lines.push("## Most-connected definitions");
|
|
2354
|
+
lines.push("");
|
|
2355
|
+
lines.push("| Symbol | Kind | File | Importance |");
|
|
2356
|
+
lines.push("| --- | --- | --- | ---: |");
|
|
2357
|
+
for (const n of topNodes) {
|
|
2358
|
+
lines.push(
|
|
2359
|
+
`| \`${n.qualifiedName}\` | ${n.kind} | \`${n.file}\`:${n.span.start} | ${n.importance.toFixed(3)} |`
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
lines.push("");
|
|
2363
|
+
}
|
|
2364
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
2365
|
+
for (const e of edges) byKind.set(e.kind, (byKind.get(e.kind) ?? 0) + 1);
|
|
2366
|
+
if (byKind.size) {
|
|
2367
|
+
lines.push("## Edges by kind");
|
|
2368
|
+
lines.push("");
|
|
2369
|
+
lines.push("| Kind | Count |");
|
|
2370
|
+
lines.push("| --- | ---: |");
|
|
2371
|
+
for (const [kind, count] of [...byKind.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
2372
|
+
lines.push(`| ${kind} | ${count} |`);
|
|
2373
|
+
}
|
|
2374
|
+
lines.push("");
|
|
2375
|
+
}
|
|
2376
|
+
lines.push("---");
|
|
2377
|
+
lines.push("");
|
|
2378
|
+
lines.push(`_Files: ${fileNodes.length} \xB7 Corpus: \`${provenance.corpusHash.slice(0, 16)}\u2026\`_`);
|
|
2379
|
+
lines.push("");
|
|
2380
|
+
return lines.join("\n");
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
// src/engine/html.ts
|
|
2384
|
+
function renderHtml(graph) {
|
|
2385
|
+
const top = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 50);
|
|
2386
|
+
const rows = top.map(
|
|
2387
|
+
(n) => `<tr><td><code>${esc(n.qualifiedName)}</code></td><td>${esc(n.kind)}</td><td><code>${esc(n.file)}</code>:${n.span.start}</td><td>${n.importance.toFixed(3)}</td></tr>`
|
|
2388
|
+
).join("\n");
|
|
2389
|
+
const embedded = stableStringify(graph, 0);
|
|
2390
|
+
return `<!doctype html>
|
|
2391
|
+
<html lang="en">
|
|
2392
|
+
<head>
|
|
2393
|
+
<meta charset="utf-8">
|
|
2394
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2395
|
+
<title>vg \xB7 code graph</title>
|
|
2396
|
+
<style>
|
|
2397
|
+
:root { color-scheme: light dark; }
|
|
2398
|
+
body { font: 14px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; }
|
|
2399
|
+
h1 { font-size: 1.4rem; } h2 { font-size: 1.1rem; margin-top: 2rem; }
|
|
2400
|
+
table { border-collapse: collapse; width: 100%; }
|
|
2401
|
+
th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #8884; }
|
|
2402
|
+
td:last-child, th:last-child { text-align: right; }
|
|
2403
|
+
.meta { color: #888; } code { font-size: 0.9em; }
|
|
2404
|
+
</style>
|
|
2405
|
+
</head>
|
|
2406
|
+
<body>
|
|
2407
|
+
<h1>vg \xB7 code graph</h1>
|
|
2408
|
+
<p class="meta">${graph.meta.counts.nodes} nodes \xB7 ${graph.meta.counts.edges} edges \xB7
|
|
2409
|
+
${graph.meta.counts.areas} areas \xB7 ${esc(graph.meta.languages.join(", ") || "\u2014")} \xB7
|
|
2410
|
+
clustering: ${esc(graph.meta.cluster)} \xB7 vg ${esc(graph.provenance.version)}</p>
|
|
2411
|
+
<h2>Most-connected definitions</h2>
|
|
2412
|
+
<table>
|
|
2413
|
+
<thead><tr><th>Symbol</th><th>Kind</th><th>Location</th><th>Importance</th></tr></thead>
|
|
2414
|
+
<tbody>
|
|
2415
|
+
${rows}
|
|
2416
|
+
</tbody>
|
|
2417
|
+
</table>
|
|
2418
|
+
<p class="meta">Interactive WebGL view arrives in Phase 1. The full graph is embedded below for tooling.</p>
|
|
2419
|
+
<script type="application/json" id="vg-graph">
|
|
2420
|
+
${embedded}
|
|
2421
|
+
</script>
|
|
2422
|
+
</body>
|
|
2423
|
+
</html>
|
|
2424
|
+
`;
|
|
2425
|
+
}
|
|
2426
|
+
function esc(s) {
|
|
2427
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2428
|
+
}
|
|
2429
|
+
function vibgrateDir(root) {
|
|
2430
|
+
return path18.join(root, ".vibgrate");
|
|
2431
|
+
}
|
|
2432
|
+
function defaultGraphPath(root) {
|
|
2433
|
+
return path18.join(vibgrateDir(root), "graph.json");
|
|
2434
|
+
}
|
|
2435
|
+
function writeArtifacts(graph, options) {
|
|
2436
|
+
const dir = vibgrateDir(options.root);
|
|
2437
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
2438
|
+
const graphPath = options.graphPath ?? defaultGraphPath(options.root);
|
|
2439
|
+
fs18.mkdirSync(path18.dirname(graphPath), { recursive: true });
|
|
2440
|
+
const tmp = `${graphPath}.${process.pid}.tmp`;
|
|
2441
|
+
try {
|
|
2442
|
+
fs18.writeFileSync(tmp, serializeGraph(graph));
|
|
2443
|
+
fs18.renameSync(tmp, graphPath);
|
|
2444
|
+
} catch (err) {
|
|
2445
|
+
fs18.rmSync(tmp, { force: true });
|
|
2446
|
+
throw err;
|
|
2447
|
+
}
|
|
2448
|
+
const written = { graphPath };
|
|
2449
|
+
if (options.report !== false) {
|
|
2450
|
+
const reportPath = path18.join(dir, "GRAPH_REPORT.md");
|
|
2451
|
+
fs18.writeFileSync(reportPath, renderReport(graph));
|
|
2452
|
+
written.reportPath = reportPath;
|
|
2453
|
+
}
|
|
2454
|
+
if (options.html !== false) {
|
|
2455
|
+
const htmlPath = path18.join(dir, "graph.html");
|
|
2456
|
+
fs18.writeFileSync(htmlPath, renderHtml(graph));
|
|
2457
|
+
written.htmlPath = htmlPath;
|
|
2458
|
+
}
|
|
2459
|
+
if (graph.facts && graph.facts.length) {
|
|
2460
|
+
const factsPath = path18.join(dir, "facts.jsonl");
|
|
2461
|
+
fs18.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
|
|
2462
|
+
written.factsPath = factsPath;
|
|
2463
|
+
}
|
|
2464
|
+
return written;
|
|
2465
|
+
}
|
|
2466
|
+
function loadGraph(root, graphPath) {
|
|
2467
|
+
const file = graphPath ?? defaultGraphPath(root);
|
|
2468
|
+
if (!fs18.existsSync(file)) return null;
|
|
2469
|
+
return parseGraph(fs18.readFileSync(file, "utf8"));
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// src/engine/verify.ts
|
|
2473
|
+
var PINNED = "1970-01-01T00:00:00.000Z";
|
|
2474
|
+
async function verifyDeterminism(opts) {
|
|
2475
|
+
const base = { root: opts.root, only: opts.only, exclude: opts.exclude, generatedAt: PINNED };
|
|
2476
|
+
const buildA = await buildGraph({ ...base, noCache: true, jobs: opts.jobs });
|
|
2477
|
+
const a = serializeGraph(buildA.graph);
|
|
2478
|
+
const b = serializeGraph((await buildGraph({ ...base, noCache: true, jobs: 1 })).graph);
|
|
2479
|
+
const cached2 = serializeGraph((await buildGraph({ ...base, noCache: false })).graph);
|
|
2480
|
+
const checks = [
|
|
2481
|
+
{ name: "run-to-run determinism", ok: a === b, detail: diffHint(a, b) },
|
|
2482
|
+
{ name: "cache safety (incremental == full)", ok: a === cached2, detail: diffHint(a, cached2) }
|
|
2483
|
+
];
|
|
2484
|
+
const scoped = Boolean(opts.only?.length || opts.exclude?.length);
|
|
2485
|
+
const committed = scoped ? null : loadGraph(opts.root);
|
|
2486
|
+
const committedFp = committed?.provenance.toolchain?.fingerprint;
|
|
2487
|
+
const currentFp = buildA.graph.provenance.toolchain?.fingerprint;
|
|
2488
|
+
if (committedFp && currentFp) {
|
|
2489
|
+
const ok = committedFp === currentFp;
|
|
2490
|
+
checks.push({
|
|
2491
|
+
name: "toolchain fingerprint matches committed graph",
|
|
2492
|
+
ok,
|
|
2493
|
+
detail: ok ? void 0 : `committed toolchain ${committedFp} != current ${currentFp} (grammar/resolver versions differ \u2014 rebuild or align the toolchain)`
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
return {
|
|
2497
|
+
ok: checks.every((c) => c.ok),
|
|
2498
|
+
checks,
|
|
2499
|
+
digest: hashString(a)
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function diffHint(x, y) {
|
|
2503
|
+
if (x === y) return void 0;
|
|
2504
|
+
const xl = x.split("\n");
|
|
2505
|
+
const yl = y.split("\n");
|
|
2506
|
+
const n = Math.max(xl.length, yl.length);
|
|
2507
|
+
for (let i = 0; i < n; i++) {
|
|
2508
|
+
if (xl[i] !== yl[i]) {
|
|
2509
|
+
return `first divergence at line ${i + 1}`;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
return "outputs differ in length";
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
// src/util/exit.ts
|
|
2516
|
+
var ExitCode = {
|
|
2517
|
+
OK: 0,
|
|
2518
|
+
ERROR: 1,
|
|
2519
|
+
GATE_FAILED: 2,
|
|
2520
|
+
NOT_FOUND: 3,
|
|
2521
|
+
NON_DETERMINISTIC: 4,
|
|
2522
|
+
USAGE_ERROR: 5
|
|
2523
|
+
};
|
|
2524
|
+
var CliError = class extends Error {
|
|
2525
|
+
code;
|
|
2526
|
+
constructor(message, code = ExitCode.ERROR) {
|
|
2527
|
+
super(message);
|
|
2528
|
+
this.name = "CliError";
|
|
2529
|
+
this.code = code;
|
|
2530
|
+
}
|
|
2531
|
+
};
|
|
2532
|
+
function usageError(message) {
|
|
2533
|
+
return new CliError(message, ExitCode.USAGE_ERROR);
|
|
2534
|
+
}
|
|
2252
2535
|
var DEFAULT_LOCK_STALE_MS = 15 * 60 * 1e3;
|
|
2253
2536
|
function isProcessAlive(pid) {
|
|
2254
2537
|
try {
|
|
@@ -2574,155 +2857,6 @@ async function getNodeEmbeddings(graph, embedder, root, onProgress) {
|
|
|
2574
2857
|
function safe(id) {
|
|
2575
2858
|
return id.replace(/[^a-z0-9.-]+/gi, "_");
|
|
2576
2859
|
}
|
|
2577
|
-
|
|
2578
|
-
// src/engine/report.ts
|
|
2579
|
-
function renderReport(graph) {
|
|
2580
|
-
const { meta, provenance, nodes, edges } = graph;
|
|
2581
|
-
const lines = [];
|
|
2582
|
-
lines.push("# Code Graph Report");
|
|
2583
|
-
lines.push("");
|
|
2584
|
-
lines.push(`Generated by \`vg\` ${provenance.version} \xB7 schema \`${graph.schemaVersion}\``);
|
|
2585
|
-
lines.push("");
|
|
2586
|
-
lines.push("## Summary");
|
|
2587
|
-
lines.push("");
|
|
2588
|
-
lines.push(`- **Nodes:** ${meta.counts.nodes.toLocaleString("en-US")}`);
|
|
2589
|
-
lines.push(`- **Edges:** ${meta.counts.edges.toLocaleString("en-US")}`);
|
|
2590
|
-
lines.push(`- **Areas:** ${meta.counts.areas.toLocaleString("en-US")}`);
|
|
2591
|
-
lines.push(`- **Languages:** ${meta.languages.join(", ") || "\u2014"}`);
|
|
2592
|
-
lines.push(`- **Clustering:** ${meta.cluster}`);
|
|
2593
|
-
lines.push(`- **Resolvers:** ${provenance.resolver.join(", ")}`);
|
|
2594
|
-
lines.push(`- **Edge kinds:** ${meta.edgeKinds.join(", ") || "\u2014"}`);
|
|
2595
|
-
lines.push("");
|
|
2596
|
-
const fileNodes = nodes.filter((n) => n.kind === "file");
|
|
2597
|
-
const defsByFile = /* @__PURE__ */ new Map();
|
|
2598
|
-
for (const n of nodes) {
|
|
2599
|
-
if (n.kind === "file" || n.kind === "external") continue;
|
|
2600
|
-
defsByFile.set(n.file, (defsByFile.get(n.file) ?? 0) + 1);
|
|
2601
|
-
}
|
|
2602
|
-
const topFiles = [...defsByFile.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 15);
|
|
2603
|
-
if (topFiles.length) {
|
|
2604
|
-
lines.push("## Largest files (by definitions)");
|
|
2605
|
-
lines.push("");
|
|
2606
|
-
lines.push("| File | Definitions |");
|
|
2607
|
-
lines.push("| --- | ---: |");
|
|
2608
|
-
for (const [file, count] of topFiles) lines.push(`| \`${file}\` | ${count} |`);
|
|
2609
|
-
lines.push("");
|
|
2610
|
-
}
|
|
2611
|
-
const topNodes = nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 15);
|
|
2612
|
-
if (topNodes.length) {
|
|
2613
|
-
lines.push("## Most-connected definitions");
|
|
2614
|
-
lines.push("");
|
|
2615
|
-
lines.push("| Symbol | Kind | File | Importance |");
|
|
2616
|
-
lines.push("| --- | --- | --- | ---: |");
|
|
2617
|
-
for (const n of topNodes) {
|
|
2618
|
-
lines.push(
|
|
2619
|
-
`| \`${n.qualifiedName}\` | ${n.kind} | \`${n.file}\`:${n.span.start} | ${n.importance.toFixed(3)} |`
|
|
2620
|
-
);
|
|
2621
|
-
}
|
|
2622
|
-
lines.push("");
|
|
2623
|
-
}
|
|
2624
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
2625
|
-
for (const e of edges) byKind.set(e.kind, (byKind.get(e.kind) ?? 0) + 1);
|
|
2626
|
-
if (byKind.size) {
|
|
2627
|
-
lines.push("## Edges by kind");
|
|
2628
|
-
lines.push("");
|
|
2629
|
-
lines.push("| Kind | Count |");
|
|
2630
|
-
lines.push("| --- | ---: |");
|
|
2631
|
-
for (const [kind, count] of [...byKind.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
2632
|
-
lines.push(`| ${kind} | ${count} |`);
|
|
2633
|
-
}
|
|
2634
|
-
lines.push("");
|
|
2635
|
-
}
|
|
2636
|
-
lines.push("---");
|
|
2637
|
-
lines.push("");
|
|
2638
|
-
lines.push(`_Files: ${fileNodes.length} \xB7 Corpus: \`${provenance.corpusHash.slice(0, 16)}\u2026\`_`);
|
|
2639
|
-
lines.push("");
|
|
2640
|
-
return lines.join("\n");
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
|
-
// src/engine/html.ts
|
|
2644
|
-
function renderHtml(graph) {
|
|
2645
|
-
const top = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, 50);
|
|
2646
|
-
const rows = top.map(
|
|
2647
|
-
(n) => `<tr><td><code>${esc(n.qualifiedName)}</code></td><td>${esc(n.kind)}</td><td><code>${esc(n.file)}</code>:${n.span.start}</td><td>${n.importance.toFixed(3)}</td></tr>`
|
|
2648
|
-
).join("\n");
|
|
2649
|
-
const embedded = stableStringify(graph, 0);
|
|
2650
|
-
return `<!doctype html>
|
|
2651
|
-
<html lang="en">
|
|
2652
|
-
<head>
|
|
2653
|
-
<meta charset="utf-8">
|
|
2654
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2655
|
-
<title>vg \xB7 code graph</title>
|
|
2656
|
-
<style>
|
|
2657
|
-
:root { color-scheme: light dark; }
|
|
2658
|
-
body { font: 14px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 60rem; padding: 0 1rem; }
|
|
2659
|
-
h1 { font-size: 1.4rem; } h2 { font-size: 1.1rem; margin-top: 2rem; }
|
|
2660
|
-
table { border-collapse: collapse; width: 100%; }
|
|
2661
|
-
th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #8884; }
|
|
2662
|
-
td:last-child, th:last-child { text-align: right; }
|
|
2663
|
-
.meta { color: #888; } code { font-size: 0.9em; }
|
|
2664
|
-
</style>
|
|
2665
|
-
</head>
|
|
2666
|
-
<body>
|
|
2667
|
-
<h1>vg \xB7 code graph</h1>
|
|
2668
|
-
<p class="meta">${graph.meta.counts.nodes} nodes \xB7 ${graph.meta.counts.edges} edges \xB7
|
|
2669
|
-
${graph.meta.counts.areas} areas \xB7 ${esc(graph.meta.languages.join(", ") || "\u2014")} \xB7
|
|
2670
|
-
clustering: ${esc(graph.meta.cluster)} \xB7 vg ${esc(graph.provenance.version)}</p>
|
|
2671
|
-
<h2>Most-connected definitions</h2>
|
|
2672
|
-
<table>
|
|
2673
|
-
<thead><tr><th>Symbol</th><th>Kind</th><th>Location</th><th>Importance</th></tr></thead>
|
|
2674
|
-
<tbody>
|
|
2675
|
-
${rows}
|
|
2676
|
-
</tbody>
|
|
2677
|
-
</table>
|
|
2678
|
-
<p class="meta">Interactive WebGL view arrives in Phase 1. The full graph is embedded below for tooling.</p>
|
|
2679
|
-
<script type="application/json" id="vg-graph">
|
|
2680
|
-
${embedded}
|
|
2681
|
-
</script>
|
|
2682
|
-
</body>
|
|
2683
|
-
</html>
|
|
2684
|
-
`;
|
|
2685
|
-
}
|
|
2686
|
-
function esc(s) {
|
|
2687
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2688
|
-
}
|
|
2689
|
-
function vibgrateDir(root) {
|
|
2690
|
-
return path18.join(root, ".vibgrate");
|
|
2691
|
-
}
|
|
2692
|
-
function defaultGraphPath(root) {
|
|
2693
|
-
return path18.join(vibgrateDir(root), "graph.json");
|
|
2694
|
-
}
|
|
2695
|
-
function writeArtifacts(graph, options) {
|
|
2696
|
-
const dir = vibgrateDir(options.root);
|
|
2697
|
-
fs18.mkdirSync(dir, { recursive: true });
|
|
2698
|
-
const graphPath = options.graphPath ?? defaultGraphPath(options.root);
|
|
2699
|
-
fs18.mkdirSync(path18.dirname(graphPath), { recursive: true });
|
|
2700
|
-
const tmp = `${graphPath}.${process.pid}.tmp`;
|
|
2701
|
-
try {
|
|
2702
|
-
fs18.writeFileSync(tmp, serializeGraph(graph));
|
|
2703
|
-
fs18.renameSync(tmp, graphPath);
|
|
2704
|
-
} catch (err) {
|
|
2705
|
-
fs18.rmSync(tmp, { force: true });
|
|
2706
|
-
throw err;
|
|
2707
|
-
}
|
|
2708
|
-
const written = { graphPath };
|
|
2709
|
-
if (options.report !== false) {
|
|
2710
|
-
const reportPath = path18.join(dir, "GRAPH_REPORT.md");
|
|
2711
|
-
fs18.writeFileSync(reportPath, renderReport(graph));
|
|
2712
|
-
written.reportPath = reportPath;
|
|
2713
|
-
}
|
|
2714
|
-
if (options.html !== false) {
|
|
2715
|
-
const htmlPath = path18.join(dir, "graph.html");
|
|
2716
|
-
fs18.writeFileSync(htmlPath, renderHtml(graph));
|
|
2717
|
-
written.htmlPath = htmlPath;
|
|
2718
|
-
}
|
|
2719
|
-
if (graph.facts && graph.facts.length) {
|
|
2720
|
-
const factsPath = path18.join(dir, "facts.jsonl");
|
|
2721
|
-
fs18.writeFileSync(factsPath, graph.facts.map((f) => JSON.stringify(f)).join("\n") + "\n");
|
|
2722
|
-
written.factsPath = factsPath;
|
|
2723
|
-
}
|
|
2724
|
-
return written;
|
|
2725
|
-
}
|
|
2726
2860
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2727
2861
|
function snapshotPath(root) {
|
|
2728
2862
|
return path18.join(cacheDir(root), "freshness.json");
|
|
@@ -2824,62 +2958,6 @@ function pruneScope(scope) {
|
|
|
2824
2958
|
return out;
|
|
2825
2959
|
}
|
|
2826
2960
|
|
|
2827
|
-
// src/util/exit.ts
|
|
2828
|
-
var ExitCode = {
|
|
2829
|
-
OK: 0,
|
|
2830
|
-
ERROR: 1,
|
|
2831
|
-
GATE_FAILED: 2,
|
|
2832
|
-
NOT_FOUND: 3,
|
|
2833
|
-
NON_DETERMINISTIC: 4,
|
|
2834
|
-
USAGE_ERROR: 5
|
|
2835
|
-
};
|
|
2836
|
-
var CliError = class extends Error {
|
|
2837
|
-
code;
|
|
2838
|
-
constructor(message, code = ExitCode.ERROR) {
|
|
2839
|
-
super(message);
|
|
2840
|
-
this.name = "CliError";
|
|
2841
|
-
this.code = code;
|
|
2842
|
-
}
|
|
2843
|
-
};
|
|
2844
|
-
function usageError(message) {
|
|
2845
|
-
return new CliError(message, ExitCode.USAGE_ERROR);
|
|
2846
|
-
}
|
|
2847
|
-
function loadGraph(root, graphPath) {
|
|
2848
|
-
const file = graphPath ?? defaultGraphPath(root);
|
|
2849
|
-
if (!fs18.existsSync(file)) return null;
|
|
2850
|
-
return parseGraph(fs18.readFileSync(file, "utf8"));
|
|
2851
|
-
}
|
|
2852
|
-
|
|
2853
|
-
// src/engine/verify.ts
|
|
2854
|
-
var PINNED = "1970-01-01T00:00:00.000Z";
|
|
2855
|
-
async function verifyDeterminism(opts) {
|
|
2856
|
-
const base = { root: opts.root, only: opts.only, exclude: opts.exclude, generatedAt: PINNED };
|
|
2857
|
-
const a = serializeGraph((await buildGraph({ ...base, noCache: true, jobs: opts.jobs })).graph);
|
|
2858
|
-
const b = serializeGraph((await buildGraph({ ...base, noCache: true, jobs: 1 })).graph);
|
|
2859
|
-
const cached2 = serializeGraph((await buildGraph({ ...base, noCache: false })).graph);
|
|
2860
|
-
const checks = [
|
|
2861
|
-
{ name: "run-to-run determinism", ok: a === b, detail: diffHint(a, b) },
|
|
2862
|
-
{ name: "cache safety (incremental == full)", ok: a === cached2, detail: diffHint(a, cached2) }
|
|
2863
|
-
];
|
|
2864
|
-
return {
|
|
2865
|
-
ok: checks.every((c) => c.ok),
|
|
2866
|
-
checks,
|
|
2867
|
-
digest: hashString(a)
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
function diffHint(x, y) {
|
|
2871
|
-
if (x === y) return void 0;
|
|
2872
|
-
const xl = x.split("\n");
|
|
2873
|
-
const yl = y.split("\n");
|
|
2874
|
-
const n = Math.max(xl.length, yl.length);
|
|
2875
|
-
for (let i = 0; i < n; i++) {
|
|
2876
|
-
if (xl[i] !== yl[i]) {
|
|
2877
|
-
return `first divergence at line ${i + 1}`;
|
|
2878
|
-
}
|
|
2879
|
-
}
|
|
2880
|
-
return "outputs differ in length";
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
2961
|
// src/engine/relations.ts
|
|
2884
2962
|
var GraphIndex = class {
|
|
2885
2963
|
constructor(graph) {
|
|
@@ -2976,12 +3054,13 @@ function queryGraph(graph, question, options = {}) {
|
|
|
2976
3054
|
const budget = options.budget ?? 2e3;
|
|
2977
3055
|
const limit = options.limit ?? 12;
|
|
2978
3056
|
const terms = tokenize(question);
|
|
3057
|
+
const weightOf = termWeights(graph, terms);
|
|
2979
3058
|
const index = new GraphIndex(graph);
|
|
2980
3059
|
const scored = [];
|
|
2981
3060
|
for (const node of graph.nodes) {
|
|
2982
3061
|
if (node.kind === "file" || node.kind === "external") continue;
|
|
2983
|
-
const { score, why } = scoreNode(node, terms);
|
|
2984
|
-
if (score > 0) scored.push({ node, score: round2(score * (1 + node.importance)), why });
|
|
3062
|
+
const { score, why } = scoreNode(node, terms, weightOf);
|
|
3063
|
+
if (score > 0) scored.push({ node, score: round2(score * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
|
|
2985
3064
|
}
|
|
2986
3065
|
scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
|
|
2987
3066
|
const seeds = scored.slice(0, limit);
|
|
@@ -2992,6 +3071,7 @@ async function queryGraphSemantic(graph, question, options) {
|
|
|
2992
3071
|
const budget = options.budget ?? 2e3;
|
|
2993
3072
|
const limit = options.limit ?? 12;
|
|
2994
3073
|
const terms = tokenize(question);
|
|
3074
|
+
const weightOf = termWeights(graph, terms);
|
|
2995
3075
|
const index = new GraphIndex(graph);
|
|
2996
3076
|
const queryVec = await options.embedder.embedQuery(question);
|
|
2997
3077
|
const lexRaw = /* @__PURE__ */ new Map();
|
|
@@ -2999,7 +3079,7 @@ async function queryGraphSemantic(graph, question, options) {
|
|
|
2999
3079
|
const whyById = /* @__PURE__ */ new Map();
|
|
3000
3080
|
for (const node of graph.nodes) {
|
|
3001
3081
|
if (node.kind === "file" || node.kind === "external") continue;
|
|
3002
|
-
const { score, why } = scoreNode(node, terms);
|
|
3082
|
+
const { score, why } = scoreNode(node, terms, weightOf);
|
|
3003
3083
|
lexRaw.set(node.id, score);
|
|
3004
3084
|
whyById.set(node.id, why);
|
|
3005
3085
|
if (score > lexMax) lexMax = score;
|
|
@@ -3014,7 +3094,7 @@ async function queryGraphSemantic(graph, question, options) {
|
|
|
3014
3094
|
if (hybrid <= 0) continue;
|
|
3015
3095
|
const lexWhy = whyById.get(node.id);
|
|
3016
3096
|
const why = lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : "weak match");
|
|
3017
|
-
scored.push({ node, score: round2(hybrid * (1 + node.importance)), why });
|
|
3097
|
+
scored.push({ node, score: round2(hybrid * (1 + IMPORTANCE_WEIGHT * node.importance)), why });
|
|
3018
3098
|
}
|
|
3019
3099
|
scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));
|
|
3020
3100
|
const seeds = scored.slice(0, limit);
|
|
@@ -3028,7 +3108,7 @@ function tokenize(q2) {
|
|
|
3028
3108
|
)
|
|
3029
3109
|
];
|
|
3030
3110
|
}
|
|
3031
|
-
function scoreNode(node, terms) {
|
|
3111
|
+
function scoreNode(node, terms, weightOf = () => 1) {
|
|
3032
3112
|
let score = 0;
|
|
3033
3113
|
const hits = [];
|
|
3034
3114
|
const name = node.name.toLowerCase();
|
|
@@ -3036,28 +3116,50 @@ function scoreNode(node, terms) {
|
|
|
3036
3116
|
const file = node.file.toLowerCase();
|
|
3037
3117
|
const nameParts = identifierParts(node.name);
|
|
3038
3118
|
for (const t of terms) {
|
|
3119
|
+
const w = weightOf(t);
|
|
3039
3120
|
if (name === t) {
|
|
3040
|
-
score += 10;
|
|
3121
|
+
score += 10 * w;
|
|
3041
3122
|
hits.push(t);
|
|
3042
3123
|
} else if (nameParts.has(t)) {
|
|
3043
|
-
score += 6;
|
|
3124
|
+
score += 6 * w;
|
|
3044
3125
|
hits.push(t);
|
|
3045
3126
|
} else if (name.includes(t)) {
|
|
3046
|
-
score += 4;
|
|
3127
|
+
score += 4 * w;
|
|
3047
3128
|
hits.push(t);
|
|
3048
3129
|
} else if (qn.includes(t)) {
|
|
3049
|
-
score += 3;
|
|
3130
|
+
score += 3 * w;
|
|
3050
3131
|
hits.push(t);
|
|
3051
3132
|
} else if (fuzzyPartMatch(t, nameParts)) {
|
|
3052
|
-
score += 2;
|
|
3133
|
+
score += 2 * w;
|
|
3053
3134
|
hits.push(`~${t}`);
|
|
3054
3135
|
} else if (file.includes(t)) {
|
|
3055
|
-
score += 1;
|
|
3136
|
+
score += 1 * w;
|
|
3056
3137
|
hits.push(t);
|
|
3057
3138
|
}
|
|
3058
3139
|
}
|
|
3059
3140
|
return { score, why: hits.length ? `matched: ${hits.join(", ")}` : "" };
|
|
3060
3141
|
}
|
|
3142
|
+
var IMPORTANCE_WEIGHT = 0.4;
|
|
3143
|
+
function termWeights(graph, terms) {
|
|
3144
|
+
if (terms.length === 0) return () => 1;
|
|
3145
|
+
const df = /* @__PURE__ */ new Map();
|
|
3146
|
+
let n = 0;
|
|
3147
|
+
for (const node of graph.nodes) {
|
|
3148
|
+
if (node.kind === "file" || node.kind === "external") continue;
|
|
3149
|
+
n++;
|
|
3150
|
+
const name = node.name.toLowerCase();
|
|
3151
|
+
const parts = identifierParts(node.name);
|
|
3152
|
+
for (const t of terms) {
|
|
3153
|
+
if (parts.has(t) || name.includes(t)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
const w = /* @__PURE__ */ new Map();
|
|
3157
|
+
for (const t of terms) {
|
|
3158
|
+
const idf = Math.log((n + 1) / ((df.get(t) ?? 0) + 1)) + 1;
|
|
3159
|
+
w.set(t, Math.max(0.5, Math.min(8, idf)));
|
|
3160
|
+
}
|
|
3161
|
+
return (t) => w.get(t) ?? 1;
|
|
3162
|
+
}
|
|
3061
3163
|
function identifierParts(name) {
|
|
3062
3164
|
return new Set(
|
|
3063
3165
|
name.split(/[^a-zA-Z0-9]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/).filter(Boolean).map((s) => s.toLowerCase())
|
|
@@ -5416,7 +5518,11 @@ var PREVIEW_CHARS = 120;
|
|
|
5416
5518
|
function searchSymbols(graph, root, query, limit) {
|
|
5417
5519
|
const q2 = query.trim();
|
|
5418
5520
|
if (!q2) return { matches: [], moreAvailable: false, hint: "query is required" };
|
|
5419
|
-
|
|
5521
|
+
let nodes = findNodes(graph, q2).filter((n) => n.kind !== "file");
|
|
5522
|
+
if (nodes.length === 0) {
|
|
5523
|
+
const tokens = queryTokens(q2);
|
|
5524
|
+
if (tokens.length >= 2) nodes = multiTokenNodes(graph, tokens);
|
|
5525
|
+
}
|
|
5420
5526
|
const symbolHits = nodes.slice(0, limit).map((n, i) => ({
|
|
5421
5527
|
kind: n.kind,
|
|
5422
5528
|
name: n.qualifiedName,
|
|
@@ -5441,6 +5547,21 @@ function searchSymbols(graph, root, query, limit) {
|
|
|
5441
5547
|
}
|
|
5442
5548
|
return { matches, moreAvailable: nodes.length > symbolHits.length || truncatedScan };
|
|
5443
5549
|
}
|
|
5550
|
+
function queryTokens(q2) {
|
|
5551
|
+
return [...new Set(q2.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2))];
|
|
5552
|
+
}
|
|
5553
|
+
function multiTokenNodes(graph, tokens) {
|
|
5554
|
+
const cov = /* @__PURE__ */ new Map();
|
|
5555
|
+
for (const t of tokens) {
|
|
5556
|
+
for (const n of findNodes(graph, t)) {
|
|
5557
|
+
if (n.kind === "file") continue;
|
|
5558
|
+
const e = cov.get(n.id);
|
|
5559
|
+
if (e) e.hits++;
|
|
5560
|
+
else cov.set(n.id, { node: n, hits: 1 });
|
|
5561
|
+
}
|
|
5562
|
+
}
|
|
5563
|
+
return [...cov.values()].sort((a, b) => b.hits - a.hits || b.node.importance - a.node.importance).map((e) => e.node);
|
|
5564
|
+
}
|
|
5444
5565
|
function scanFiles(root, needle, budget, seen, out) {
|
|
5445
5566
|
const lower = needle.toLowerCase();
|
|
5446
5567
|
let scanned = 0;
|
|
@@ -5537,21 +5658,35 @@ var obj = (properties, required = []) => ({
|
|
|
5537
5658
|
var RESPONSE_FORMAT = { type: "string", enum: ["concise", "detailed"] };
|
|
5538
5659
|
var isDetailed = (args) => args.response_format === "detailed";
|
|
5539
5660
|
var CONCISE_MATCHES = 5;
|
|
5661
|
+
var EPISTEMIC_TIERS = ["observed", "name-matched", "declared"];
|
|
5662
|
+
var EDGE_FILTER_SCHEMA = {
|
|
5663
|
+
min_edge_confidence: { type: "number", description: "0..1 \u2014 drop edges below this confidence" },
|
|
5664
|
+
epistemic: { type: "string", enum: EPISTEMIC_TIERS, description: "keep only edges of this evidence tier" }
|
|
5665
|
+
};
|
|
5666
|
+
function edgeRecordFilter(args) {
|
|
5667
|
+
const min = numOrU(args.min_edge_confidence);
|
|
5668
|
+
const tier = EPISTEMIC_TIERS.includes(String(args.epistemic)) ? String(args.epistemic) : void 0;
|
|
5669
|
+
if (min == null && !tier) return () => true;
|
|
5670
|
+
return (x) => (min == null || x.edge.confidence >= min) && (!tier || x.edge.epistemic === tier);
|
|
5671
|
+
}
|
|
5540
5672
|
var TOOLS = [
|
|
5541
5673
|
{
|
|
5542
5674
|
name: "orient",
|
|
5543
5675
|
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.",
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
},
|
|
5551
|
-
|
|
5552
|
-
|
|
5676
|
+
// `query` is a no-description back-compat alias for `question`: the sibling
|
|
5677
|
+
// discovery tools (search_symbols, resolve_library) take `query`, so an agent
|
|
5678
|
+
// naturally reaches for it here too — accepting it turns what was a wasted
|
|
5679
|
+
// "question is required" round-trip into a hit. Neither is `required` (either
|
|
5680
|
+
// satisfies the in-handler check), matching library_docs/resolve_library.
|
|
5681
|
+
inputSchema: obj({
|
|
5682
|
+
question: { type: "string", maxLength: 300 },
|
|
5683
|
+
query: { type: "string", maxLength: 300 },
|
|
5684
|
+
scope: { type: "string", description: "path prefix to orient within" },
|
|
5685
|
+
budget: { type: "number", description: "token budget in detailed mode (default 1500)" },
|
|
5686
|
+
response_format: RESPONSE_FORMAT
|
|
5687
|
+
}),
|
|
5553
5688
|
handler: async (graph, args, ctx) => {
|
|
5554
|
-
const question = String(args.question ?? "");
|
|
5689
|
+
const question = String(args.question ?? args.query ?? "");
|
|
5555
5690
|
if (!question) return { error: "bad_request", message: "question is required" };
|
|
5556
5691
|
const budget = numOr(args.budget, 1500);
|
|
5557
5692
|
const { q: q2, mode } = await retrieve(graph, question, budget, ctx);
|
|
@@ -5567,7 +5702,11 @@ var TOOLS = [
|
|
|
5567
5702
|
}
|
|
5568
5703
|
const shown = detailed ? scoped : scoped.slice(0, CONCISE_MATCHES);
|
|
5569
5704
|
return {
|
|
5570
|
-
|
|
5705
|
+
// Concise "find X" calls need a one-line map (counts + languages), not the
|
|
5706
|
+
// full topAreas/topHubs blocks — those were ~half of every orient
|
|
5707
|
+
// response's tokens on the hot discovery path and the caller navigates by
|
|
5708
|
+
// `matches`. Detailed keeps the full overview.
|
|
5709
|
+
summary: detailed ? summarize(graph, 10) : conciseSummary(graph),
|
|
5571
5710
|
mode,
|
|
5572
5711
|
// The fact-annotated context block is detail: concise callers navigate
|
|
5573
5712
|
// by the ranked matches and fetch nodes on demand (plan P2).
|
|
@@ -5599,18 +5738,19 @@ var TOOLS = [
|
|
|
5599
5738
|
{
|
|
5600
5739
|
name: "query_graph",
|
|
5601
5740
|
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.",
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
},
|
|
5610
|
-
|
|
5611
|
-
),
|
|
5741
|
+
// `query` is a no-description back-compat alias for `question` (see orient):
|
|
5742
|
+
// sibling tools take `query`, so accept it here rather than reject the call.
|
|
5743
|
+
inputSchema: obj({
|
|
5744
|
+
question: { type: "string", maxLength: 300 },
|
|
5745
|
+
query: { type: "string", maxLength: 300 },
|
|
5746
|
+
limit: { type: "number", description: "ranked matches to return (default 5)" },
|
|
5747
|
+
offset: { type: "number" },
|
|
5748
|
+
budget: { type: "number", description: "context-block token budget in detailed mode (default 2000)" },
|
|
5749
|
+
response_format: RESPONSE_FORMAT
|
|
5750
|
+
}),
|
|
5612
5751
|
handler: async (graph, args, ctx) => {
|
|
5613
|
-
const question = String(args.question ?? "");
|
|
5752
|
+
const question = String(args.question ?? args.query ?? "");
|
|
5753
|
+
if (!question) return { mode: "lexical", matches: [], hint: "question (or query) is required" };
|
|
5614
5754
|
const budget = numOr(args.budget, 2e3);
|
|
5615
5755
|
const { q: r, mode } = await retrieve(graph, question, budget, ctx);
|
|
5616
5756
|
if (r.matches.length === 0) {
|
|
@@ -5639,6 +5779,7 @@ var TOOLS = [
|
|
|
5639
5779
|
{
|
|
5640
5780
|
name: { type: "string" },
|
|
5641
5781
|
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5782
|
+
...EDGE_FILTER_SCHEMA,
|
|
5642
5783
|
response_format: RESPONSE_FORMAT
|
|
5643
5784
|
},
|
|
5644
5785
|
["name"]
|
|
@@ -5647,6 +5788,9 @@ var TOOLS = [
|
|
|
5647
5788
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
5648
5789
|
if (!node) return unresolved(candidates);
|
|
5649
5790
|
const index = new GraphIndex(graph);
|
|
5791
|
+
const edgeOk = edgeRecordFilter(args);
|
|
5792
|
+
const calleeRecs = index.callees(node.id).filter(edgeOk);
|
|
5793
|
+
const callerRecs = index.callers(node.id).filter(edgeOk);
|
|
5650
5794
|
const base = {
|
|
5651
5795
|
name: node.qualifiedName,
|
|
5652
5796
|
kind: node.kind,
|
|
@@ -5658,11 +5802,11 @@ var TOOLS = [
|
|
|
5658
5802
|
area: node.area
|
|
5659
5803
|
};
|
|
5660
5804
|
if (ctx?.dedup && ctx.seen?.has(node.id)) {
|
|
5661
|
-
return { ...base, repeat: true, callsTotal: uniqueNames(
|
|
5805
|
+
return { ...base, repeat: true, callsTotal: uniqueNames(calleeRecs.map((x) => x.node.qualifiedName)).length, calledByTotal: uniqueNames(callerRecs.map((x) => x.node.qualifiedName)).length };
|
|
5662
5806
|
}
|
|
5663
5807
|
const edgeCap = isDetailed(args) ? NODE_EDGE_CAP : CONCISE_MATCHES;
|
|
5664
|
-
const calls = boundList(uniqueNames(
|
|
5665
|
-
const calledBy = boundList(uniqueNames(
|
|
5808
|
+
const calls = boundList(uniqueNames(calleeRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
5809
|
+
const calledBy = boundList(uniqueNames(callerRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
5666
5810
|
ctx?.seen?.add(node.id);
|
|
5667
5811
|
return {
|
|
5668
5812
|
...base,
|
|
@@ -5705,6 +5849,7 @@ var TOOLS = [
|
|
|
5705
5849
|
change_type: { type: "string", enum: ["modify", "delete", "rename", "add_dependency"] },
|
|
5706
5850
|
depth: { type: "number", description: "default 4" },
|
|
5707
5851
|
pick: { type: "number", description: "nth candidate if ambiguous" },
|
|
5852
|
+
min_edge_confidence: { type: "number", description: "0..1 \u2014 drop dependents below this (decayed) confidence" },
|
|
5708
5853
|
response_format: RESPONSE_FORMAT
|
|
5709
5854
|
},
|
|
5710
5855
|
["name"]
|
|
@@ -5714,6 +5859,14 @@ var TOOLS = [
|
|
|
5714
5859
|
if (!node) return unresolved(candidates);
|
|
5715
5860
|
const changeType = ["modify", "delete", "rename", "add_dependency"].includes(String(args.change_type)) ? String(args.change_type) : "modify";
|
|
5716
5861
|
const r = impactOf(graph, node.id, { depth: numOr(args.depth, 4) });
|
|
5862
|
+
const minConf = numOrU(args.min_edge_confidence);
|
|
5863
|
+
if (minConf != null) {
|
|
5864
|
+
const kept = r.affected.filter((a) => a.confidence >= minConf);
|
|
5865
|
+
r.affected = kept;
|
|
5866
|
+
r.direct = kept.filter((i) => i.depth === 1).length;
|
|
5867
|
+
r.transitive = kept.filter((i) => i.depth > 1).length;
|
|
5868
|
+
r.minEdgeConfidence = kept.length ? Math.min(...kept.map((i) => i.confidence)) : 1;
|
|
5869
|
+
}
|
|
5717
5870
|
const tests = coveringTests(graph, node);
|
|
5718
5871
|
const filesAffected = [...new Set(r.affected.map((a) => a.file))];
|
|
5719
5872
|
const heavyChange = changeType === "delete" || changeType === "rename";
|
|
@@ -6065,6 +6218,9 @@ var TOOLS = [
|
|
|
6065
6218
|
}
|
|
6066
6219
|
];
|
|
6067
6220
|
var VERBOSITY_BUDGET = { concise: 1500, balanced: 4e3, exhaustive: 12e3 };
|
|
6221
|
+
function conciseSummary(graph) {
|
|
6222
|
+
return { counts: graph.meta.counts, languages: graph.meta.languages };
|
|
6223
|
+
}
|
|
6068
6224
|
function summarize(graph, top = 10) {
|
|
6069
6225
|
return {
|
|
6070
6226
|
counts: graph.meta.counts,
|
|
@@ -6119,6 +6275,7 @@ function navigationToolsetConfig(serverName = "vibgrate") {
|
|
|
6119
6275
|
}
|
|
6120
6276
|
var LEDGER = "savings.jsonl";
|
|
6121
6277
|
var PER_FILE_TOKENS = 400;
|
|
6278
|
+
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node"]);
|
|
6122
6279
|
function ledgerPath(root) {
|
|
6123
6280
|
return path18.join(cacheDir(root), LEDGER);
|
|
6124
6281
|
}
|
|
@@ -6147,6 +6304,7 @@ function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
|
6147
6304
|
try {
|
|
6148
6305
|
const e = JSON.parse(line);
|
|
6149
6306
|
if (e.ts < cutoff) continue;
|
|
6307
|
+
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
6150
6308
|
queries++;
|
|
6151
6309
|
vgTokens += e.vgTokens;
|
|
6152
6310
|
baselineTokens += e.baselineTokens;
|
|
@@ -6172,6 +6330,47 @@ function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
|
6172
6330
|
function round22(x) {
|
|
6173
6331
|
return Math.round(x * 100) / 100;
|
|
6174
6332
|
}
|
|
6333
|
+
function readUsage(root, days, now) {
|
|
6334
|
+
const file = ledgerPath(root);
|
|
6335
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
6336
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
6337
|
+
if (fs18.existsSync(file)) {
|
|
6338
|
+
for (const line of fs18.readFileSync(file, "utf8").split("\n")) {
|
|
6339
|
+
if (!line.trim()) continue;
|
|
6340
|
+
try {
|
|
6341
|
+
const e = JSON.parse(line);
|
|
6342
|
+
if (e.ts < cutoff) continue;
|
|
6343
|
+
const outcome = e.outcome ?? "complete";
|
|
6344
|
+
const row = byTool.get(e.tool) ?? { complete: 0, partial: 0, miss: 0 };
|
|
6345
|
+
row[outcome]++;
|
|
6346
|
+
byTool.set(e.tool, row);
|
|
6347
|
+
} catch {
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
const commands = [...byTool.entries()].map(([tool, r]) => {
|
|
6352
|
+
const calls = r.complete + r.partial + r.miss;
|
|
6353
|
+
return {
|
|
6354
|
+
tool,
|
|
6355
|
+
calls,
|
|
6356
|
+
complete: r.complete,
|
|
6357
|
+
partial: r.partial,
|
|
6358
|
+
miss: r.miss,
|
|
6359
|
+
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
6360
|
+
};
|
|
6361
|
+
}).sort((a, b) => b.calls - a.calls || a.tool.localeCompare(b.tool));
|
|
6362
|
+
const totals = commands.reduce(
|
|
6363
|
+
(t, c) => ({
|
|
6364
|
+
calls: t.calls + c.calls,
|
|
6365
|
+
complete: t.complete + c.complete,
|
|
6366
|
+
partial: t.partial + c.partial,
|
|
6367
|
+
miss: t.miss + c.miss
|
|
6368
|
+
}),
|
|
6369
|
+
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
6370
|
+
);
|
|
6371
|
+
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
6372
|
+
return { enabled: savingsRecorded(root), days, commands, totals, avgSuccessPct };
|
|
6373
|
+
}
|
|
6175
6374
|
var PROBE_INTERVAL_MS = 2e3;
|
|
6176
6375
|
var MAX_PROBE_INTERVAL_MS = 3e4;
|
|
6177
6376
|
var PROBE_DUTY_FACTOR = 20;
|
|
@@ -6271,9 +6470,7 @@ function createServer(source, opts = {}) {
|
|
|
6271
6470
|
try {
|
|
6272
6471
|
const args = request.params.arguments ?? {};
|
|
6273
6472
|
const result = await tool.handler(graph, args, { root, local, dedup, seen });
|
|
6274
|
-
if (savings
|
|
6275
|
-
maybeRecordSaving(root, tool.name, result);
|
|
6276
|
-
}
|
|
6473
|
+
if (savings) recordUsage(root, tool.name, result);
|
|
6277
6474
|
return renderToolResult(result);
|
|
6278
6475
|
} catch (err) {
|
|
6279
6476
|
return errorResult(`tool "${tool.name}" failed: ${err.message}`);
|
|
@@ -6293,14 +6490,57 @@ function sleep(ms) {
|
|
|
6293
6490
|
timer.unref?.();
|
|
6294
6491
|
});
|
|
6295
6492
|
}
|
|
6296
|
-
function
|
|
6297
|
-
|
|
6493
|
+
function recordUsage(root, tool, result) {
|
|
6494
|
+
const outcome = classifyOutcome(result);
|
|
6495
|
+
let vgTokens = 0;
|
|
6496
|
+
let baselineTokens = 0;
|
|
6497
|
+
if (SAVINGS_TOOLS.has(tool) && result && typeof result === "object") {
|
|
6498
|
+
vgTokens = countTokens(renderedText(renderToolResult(result)));
|
|
6499
|
+
baselineTokens = referencedFiles(result).size * PER_FILE_TOKENS;
|
|
6500
|
+
}
|
|
6501
|
+
recordSaving(root, { tool, outcome, vgTokens, baselineTokens }, Date.now());
|
|
6502
|
+
}
|
|
6503
|
+
function classifyOutcome(result) {
|
|
6504
|
+
if (Array.isArray(result)) return result.length === 0 ? "miss" : "complete";
|
|
6505
|
+
if (!result || typeof result !== "object") return "miss";
|
|
6506
|
+
const r = result;
|
|
6507
|
+
if (typeof r.error === "string" && r.error) return "miss";
|
|
6508
|
+
if (r.connected === false) return "miss";
|
|
6509
|
+
if (Array.isArray(r.matches) && r.matches.length === 0) return "miss";
|
|
6510
|
+
return isPartial(r) ? "partial" : "complete";
|
|
6511
|
+
}
|
|
6512
|
+
function isPartial(r) {
|
|
6513
|
+
if (r.moreAvailable === true) return true;
|
|
6514
|
+
if (r._truncated && typeof r._truncated === "object") return true;
|
|
6515
|
+
for (const [key, value] of Object.entries(r)) {
|
|
6516
|
+
if (typeof value !== "number" || !key.endsWith("Total")) continue;
|
|
6517
|
+
const base = key.slice(0, -"Total".length);
|
|
6518
|
+
const shown = Array.isArray(r[base]) ? r[base].length : 0;
|
|
6519
|
+
if (value > shown) return true;
|
|
6520
|
+
}
|
|
6521
|
+
return false;
|
|
6522
|
+
}
|
|
6523
|
+
function renderedText(rendered) {
|
|
6524
|
+
const block = rendered.content?.find((b) => b.type === "text");
|
|
6525
|
+
return block && "text" in block && typeof block.text === "string" ? block.text : "";
|
|
6526
|
+
}
|
|
6527
|
+
function referencedFiles(result) {
|
|
6298
6528
|
const r = result;
|
|
6299
|
-
const
|
|
6300
|
-
if (
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6303
|
-
|
|
6529
|
+
const files = /* @__PURE__ */ new Set();
|
|
6530
|
+
if (typeof r.file === "string" && r.file) files.add(r.file);
|
|
6531
|
+
for (const m of asArray(r.matches)) {
|
|
6532
|
+
const f = m.file;
|
|
6533
|
+
if (typeof f === "string" && f) files.add(f);
|
|
6534
|
+
}
|
|
6535
|
+
for (const name of [...asArray(r.calls), ...asArray(r.calledBy)]) {
|
|
6536
|
+
if (typeof name !== "string") continue;
|
|
6537
|
+
const i = name.indexOf(":");
|
|
6538
|
+
if (i > 0) files.add(name.slice(0, i));
|
|
6539
|
+
}
|
|
6540
|
+
return files;
|
|
6541
|
+
}
|
|
6542
|
+
function asArray(v) {
|
|
6543
|
+
return Array.isArray(v) ? v : [];
|
|
6304
6544
|
}
|
|
6305
6545
|
function errorResult(message) {
|
|
6306
6546
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
@@ -6610,6 +6850,8 @@ function formatForExt(ext) {
|
|
|
6610
6850
|
return "dot";
|
|
6611
6851
|
case ".cypher":
|
|
6612
6852
|
return "cypher";
|
|
6853
|
+
case ".sql":
|
|
6854
|
+
return "sql";
|
|
6613
6855
|
case ".md":
|
|
6614
6856
|
return "md";
|
|
6615
6857
|
case ".html":
|
|
@@ -6634,6 +6876,8 @@ function exportGraph(format, ctx) {
|
|
|
6634
6876
|
return dot(ctx.graph);
|
|
6635
6877
|
case "cypher":
|
|
6636
6878
|
return cypher(ctx.graph);
|
|
6879
|
+
case "sql":
|
|
6880
|
+
return sql(ctx);
|
|
6637
6881
|
case "cyclonedx":
|
|
6638
6882
|
return cyclonedx(ctx);
|
|
6639
6883
|
case "spdx":
|
|
@@ -6696,6 +6940,66 @@ function cypherLabel(s) {
|
|
|
6696
6940
|
function q(s) {
|
|
6697
6941
|
return JSON.stringify(s);
|
|
6698
6942
|
}
|
|
6943
|
+
function sql(ctx) {
|
|
6944
|
+
const graph = ctx.graph;
|
|
6945
|
+
const lines = [];
|
|
6946
|
+
lines.push("BEGIN;");
|
|
6947
|
+
lines.push("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);");
|
|
6948
|
+
const meta = [
|
|
6949
|
+
["schemaVersion", graph.schemaVersion],
|
|
6950
|
+
["generatedAt", ctx.generatedAt],
|
|
6951
|
+
["corpusHash", graph.provenance.corpusHash],
|
|
6952
|
+
["tool", graph.provenance.tool],
|
|
6953
|
+
["toolVersion", graph.provenance.version],
|
|
6954
|
+
["resolver", graph.provenance.resolver.join(",")],
|
|
6955
|
+
["fingerprint", graph.provenance.toolchain?.fingerprint ?? null]
|
|
6956
|
+
];
|
|
6957
|
+
for (const [k, v] of meta) lines.push(`INSERT INTO meta VALUES (${sqlStr(k)}, ${sqlStr(v)});`);
|
|
6958
|
+
lines.push(
|
|
6959
|
+
"CREATE TABLE IF NOT EXISTS nodes (id TEXT PRIMARY KEY, kind TEXT, name TEXT, qualified_name TEXT, file TEXT, span_start INTEGER, span_end INTEGER, lang TEXT, visibility TEXT, signature TEXT, importance REAL, area INTEGER, is_hub INTEGER, tested INTEGER, coverage REAL);"
|
|
6960
|
+
);
|
|
6961
|
+
for (const n of graph.nodes) {
|
|
6962
|
+
lines.push(
|
|
6963
|
+
`INSERT INTO nodes VALUES (${sqlStr(n.id)}, ${sqlStr(n.kind)}, ${sqlStr(n.name)}, ${sqlStr(n.qualifiedName)}, ${sqlStr(n.file)}, ${sqlNum(n.span.start)}, ${sqlNum(n.span.end)}, ${sqlStr(n.lang)}, ${sqlStr(n.visibility)}, ${sqlStr(n.signature)}, ${sqlNum(n.importance)}, ${sqlNum(n.area)}, ${sqlBool(n.isHub)}, ${sqlBool(n.tested)}, ${sqlNum(n.coverage)});`
|
|
6964
|
+
);
|
|
6965
|
+
}
|
|
6966
|
+
lines.push(
|
|
6967
|
+
'CREATE TABLE IF NOT EXISTS edges (id TEXT PRIMARY KEY, kind TEXT, src TEXT, dst TEXT, resolution TEXT, confidence REAL, epistemic TEXT, surprise REAL, "count" INTEGER);'
|
|
6968
|
+
);
|
|
6969
|
+
for (const e of graph.edges) {
|
|
6970
|
+
lines.push(
|
|
6971
|
+
`INSERT INTO edges VALUES (${sqlStr(e.id)}, ${sqlStr(e.kind)}, ${sqlStr(e.src)}, ${sqlStr(e.dst)}, ${sqlStr(e.resolution)}, ${sqlNum(e.confidence)}, ${sqlStr(e.epistemic)}, ${sqlNum(e.surprise)}, ${sqlNum(e.count)});`
|
|
6972
|
+
);
|
|
6973
|
+
}
|
|
6974
|
+
lines.push("CREATE TABLE IF NOT EXISTS areas (id INTEGER PRIMARY KEY, label TEXT, size INTEGER, cohesion REAL, external_edges INTEGER);");
|
|
6975
|
+
lines.push("CREATE TABLE IF NOT EXISTS area_members (area_id INTEGER, node_id TEXT);");
|
|
6976
|
+
for (const a of graph.areas) {
|
|
6977
|
+
lines.push(`INSERT INTO areas VALUES (${sqlNum(a.id)}, ${sqlStr(a.label)}, ${sqlNum(a.size)}, ${sqlNum(a.cohesion)}, ${sqlNum(a.externalEdges)});`);
|
|
6978
|
+
for (const m of a.members) lines.push(`INSERT INTO area_members VALUES (${sqlNum(a.id)}, ${sqlStr(m)});`);
|
|
6979
|
+
}
|
|
6980
|
+
if (graph.facts && graph.facts.length) {
|
|
6981
|
+
lines.push("CREATE TABLE IF NOT EXISTS facts (id TEXT PRIMARY KEY, kind TEXT, predicate_json TEXT, derived_by TEXT, confidence TEXT);");
|
|
6982
|
+
lines.push("CREATE TABLE IF NOT EXISTS fact_subjects (fact_id TEXT, node_id TEXT);");
|
|
6983
|
+
lines.push("CREATE TABLE IF NOT EXISTS fact_evidence (fact_id TEXT, file TEXT, span_start INTEGER, span_end INTEGER);");
|
|
6984
|
+
for (const f of graph.facts) {
|
|
6985
|
+
lines.push(`INSERT INTO facts VALUES (${sqlStr(f.id)}, ${sqlStr(f.kind)}, ${sqlStr(JSON.stringify(f.predicate))}, ${sqlStr(f.derivedBy)}, ${sqlStr(f.confidence)});`);
|
|
6986
|
+
for (const s of f.subjectIds) lines.push(`INSERT INTO fact_subjects VALUES (${sqlStr(f.id)}, ${sqlStr(s)});`);
|
|
6987
|
+
for (const ev of f.evidence) lines.push(`INSERT INTO fact_evidence VALUES (${sqlStr(f.id)}, ${sqlStr(ev.file)}, ${sqlNum(ev.span.start)}, ${sqlNum(ev.span.end)});`);
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6990
|
+
lines.push("COMMIT;");
|
|
6991
|
+
return lines.join("\n") + "\n";
|
|
6992
|
+
}
|
|
6993
|
+
function sqlStr(v) {
|
|
6994
|
+
if (v == null) return "NULL";
|
|
6995
|
+
return `'${v.replace(/'/g, "''")}'`;
|
|
6996
|
+
}
|
|
6997
|
+
function sqlNum(v) {
|
|
6998
|
+
return v == null || !Number.isFinite(v) ? "NULL" : String(v);
|
|
6999
|
+
}
|
|
7000
|
+
function sqlBool(v) {
|
|
7001
|
+
return v == null ? "NULL" : v ? "1" : "0";
|
|
7002
|
+
}
|
|
6699
7003
|
function cyclonedx(ctx) {
|
|
6700
7004
|
const components = [];
|
|
6701
7005
|
for (const d of ctx.deps ?? []) {
|
|
@@ -6735,6 +7039,6 @@ function spdx(ctx) {
|
|
|
6735
7039
|
return JSON.stringify(doc, null, 2) + "\n";
|
|
6736
7040
|
}
|
|
6737
7041
|
|
|
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-
|
|
6740
|
-
//# sourceMappingURL=chunk-
|
|
7042
|
+
export { ASSISTANTS, CliError, ECOSYSTEMS, ExitCode, FREE_PACK, GraphIndex, GraphSource, NPX_INVOCATION, ResourceLimitError, SCHEMA_VERSION, SKIP_DIRS, SKIP_FILES, TOOLS, UsageError, addLibrary, analyze, applyCoverage, applyStaticTestLinkage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, buildModuleResolver, cacheDir, catalogPath, clearModelCache, clearStoredCredentials, cosine, countPending, coveringTests, createServer, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, defaultGraphPath, detectRunner, detectServeLaunch, discover, discoverModels, driftCount, driftFor, dsnCommand, embeddingsCached, enrichOnline, ensureGitignored, epistemicBreakdown, exportGraph, fetchHostedDocs, findGitRoot, findNodes, formatForExt, getNodeEmbeddings, gitignoreEntryForCredentials, groundGraph, hasDrift, identifierParts, impactOf, ingestHostForRegionId, installAssistant, inventory, isModelReady, isTestFile, libDir, libId, loadCatalog, loadCoverage, loadEmbedder, loadGraph, loadSnapshot, localApiSurface, localPackageDocs, modelCacheInfo, nodeById, nodeEmbedText, parseDsn, parseGraph, parseJsonc, probeFreshness, publishPrivateLibrary, pushCommand, queryGraph, queryGraphSemantic, readDoc, readSavings, readScanArtifact, readUsage, recordSaving, refreshIfStale, relativeResolver, renderHtml, renderReport, resolveCliInvocation, resolveDsn, resolveEmbedModel, resolveIngestHost, resolveLib, resolveLimits, resolveOne, resolveVersion, saveCatalog, savingsRecorded, scipEdges, selectForBudget, serializeGraph, serveStdio, shortestPath, stableStringify, symbolsFromApi, testsToRun, unavailableMessage, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, vibgrateDir, writeArtifacts, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
7043
|
+
//# sourceMappingURL=chunk-AWEAREOE.js.map
|
|
7044
|
+
//# sourceMappingURL=chunk-AWEAREOE.js.map
|