@vibgrate/cli 2026.727.1 → 2026.727.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +21 -18
- package/README.md +2 -2
- package/dist/baseline-ATM5MXBZ.js +8 -0
- package/dist/{baseline-K7YBFKZI.js.map → baseline-ATM5MXBZ.js.map} +1 -1
- package/dist/{chunk-OXQNQW5J.js → chunk-2VFWXNS5.js} +4 -4
- package/dist/{chunk-OXQNQW5J.js.map → chunk-2VFWXNS5.js.map} +1 -1
- package/dist/{chunk-VXGV6NTD.js → chunk-3V24O47U.js} +11 -9
- package/dist/chunk-3V24O47U.js.map +1 -0
- package/dist/{chunk-C4CKTAZP.js → chunk-6JIKUDWM.js} +4 -4
- package/dist/chunk-6JIKUDWM.js.map +1 -0
- package/dist/{chunk-6IOQJGFT.js → chunk-6UK6QPTR.js} +96 -140
- package/dist/chunk-6UK6QPTR.js.map +1 -0
- package/dist/chunk-6UQMXIAG.js +122 -0
- package/dist/chunk-6UQMXIAG.js.map +1 -0
- package/dist/{chunk-SZLF66FJ.js → chunk-77LYGXBI.js} +5 -5
- package/dist/{chunk-SZLF66FJ.js.map → chunk-77LYGXBI.js.map} +1 -1
- package/dist/{chunk-KHJO4D5N.js → chunk-7N4Y3E47.js} +2 -2
- package/dist/{chunk-KHJO4D5N.js.map → chunk-7N4Y3E47.js.map} +1 -1
- package/dist/{chunk-65MEOA4E.js → chunk-AVQSHIJ5.js} +22 -18
- package/dist/chunk-AVQSHIJ5.js.map +1 -0
- package/dist/chunk-B3YUO3NW.js +469 -0
- package/dist/chunk-B3YUO3NW.js.map +1 -0
- package/dist/{chunk-YDAT7LD2.js → chunk-C66GOHYB.js} +18 -10
- package/dist/chunk-C66GOHYB.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +10 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/{chunk-HYZVHJ3Q.js → chunk-FFWGT55D.js} +4 -4
- package/dist/{chunk-HYZVHJ3Q.js.map → chunk-FFWGT55D.js.map} +1 -1
- package/dist/{chunk-4CQ3MGNN.js → chunk-HK3AWUXH.js} +3 -3
- package/dist/{chunk-4CQ3MGNN.js.map → chunk-HK3AWUXH.js.map} +1 -1
- package/dist/{chunk-S6CYDZEW.js → chunk-MAY5HLDH.js} +772 -200
- package/dist/chunk-MAY5HLDH.js.map +1 -0
- package/dist/{chunk-7JHPQ6WF.js → chunk-OFUC6RYN.js} +3 -3
- package/dist/{chunk-7JHPQ6WF.js.map → chunk-OFUC6RYN.js.map} +1 -1
- package/dist/{chunk-3Q7QBWFW.js → chunk-ON25BX6T.js} +9 -3
- package/dist/chunk-ON25BX6T.js.map +1 -0
- package/dist/{chunk-JZYGEZTU.js → chunk-XPJZTSY7.js} +3 -3
- package/dist/{chunk-JZYGEZTU.js.map → chunk-XPJZTSY7.js.map} +1 -1
- package/dist/{chunk-WLETRGAC.js → chunk-Y23HMSWU.js} +11 -11
- package/dist/{chunk-WLETRGAC.js.map → chunk-Y23HMSWU.js.map} +1 -1
- package/dist/{chunk-3BVSDMHV.js → chunk-Z7GP7XTJ.js} +8 -8
- package/dist/{chunk-3BVSDMHV.js.map → chunk-Z7GP7XTJ.js.map} +1 -1
- package/dist/{chunk-EDAAG7BL.js → chunk-ZBTY6VV4.js} +4 -3
- package/dist/chunk-ZBTY6VV4.js.map +1 -0
- package/dist/chunk-ZKNB4YH4.js +102 -0
- package/dist/chunk-ZKNB4YH4.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +475 -404
- package/dist/cli.js.map +1 -1
- package/dist/{execution-env-3BN7Z737.js → execution-env-ZU45JZ4G.js} +3 -2
- package/dist/{execution-env-3BN7Z737.js.map → execution-env-ZU45JZ4G.js.map} +1 -1
- package/dist/{federation-EPZDUWCK.js → federation-SZWZIFZ7.js} +3 -2
- package/dist/{federation-EPZDUWCK.js.map → federation-SZWZIFZ7.js.map} +1 -1
- package/dist/{fs-IPY4FJKH.js → fs-B5HZ2MIU.js} +3 -2
- package/dist/fs-B5HZ2MIU.js.map +1 -0
- package/dist/{fs-KCABDURV.js → fs-O5IQYXHO.js} +3 -2
- package/dist/fs-O5IQYXHO.js.map +1 -0
- package/dist/{git-ref-HPRLUFFW.js → git-ref-XDXB6JP5.js} +3 -2
- package/dist/{git-ref-HPRLUFFW.js.map → git-ref-XDXB6JP5.js.map} +1 -1
- package/dist/graph-backend-UTOL3SHE.js +11 -0
- package/dist/{graph-backend-ZMSYOZMW.js.map → graph-backend-UTOL3SHE.js.map} +1 -1
- package/dist/index.d.ts +53 -17
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/{interactive-ICL7D7LO.js → interactive-INGJNSON.js} +23 -22
- package/dist/interactive-INGJNSON.js.map +1 -0
- package/dist/{llm-host-2RAYQSDQ.js → llm-host-7GWDO6IX.js} +3 -2
- package/dist/{llm-host-2RAYQSDQ.js.map → llm-host-7GWDO6IX.js.map} +1 -1
- package/dist/load-QPOWQD7C.js +8 -0
- package/dist/{load-OHVKDX4R.js.map → load-QPOWQD7C.js.map} +1 -1
- package/dist/local-runtime-5O2NN3GA.js +5 -0
- package/dist/{local-runtime-OJFLX2HI.js.map → local-runtime-5O2NN3GA.js.map} +1 -1
- package/dist/mcp-tools-I7JK3S2M.js +4 -0
- package/dist/{mcp-tools-I6PME3AV.js.map → mcp-tools-I7JK3S2M.js.map} +1 -1
- package/dist/{model-execution-profile-MQ2QCP24.js → model-execution-profile-TFBKCNJG.js} +3 -2
- package/dist/{model-execution-profile-MQ2QCP24.js.map → model-execution-profile-TFBKCNJG.js.map} +1 -1
- package/dist/{model-orchestrator-FFTZI3U2.js → model-orchestrator-TVONAUPS.js} +5 -4
- package/dist/{model-orchestrator-FFTZI3U2.js.map → model-orchestrator-TVONAUPS.js.map} +1 -1
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +2 -1
- package/dist/parse-worker.js.map +1 -1
- package/dist/{paths-MCH75O3G.js → paths-2RAH3GME.js} +3 -2
- package/dist/{paths-MCH75O3G.js.map → paths-2RAH3GME.js.map} +1 -1
- package/dist/{resolve-gguf-4UPIGPEE.js → resolve-gguf-VPL4YQ6J.js} +3 -2
- package/dist/{resolve-gguf-4UPIGPEE.js.map → resolve-gguf-VPL4YQ6J.js.map} +1 -1
- package/dist/runtime-session-4AMZIAAG.js +18 -0
- package/dist/{runtime-session-IDU5BDUJ.js.map → runtime-session-4AMZIAAG.js.map} +1 -1
- package/dist/session-M72522AP.js +11 -0
- package/dist/{session-L2OSDRPI.js.map → session-M72522AP.js.map} +1 -1
- package/dist/session-store-WFBX7HXL.js +4 -0
- package/dist/{session-store-W5RZZHA3.js.map → session-store-WFBX7HXL.js.map} +1 -1
- package/dist/sql-extract-JELDZA6W.js +81 -0
- package/dist/sql-extract-JELDZA6W.js.map +1 -0
- package/dist/{stream-json-F74W5FCK.js → stream-json-6ZBUA2L3.js} +11 -10
- package/dist/stream-json-6ZBUA2L3.js.map +1 -0
- package/dist/{types-ZMg1mSuu.d.ts → types-DLH-Ritr.d.ts} +22 -1
- package/dist/ui-Q6DJ3GCU.js +5 -0
- package/dist/ui-Q6DJ3GCU.js.map +1 -0
- package/dist/{vgd-EGBQD7BW.js → vgd-CVLKBS7K.js} +7 -6
- package/dist/vgd-CVLKBS7K.js.map +1 -0
- package/package.json +1 -1
- package/dist/baseline-K7YBFKZI.js +0 -7
- package/dist/chunk-3Q7QBWFW.js.map +0 -1
- package/dist/chunk-5Q54GEHM.js +0 -29
- package/dist/chunk-5Q54GEHM.js.map +0 -1
- package/dist/chunk-65MEOA4E.js.map +0 -1
- package/dist/chunk-6IOQJGFT.js.map +0 -1
- package/dist/chunk-C4CKTAZP.js.map +0 -1
- package/dist/chunk-EDAAG7BL.js.map +0 -1
- package/dist/chunk-EDBV33B7.js +0 -57
- package/dist/chunk-EDBV33B7.js.map +0 -1
- package/dist/chunk-HABUT7HW.js +0 -233
- package/dist/chunk-HABUT7HW.js.map +0 -1
- package/dist/chunk-S6CYDZEW.js.map +0 -1
- package/dist/chunk-VXGV6NTD.js.map +0 -1
- package/dist/chunk-YDAT7LD2.js.map +0 -1
- package/dist/fs-IPY4FJKH.js.map +0 -1
- package/dist/fs-KCABDURV.js.map +0 -1
- package/dist/graph-backend-ZMSYOZMW.js +0 -10
- package/dist/interactive-ICL7D7LO.js.map +0 -1
- package/dist/load-OHVKDX4R.js +0 -7
- package/dist/local-runtime-OJFLX2HI.js +0 -4
- package/dist/mcp-tools-I6PME3AV.js +0 -3
- package/dist/runtime-session-IDU5BDUJ.js +0 -17
- package/dist/session-L2OSDRPI.js +0 -10
- package/dist/session-store-W5RZZHA3.js +0 -3
- package/dist/stream-json-F74W5FCK.js.map +0 -1
- package/dist/ui-HOVOPWMX.js +0 -4
- package/dist/ui-HOVOPWMX.js.map +0 -1
- package/dist/vgd-EGBQD7BW.js.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { resolveVersion, resolveLib, loadCatalog, driftFor, localPackageDocs, readDoc, inventory, localApiSurface, edgeId, buildModuleResolver, resolve, applyStaticTestLinkage, nodeId } from './chunk-6JIKUDWM.js';
|
|
2
|
+
import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-Y23HMSWU.js';
|
|
3
|
+
import { writeTextFile, pathExists, readJsonFile } from './chunk-CRAMOEBE.js';
|
|
3
4
|
import { resolveOne, findNodes } from './chunk-J7LKEB2R.js';
|
|
4
5
|
import { discoverModels } from './chunk-OOIG3RUM.js';
|
|
5
|
-
import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady,
|
|
6
|
-
import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-
|
|
6
|
+
import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady, acquireLock, releaseLock } from './chunk-6UK6QPTR.js';
|
|
7
|
+
import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-ON25BX6T.js';
|
|
7
8
|
import { redactSecrets } from './chunk-RXP66R2E.js';
|
|
8
|
-
import { loadGraph, ensureVibgrateGitignore, vibgrateDir, writeArtifacts } from './chunk-
|
|
9
|
-
import { serializeGraph, stableStringify } from './chunk-
|
|
9
|
+
import { writeGraphIndex, SCHEMA_VERSION, loadGraph, ensureVibgrateGitignore, vibgrateDir, writeArtifacts } from './chunk-B3YUO3NW.js';
|
|
10
|
+
import { cacheDir, loadCache, serializeGraph, stableStringify } from './chunk-ZKNB4YH4.js';
|
|
10
11
|
import { shortId, canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
|
|
11
|
-
import
|
|
12
|
-
import * as fs15 from 'fs';
|
|
12
|
+
import * as fs18 from 'fs';
|
|
13
13
|
import { spawnSync, execFileSync } from 'child_process';
|
|
14
|
-
import * as
|
|
14
|
+
import * as path19 from 'path';
|
|
15
15
|
import ignore from 'ignore';
|
|
16
16
|
import * as v8 from 'v8';
|
|
17
17
|
import Graph from 'graphology';
|
|
@@ -22,6 +22,7 @@ import louvain from 'graphology-communities-louvain';
|
|
|
22
22
|
import * as os from 'os';
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import ts from 'typescript';
|
|
25
|
+
import * as fs8 from 'fs/promises';
|
|
25
26
|
import { bidirectional } from 'graphology-shortest-path/unweighted.js';
|
|
26
27
|
import * as crypto from 'crypto';
|
|
27
28
|
import { createHash } from 'crypto';
|
|
@@ -46,9 +47,9 @@ function whichOnPath(cmd) {
|
|
|
46
47
|
}
|
|
47
48
|
function isOwnBinary(binPath) {
|
|
48
49
|
try {
|
|
49
|
-
const real =
|
|
50
|
+
const real = fs18.realpathSync(binPath);
|
|
50
51
|
if (/[\\/]@vibgrate[\\/]cli[\\/]/.test(real)) return true;
|
|
51
|
-
const head =
|
|
52
|
+
const head = fs18.readFileSync(real, { encoding: "utf8" }).slice(0, 2048);
|
|
52
53
|
return head.includes("@vibgrate/cli") || head.includes("vibgrate");
|
|
53
54
|
} catch {
|
|
54
55
|
return false;
|
|
@@ -58,7 +59,7 @@ function isEphemeralNpxBinary(binPath) {
|
|
|
58
59
|
const NPX_SEGMENT = /[\\/]_npx[\\/]/;
|
|
59
60
|
if (NPX_SEGMENT.test(binPath)) return true;
|
|
60
61
|
try {
|
|
61
|
-
return NPX_SEGMENT.test(
|
|
62
|
+
return NPX_SEGMENT.test(fs18.realpathSync(binPath));
|
|
62
63
|
} catch {
|
|
63
64
|
return false;
|
|
64
65
|
}
|
|
@@ -235,19 +236,19 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
|
235
236
|
"flake.lock"
|
|
236
237
|
]);
|
|
237
238
|
function toPosix(p) {
|
|
238
|
-
return p.split(
|
|
239
|
+
return p.split(path19.sep).join("/");
|
|
239
240
|
}
|
|
240
241
|
function buildRootIgnore(root, exclude) {
|
|
241
242
|
const ig = ignore();
|
|
242
|
-
const gitignorePath =
|
|
243
|
-
if (
|
|
244
|
-
ig.add(
|
|
243
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
244
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
245
|
+
ig.add(fs18.readFileSync(gitignorePath, "utf8"));
|
|
245
246
|
}
|
|
246
247
|
if (exclude.length) ig.add(exclude);
|
|
247
248
|
return ig;
|
|
248
249
|
}
|
|
249
250
|
function discover(options) {
|
|
250
|
-
const root =
|
|
251
|
+
const root = path19.resolve(options.root);
|
|
251
252
|
const onlyLangs = (options.only ?? []).filter(Boolean);
|
|
252
253
|
const allowLang = (lang) => onlyLangs.length === 0 || onlyLangs.includes(lang.id);
|
|
253
254
|
for (const id of onlyLangs) {
|
|
@@ -256,27 +257,27 @@ function discover(options) {
|
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
const rootIg = buildRootIgnore(root, options.exclude ?? []);
|
|
259
|
-
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) =>
|
|
260
|
+
const scopeAbs = (options.paths && options.paths.length ? options.paths.map((p) => path19.resolve(root, p)) : [root]).filter((p) => fs18.existsSync(p));
|
|
260
261
|
const found = /* @__PURE__ */ new Map();
|
|
261
262
|
const considerFile = (abs) => {
|
|
262
|
-
const rel2 = toPosix(
|
|
263
|
+
const rel2 = toPosix(path19.relative(root, abs));
|
|
263
264
|
if (rel2.startsWith("..")) return;
|
|
264
265
|
if (rel2 === "" || rootIg.ignores(rel2)) return;
|
|
265
|
-
if (SKIP_FILES.has(
|
|
266
|
-
const lang = langForExtension(
|
|
266
|
+
if (SKIP_FILES.has(path19.basename(abs).toLowerCase())) return;
|
|
267
|
+
const lang = langForExtension(path19.extname(abs));
|
|
267
268
|
if (!lang || !allowLang(lang)) return;
|
|
268
269
|
found.set(rel2, { rel: rel2, abs, lang });
|
|
269
270
|
};
|
|
270
271
|
const walk = (dir) => {
|
|
271
272
|
let entries;
|
|
272
273
|
try {
|
|
273
|
-
entries =
|
|
274
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
274
275
|
} catch {
|
|
275
276
|
return;
|
|
276
277
|
}
|
|
277
278
|
for (const entry of entries) {
|
|
278
|
-
const abs =
|
|
279
|
-
const rel2 = toPosix(
|
|
279
|
+
const abs = path19.join(dir, entry.name);
|
|
280
|
+
const rel2 = toPosix(path19.relative(root, abs));
|
|
280
281
|
if (entry.isDirectory()) {
|
|
281
282
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
282
283
|
if (rel2 && rootIg.ignores(`${rel2}/`)) continue;
|
|
@@ -287,7 +288,7 @@ function discover(options) {
|
|
|
287
288
|
}
|
|
288
289
|
};
|
|
289
290
|
for (const scope of scopeAbs) {
|
|
290
|
-
const stat =
|
|
291
|
+
const stat = fs18.statSync(scope);
|
|
291
292
|
if (stat.isDirectory()) walk(scope);
|
|
292
293
|
else if (stat.isFile()) considerFile(scope);
|
|
293
294
|
}
|
|
@@ -540,25 +541,36 @@ function mulberry32(seed) {
|
|
|
540
541
|
var CLUSTER_SEED = 42;
|
|
541
542
|
|
|
542
543
|
// src/engine/analyze.ts
|
|
544
|
+
var TIER_LARGE_NODES = 5e3;
|
|
545
|
+
var TIER_XL_NODES = 5e4;
|
|
546
|
+
function pickAnalysisTier(nodeCount, forced) {
|
|
547
|
+
if (forced) return forced;
|
|
548
|
+
if (nodeCount > TIER_XL_NODES) return "xl";
|
|
549
|
+
if (nodeCount > TIER_LARGE_NODES) return "large";
|
|
550
|
+
return "full";
|
|
551
|
+
}
|
|
543
552
|
var DEP_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
|
|
544
553
|
var CLUSTER_EDGE_KINDS = /* @__PURE__ */ new Set([...DEP_EDGES, "contains"]);
|
|
545
554
|
var IMPORTANCE_WEIGHTS = { pagerank: 0.4, betweenness: 0.25, eigenvector: 0.2, degree: 0.15 };
|
|
546
555
|
var DEFAULT_BETWEENNESS_LIMIT = 5e3;
|
|
547
556
|
function analyze(nodes, edges, options = {}) {
|
|
548
|
-
const
|
|
557
|
+
const tier = pickAnalysisTier(nodes.length, options.tier);
|
|
558
|
+
const mode = options.cluster ?? (tier === "xl" ? "louvain" : "louvain");
|
|
549
559
|
const ids = [...nodes.map((n) => n.id)].sort((a, b) => a.localeCompare(b));
|
|
550
560
|
const dg = simpleGraph(ids, edges, DEP_EDGES, "directed");
|
|
551
561
|
const pr = safeMetric(() => pagerank(dg), ids);
|
|
552
|
-
const
|
|
553
|
-
const
|
|
562
|
+
const bwLimit = options.betweennessLimit ?? DEFAULT_BETWEENNESS_LIMIT;
|
|
563
|
+
const bw = tier === "full" && dg.order <= bwLimit ? safeMetric(() => betweenness(dg), ids) : zeros(ids);
|
|
564
|
+
const ev = tier === "full" || tier === "large" ? safeMetric(() => eigenvector(dg), ids) : zeros(ids);
|
|
554
565
|
const deg = degreeMap(dg, ids);
|
|
555
566
|
const prN = normalize(pr);
|
|
556
567
|
const bwN = normalize(bw);
|
|
557
568
|
const evN = normalize(ev);
|
|
558
569
|
const degN = normalize(deg);
|
|
570
|
+
const weights = tier === "xl" ? { pagerank: 0.7, betweenness: 0, eigenvector: 0, degree: 0.3 } : tier === "large" ? { pagerank: 0.5, betweenness: 0, eigenvector: 0.25, degree: 0.25 } : IMPORTANCE_WEIGHTS;
|
|
559
571
|
const importance = /* @__PURE__ */ new Map();
|
|
560
572
|
for (const id of ids) {
|
|
561
|
-
const score =
|
|
573
|
+
const score = weights.pagerank * (prN.get(id) ?? 0) + weights.betweenness * (bwN.get(id) ?? 0) + weights.eigenvector * (evN.get(id) ?? 0) + weights.degree * (degN.get(id) ?? 0);
|
|
562
574
|
importance.set(id, score);
|
|
563
575
|
}
|
|
564
576
|
const importanceN = normalize(importance);
|
|
@@ -567,7 +579,7 @@ function analyze(nodes, edges, options = {}) {
|
|
|
567
579
|
const variance = vals.reduce((a, b) => a + (b - mean) ** 2, 0) / (vals.length || 1);
|
|
568
580
|
const std = Math.sqrt(variance);
|
|
569
581
|
const hubThreshold = mean + 2 * std;
|
|
570
|
-
const { areaByNode, mode: actualMode } = cluster(ids, edges, mode);
|
|
582
|
+
const { areaByNode, mode: actualMode } = tier === "xl" ? clusterFileLevel(nodes, edges, mode) : cluster(ids, edges, mode);
|
|
571
583
|
const outNodes = nodes.map((n) => ({
|
|
572
584
|
...n,
|
|
573
585
|
centrality: {
|
|
@@ -582,7 +594,42 @@ function analyze(nodes, edges, options = {}) {
|
|
|
582
594
|
})).sort((a, b) => a.id.localeCompare(b.id));
|
|
583
595
|
const areas = buildAreas(outNodes, edges, areaByNode);
|
|
584
596
|
const outEdges = scoreSurprise(edges, areaByNode);
|
|
585
|
-
return { nodes: outNodes, edges: outEdges, areas, cluster: actualMode };
|
|
597
|
+
return { nodes: outNodes, edges: outEdges, areas, cluster: actualMode, tier };
|
|
598
|
+
}
|
|
599
|
+
function clusterFileLevel(nodes, edges, mode) {
|
|
600
|
+
const fileNodes = nodes.filter((n) => n.kind === "file");
|
|
601
|
+
const fileIds = fileNodes.map((n) => n.id).sort((a, b) => a.localeCompare(b));
|
|
602
|
+
if (fileIds.length === 0) {
|
|
603
|
+
return { areaByNode: new Map(nodes.map((n) => [n.id, -1])), mode: "none" };
|
|
604
|
+
}
|
|
605
|
+
const fileIdByPath = new Map(fileNodes.map((n) => [n.file, n.id]));
|
|
606
|
+
const fileOf = /* @__PURE__ */ new Map();
|
|
607
|
+
for (const n of nodes) {
|
|
608
|
+
if (n.kind === "file") fileOf.set(n.id, n.id);
|
|
609
|
+
else if (n.file) {
|
|
610
|
+
const fid = fileIdByPath.get(n.file);
|
|
611
|
+
if (fid) fileOf.set(n.id, fid);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const fileEdges = [];
|
|
615
|
+
const seen = /* @__PURE__ */ new Set();
|
|
616
|
+
for (const e of edges) {
|
|
617
|
+
if (!DEP_EDGES.has(e.kind)) continue;
|
|
618
|
+
const sf = fileOf.get(e.src);
|
|
619
|
+
const df = fileOf.get(e.dst);
|
|
620
|
+
if (!sf || !df || sf === df) continue;
|
|
621
|
+
const key = `${e.kind}|${sf}|${df}`;
|
|
622
|
+
if (seen.has(key)) continue;
|
|
623
|
+
seen.add(key);
|
|
624
|
+
fileEdges.push({ ...e, src: sf, dst: df, id: key });
|
|
625
|
+
}
|
|
626
|
+
const { areaByNode: fileAreas, mode: used } = cluster(fileIds, fileEdges, mode);
|
|
627
|
+
const areaByNode = /* @__PURE__ */ new Map();
|
|
628
|
+
for (const n of nodes) {
|
|
629
|
+
const fid = fileOf.get(n.id);
|
|
630
|
+
areaByNode.set(n.id, fid ? fileAreas.get(fid) ?? -1 : -1);
|
|
631
|
+
}
|
|
632
|
+
return { areaByNode, mode: used };
|
|
586
633
|
}
|
|
587
634
|
function simpleGraph(ids, edges, kinds, type) {
|
|
588
635
|
const g = new Graph({ type, multi: false, allowSelfLoops: false });
|
|
@@ -723,14 +770,14 @@ var DEFAULT_PATHS = [
|
|
|
723
770
|
];
|
|
724
771
|
function loadCoverage(root, explicit) {
|
|
725
772
|
const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
|
|
726
|
-
(p) =>
|
|
773
|
+
(p) => path19.resolve(root, p)
|
|
727
774
|
);
|
|
728
|
-
const found = candidates.filter((p) =>
|
|
775
|
+
const found = candidates.filter((p) => fs18.existsSync(p));
|
|
729
776
|
if (found.length === 0) return null;
|
|
730
777
|
const map = /* @__PURE__ */ new Map();
|
|
731
778
|
for (const file of found) {
|
|
732
779
|
try {
|
|
733
|
-
const text =
|
|
780
|
+
const text = fs18.readFileSync(file, "utf8");
|
|
734
781
|
if (file.endsWith(".json")) mergeIstanbul(map, text, root);
|
|
735
782
|
else mergeLcov(map, text, root);
|
|
736
783
|
} catch {
|
|
@@ -739,8 +786,8 @@ function loadCoverage(root, explicit) {
|
|
|
739
786
|
return map.size ? map : null;
|
|
740
787
|
}
|
|
741
788
|
function rel(root, p) {
|
|
742
|
-
const abs =
|
|
743
|
-
return
|
|
789
|
+
const abs = path19.isAbsolute(p) ? p : path19.resolve(root, p);
|
|
790
|
+
return path19.relative(root, abs).split(path19.sep).join("/");
|
|
744
791
|
}
|
|
745
792
|
function bump(map, file, line, hits) {
|
|
746
793
|
let lh = map.get(file);
|
|
@@ -1027,9 +1074,6 @@ function matchConfidence(entry, nameParts, imports, callees) {
|
|
|
1027
1074
|
}
|
|
1028
1075
|
return conf;
|
|
1029
1076
|
}
|
|
1030
|
-
|
|
1031
|
-
// src/schema.ts
|
|
1032
|
-
var SCHEMA_VERSION = "vg-graph/1.0";
|
|
1033
1077
|
var DEFAULT_INLINE_THRESHOLD = 24;
|
|
1034
1078
|
async function parseFiles(files, options = {}) {
|
|
1035
1079
|
const threshold = options.inlineThreshold ?? DEFAULT_INLINE_THRESHOLD;
|
|
@@ -1050,7 +1094,7 @@ async function parseInline(files, options) {
|
|
|
1050
1094
|
onProgress?.(0, files.length);
|
|
1051
1095
|
for (const file of files) {
|
|
1052
1096
|
try {
|
|
1053
|
-
const source =
|
|
1097
|
+
const source = fs18.readFileSync(file.abs, "utf8");
|
|
1054
1098
|
out.push(await parseSource(file.rel, file.lang.id, source));
|
|
1055
1099
|
} catch (err) {
|
|
1056
1100
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
@@ -1105,9 +1149,9 @@ function isWorkerOom(err) {
|
|
|
1105
1149
|
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1106
1150
|
}
|
|
1107
1151
|
function resolveWorkerFile() {
|
|
1108
|
-
const here =
|
|
1109
|
-
const candidate =
|
|
1110
|
-
return
|
|
1152
|
+
const here = path19.dirname(fileURLToPath(import.meta.url));
|
|
1153
|
+
const candidate = path19.join(here, "parse-worker.js");
|
|
1154
|
+
return fs18.existsSync(candidate) ? candidate : null;
|
|
1111
1155
|
}
|
|
1112
1156
|
function chunk(items, buckets) {
|
|
1113
1157
|
const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
|
|
@@ -1356,7 +1400,7 @@ function enclosing3(nodes, line) {
|
|
|
1356
1400
|
return best;
|
|
1357
1401
|
}
|
|
1358
1402
|
function normalize2(p) {
|
|
1359
|
-
return
|
|
1403
|
+
return path19.resolve(p).split(path19.sep).join("/");
|
|
1360
1404
|
}
|
|
1361
1405
|
|
|
1362
1406
|
// src/engine/epistemic.ts
|
|
@@ -1599,14 +1643,14 @@ var IMPORTANCE = {
|
|
|
1599
1643
|
"other-config": 0.08
|
|
1600
1644
|
};
|
|
1601
1645
|
function toPosix2(p) {
|
|
1602
|
-
return p.split(
|
|
1646
|
+
return p.split(path19.sep).join("/");
|
|
1603
1647
|
}
|
|
1604
1648
|
function buildRootIgnore2(root, exclude) {
|
|
1605
1649
|
const ig = ignore();
|
|
1606
|
-
const gitignorePath =
|
|
1607
|
-
if (
|
|
1650
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
1651
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
1608
1652
|
try {
|
|
1609
|
-
ig.add(
|
|
1653
|
+
ig.add(fs18.readFileSync(gitignorePath, "utf8"));
|
|
1610
1654
|
} catch {
|
|
1611
1655
|
}
|
|
1612
1656
|
}
|
|
@@ -1615,7 +1659,7 @@ function buildRootIgnore2(root, exclude) {
|
|
|
1615
1659
|
}
|
|
1616
1660
|
function classifyProjectContext(relOrName) {
|
|
1617
1661
|
const rel2 = relOrName.replace(/\\/g, "/");
|
|
1618
|
-
const base =
|
|
1662
|
+
const base = path19.basename(rel2);
|
|
1619
1663
|
const baseLower = base.toLowerCase();
|
|
1620
1664
|
if (/^\.env($|\.)/i.test(base) && !isEnvExampleName(baseLower)) return null;
|
|
1621
1665
|
if (SKIP_FILES.has(baseLower)) return null;
|
|
@@ -1628,7 +1672,7 @@ function classifyProjectContext(relOrName) {
|
|
|
1628
1672
|
for (const { re, category } of PATH_PATTERNS) {
|
|
1629
1673
|
if (re.test(rel2)) return category;
|
|
1630
1674
|
}
|
|
1631
|
-
const ext =
|
|
1675
|
+
const ext = path19.extname(baseLower);
|
|
1632
1676
|
if (DOC_EXTENSIONS.has(ext)) return ext === ".txt" ? "text" : "markdown";
|
|
1633
1677
|
if (API_EXTENSIONS.has(ext)) return "api-contract";
|
|
1634
1678
|
if ((ext === ".yaml" || ext === ".yml" || ext === ".json") && /openapi|swagger|asyncapi/i.test(base)) {
|
|
@@ -1646,24 +1690,24 @@ function isEnvExampleName(baseLower) {
|
|
|
1646
1690
|
return false;
|
|
1647
1691
|
}
|
|
1648
1692
|
function discoverDocs(options) {
|
|
1649
|
-
const root =
|
|
1693
|
+
const root = path19.resolve(options.root);
|
|
1650
1694
|
const maxFiles = options.maxFiles ?? DOC_MAX_FILES;
|
|
1651
1695
|
const rootIg = buildRootIgnore2(root, options.exclude ?? []);
|
|
1652
|
-
const scopeAbs = (options.paths?.length ? options.paths.map((p) =>
|
|
1653
|
-
(p) =>
|
|
1696
|
+
const scopeAbs = (options.paths?.length ? options.paths.map((p) => path19.resolve(root, p)) : [root]).filter(
|
|
1697
|
+
(p) => fs18.existsSync(p)
|
|
1654
1698
|
);
|
|
1655
1699
|
const found = /* @__PURE__ */ new Map();
|
|
1656
1700
|
const consider = (abs) => {
|
|
1657
1701
|
if (found.size >= maxFiles) return;
|
|
1658
|
-
const rel2 = toPosix2(
|
|
1702
|
+
const rel2 = toPosix2(path19.relative(root, abs));
|
|
1659
1703
|
if (!rel2 || rel2.startsWith("..")) return;
|
|
1660
1704
|
if (rootIg.ignores(rel2)) return;
|
|
1661
|
-
const base =
|
|
1705
|
+
const base = path19.basename(abs);
|
|
1662
1706
|
if (SKIP_FILES.has(base.toLowerCase())) return;
|
|
1663
1707
|
const category = classifyProjectContext(rel2);
|
|
1664
1708
|
if (!category) return;
|
|
1665
1709
|
try {
|
|
1666
|
-
const st =
|
|
1710
|
+
const st = fs18.statSync(abs);
|
|
1667
1711
|
if (!st.isFile() || st.size > DOC_FILE_MAX_BYTES) return;
|
|
1668
1712
|
if (st.size === 0) return;
|
|
1669
1713
|
} catch {
|
|
@@ -1675,14 +1719,14 @@ function discoverDocs(options) {
|
|
|
1675
1719
|
if (found.size >= maxFiles || depth > 12) return;
|
|
1676
1720
|
let entries;
|
|
1677
1721
|
try {
|
|
1678
|
-
entries =
|
|
1722
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
1679
1723
|
} catch {
|
|
1680
1724
|
return;
|
|
1681
1725
|
}
|
|
1682
1726
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1683
1727
|
if (found.size >= maxFiles) return;
|
|
1684
|
-
const abs =
|
|
1685
|
-
const rel2 = toPosix2(
|
|
1728
|
+
const abs = path19.join(dir, entry.name);
|
|
1729
|
+
const rel2 = toPosix2(path19.relative(root, abs));
|
|
1686
1730
|
if (entry.isDirectory()) {
|
|
1687
1731
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1688
1732
|
if (rel2 && rootIg.ignores(`${rel2}/`) && !rel2.startsWith(".github")) continue;
|
|
@@ -1694,7 +1738,7 @@ function discoverDocs(options) {
|
|
|
1694
1738
|
};
|
|
1695
1739
|
for (const scope of scopeAbs) {
|
|
1696
1740
|
try {
|
|
1697
|
-
const st =
|
|
1741
|
+
const st = fs18.statSync(scope);
|
|
1698
1742
|
if (st.isDirectory()) walk(scope, 0);
|
|
1699
1743
|
else if (st.isFile()) consider(scope);
|
|
1700
1744
|
} catch {
|
|
@@ -1705,7 +1749,7 @@ function discoverDocs(options) {
|
|
|
1705
1749
|
function prepareDocBody(rel2, raw, category) {
|
|
1706
1750
|
const scrubbed = redactSecrets(raw);
|
|
1707
1751
|
if (scrubbed.length <= DOC_BODY_MAX_CHARS) return scrubbed;
|
|
1708
|
-
if (category === "manifest" &&
|
|
1752
|
+
if (category === "manifest" && path19.basename(rel2).toLowerCase() === "package.json") {
|
|
1709
1753
|
return summarizePackageJson(scrubbed);
|
|
1710
1754
|
}
|
|
1711
1755
|
if (category === "api-contract" && /\.(ya?ml|json)$/i.test(rel2)) {
|
|
@@ -1751,14 +1795,14 @@ function documentNodesFromDocs(docs) {
|
|
|
1751
1795
|
for (const d of docs) {
|
|
1752
1796
|
let raw = "";
|
|
1753
1797
|
try {
|
|
1754
|
-
raw =
|
|
1798
|
+
raw = fs18.readFileSync(d.abs, "utf8");
|
|
1755
1799
|
} catch {
|
|
1756
1800
|
continue;
|
|
1757
1801
|
}
|
|
1758
1802
|
const category = d.category ?? d.kind;
|
|
1759
1803
|
const scrubbed = prepareDocBody(d.rel, raw, category);
|
|
1760
1804
|
if (!scrubbed.trim()) continue;
|
|
1761
|
-
const name =
|
|
1805
|
+
const name = path19.basename(d.rel);
|
|
1762
1806
|
const qualifiedName = d.rel.replace(/\\/g, "/");
|
|
1763
1807
|
const id = nodeId({
|
|
1764
1808
|
kind: "document",
|
|
@@ -1787,17 +1831,362 @@ function documentNodesFromDocs(docs) {
|
|
|
1787
1831
|
}
|
|
1788
1832
|
return nodes.sort((a, b) => a.file.localeCompare(b.file) || a.id.localeCompare(b.id));
|
|
1789
1833
|
}
|
|
1834
|
+
function shardTsProjects(root, files) {
|
|
1835
|
+
if (files.length === 0) return [];
|
|
1836
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1837
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1838
|
+
for (const f of files) {
|
|
1839
|
+
const dir = path19.dirname(f.abs);
|
|
1840
|
+
const key = nearestProjectKey(root, dir, cache);
|
|
1841
|
+
const list = byKey.get(key);
|
|
1842
|
+
if (list) list.push(f);
|
|
1843
|
+
else byKey.set(key, [f]);
|
|
1844
|
+
}
|
|
1845
|
+
return [...byKey.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([key, shardFiles]) => ({
|
|
1846
|
+
key,
|
|
1847
|
+
files: [...shardFiles].sort((a, b) => a.rel.localeCompare(b.rel))
|
|
1848
|
+
}));
|
|
1849
|
+
}
|
|
1850
|
+
function nearestProjectKey(root, startDir, cache) {
|
|
1851
|
+
const hit = cache.get(startDir);
|
|
1852
|
+
if (hit !== void 0) return hit;
|
|
1853
|
+
let dir = startDir;
|
|
1854
|
+
const rootAbs = path19.resolve(root);
|
|
1855
|
+
while (true) {
|
|
1856
|
+
if (fs18.existsSync(path19.join(dir, "tsconfig.json")) || fs18.existsSync(path19.join(dir, "tsconfig.base.json")) || fs18.existsSync(path19.join(dir, "package.json"))) {
|
|
1857
|
+
const key = toPosix3(path19.relative(rootAbs, dir)) || ".";
|
|
1858
|
+
cache.set(startDir, key);
|
|
1859
|
+
return key;
|
|
1860
|
+
}
|
|
1861
|
+
if (dir === rootAbs || path19.dirname(dir) === dir) {
|
|
1862
|
+
cache.set(startDir, ".");
|
|
1863
|
+
return ".";
|
|
1864
|
+
}
|
|
1865
|
+
dir = path19.dirname(dir);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
function toPosix3(p) {
|
|
1869
|
+
return p.split(path19.sep).join("/");
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// src/engine/timing.ts
|
|
1873
|
+
var StageTimer = class {
|
|
1874
|
+
marks = /* @__PURE__ */ new Map();
|
|
1875
|
+
durations = {};
|
|
1876
|
+
start(name) {
|
|
1877
|
+
this.marks.set(name, nowMs());
|
|
1878
|
+
}
|
|
1879
|
+
end(name) {
|
|
1880
|
+
const t0 = this.marks.get(name);
|
|
1881
|
+
if (t0 === void 0) return 0;
|
|
1882
|
+
const ms = Math.round((nowMs() - t0) * 1e3) / 1e3;
|
|
1883
|
+
this.durations[name] = ms;
|
|
1884
|
+
this.marks.delete(name);
|
|
1885
|
+
return ms;
|
|
1886
|
+
}
|
|
1887
|
+
/** Run `fn` under a named stage. */
|
|
1888
|
+
async measure(name, fn) {
|
|
1889
|
+
this.start(name);
|
|
1890
|
+
try {
|
|
1891
|
+
return await fn();
|
|
1892
|
+
} finally {
|
|
1893
|
+
this.end(name);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
snapshot() {
|
|
1897
|
+
return { ...this.durations };
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
function nowMs() {
|
|
1901
|
+
return Number(process.hrtime.bigint() / 1000n) / 1e3;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
// src/engine/summaries.ts
|
|
1905
|
+
var DEPEND_EDGES = /* @__PURE__ */ new Set(["call", "import", "extends", "implements", "references"]);
|
|
1906
|
+
var HUB_LIMIT = 40;
|
|
1907
|
+
function buildSummaries(graph) {
|
|
1908
|
+
const index = indexFor(graph);
|
|
1909
|
+
const candidates = graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external" && n.kind !== "document").sort((a, b) => b.importance - a.importance || a.qualifiedName.localeCompare(b.qualifiedName)).slice(0, HUB_LIMIT);
|
|
1910
|
+
const hubs = candidates.map((n) => {
|
|
1911
|
+
const d1 = reverseDependents(index, n.id, 1);
|
|
1912
|
+
const d2 = reverseDependents(index, n.id, 2);
|
|
1913
|
+
const files = /* @__PURE__ */ new Set();
|
|
1914
|
+
for (const id of d2) {
|
|
1915
|
+
const node = index.node(id);
|
|
1916
|
+
if (node?.file) files.add(node.file);
|
|
1917
|
+
}
|
|
1918
|
+
return {
|
|
1919
|
+
id: n.id,
|
|
1920
|
+
name: n.qualifiedName,
|
|
1921
|
+
kind: n.kind,
|
|
1922
|
+
file: n.file,
|
|
1923
|
+
importance: n.importance,
|
|
1924
|
+
direct: d1.size,
|
|
1925
|
+
depth2: d2.size,
|
|
1926
|
+
files: files.size,
|
|
1927
|
+
tested: n.tested
|
|
1928
|
+
};
|
|
1929
|
+
});
|
|
1930
|
+
hubs.sort((a, b) => b.importance - a.importance || a.name.localeCompare(b.name));
|
|
1931
|
+
return { hubs };
|
|
1932
|
+
}
|
|
1933
|
+
function reverseDependents(index, rootId, maxDepth) {
|
|
1934
|
+
const affected = /* @__PURE__ */ new Set();
|
|
1935
|
+
let frontier = [rootId];
|
|
1936
|
+
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
1937
|
+
const next = [];
|
|
1938
|
+
for (const id of frontier) {
|
|
1939
|
+
for (const e of index.in(id)) {
|
|
1940
|
+
if (!DEPEND_EDGES.has(e.kind)) continue;
|
|
1941
|
+
if (e.src === rootId || affected.has(e.src)) continue;
|
|
1942
|
+
affected.add(e.src);
|
|
1943
|
+
next.push(e.src);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
frontier = next;
|
|
1947
|
+
if (frontier.length === 0) break;
|
|
1948
|
+
}
|
|
1949
|
+
return affected;
|
|
1950
|
+
}
|
|
1951
|
+
var emptyNode = (partial) => ({
|
|
1952
|
+
...partial,
|
|
1953
|
+
span: partial.span ?? { start: 1, end: 1 },
|
|
1954
|
+
importance: 0,
|
|
1955
|
+
centrality: { degree: 0, pagerank: 0, betweenness: 0, eigenvector: 0 },
|
|
1956
|
+
area: -1,
|
|
1957
|
+
isHub: false,
|
|
1958
|
+
tested: null
|
|
1959
|
+
});
|
|
1960
|
+
function extractManifests(root, opts = {}) {
|
|
1961
|
+
const absRoot = path19.resolve(root);
|
|
1962
|
+
const ig = buildRootIgnore3(absRoot, opts.exclude ?? []);
|
|
1963
|
+
const scopes = (opts.paths?.length ? opts.paths : ["."]).map((p) => path19.resolve(absRoot, p)).filter((p) => fs18.existsSync(p));
|
|
1964
|
+
const found = /* @__PURE__ */ new Map();
|
|
1965
|
+
for (const scope of scopes) {
|
|
1966
|
+
walkManifests(absRoot, scope, ig, found);
|
|
1967
|
+
}
|
|
1968
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
1969
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1970
|
+
let deps = 0;
|
|
1971
|
+
for (const rel2 of [...found.keys()].sort()) {
|
|
1972
|
+
const abs = found.get(rel2);
|
|
1973
|
+
const base = path19.posix.basename(rel2);
|
|
1974
|
+
try {
|
|
1975
|
+
if (base === "package.json") {
|
|
1976
|
+
deps += ingestPackageJson(rel2, abs, nodes, edges);
|
|
1977
|
+
} else if (base === "go.mod") {
|
|
1978
|
+
deps += ingestGoMod(rel2, abs, nodes, edges);
|
|
1979
|
+
}
|
|
1980
|
+
} catch {
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
return {
|
|
1984
|
+
nodes: [...nodes.values()].sort((a, b) => a.id.localeCompare(b.id)),
|
|
1985
|
+
edges: [...edges.values()].sort(
|
|
1986
|
+
(a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
|
|
1987
|
+
),
|
|
1988
|
+
files: found.size,
|
|
1989
|
+
deps
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
function ingestPackageJson(rel2, abs, nodes, edges) {
|
|
1993
|
+
const raw = JSON.parse(fs18.readFileSync(abs, "utf8"));
|
|
1994
|
+
const pkgName = raw.name && String(raw.name).trim() || path19.posix.dirname(rel2) || ".";
|
|
1995
|
+
const localId = nodeId({ kind: "package", qualifiedName: pkgName, file: rel2 });
|
|
1996
|
+
nodes.set(
|
|
1997
|
+
localId,
|
|
1998
|
+
emptyNode({
|
|
1999
|
+
id: localId,
|
|
2000
|
+
kind: "package",
|
|
2001
|
+
name: pkgName.includes("/") ? pkgName.split("/").pop() : pkgName,
|
|
2002
|
+
qualifiedName: pkgName,
|
|
2003
|
+
file: rel2,
|
|
2004
|
+
lang: "json"
|
|
2005
|
+
})
|
|
2006
|
+
);
|
|
2007
|
+
const fileId = nodeId({ kind: "file", qualifiedName: rel2, file: rel2 });
|
|
2008
|
+
nodes.set(
|
|
2009
|
+
fileId,
|
|
2010
|
+
emptyNode({
|
|
2011
|
+
id: fileId,
|
|
2012
|
+
kind: "file",
|
|
2013
|
+
name: path19.posix.basename(rel2),
|
|
2014
|
+
qualifiedName: rel2,
|
|
2015
|
+
file: rel2,
|
|
2016
|
+
lang: "json"
|
|
2017
|
+
})
|
|
2018
|
+
);
|
|
2019
|
+
addEdge(edges, "contains", fileId, localId, 1);
|
|
2020
|
+
let n = 0;
|
|
2021
|
+
const depMaps = [raw.dependencies, raw.peerDependencies, raw.optionalDependencies];
|
|
2022
|
+
const names = /* @__PURE__ */ new Set();
|
|
2023
|
+
for (const m of depMaps) {
|
|
2024
|
+
if (!m || typeof m !== "object") continue;
|
|
2025
|
+
for (const name of Object.keys(m)) {
|
|
2026
|
+
if (name) names.add(name);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
for (const name of [...names].sort()) {
|
|
2030
|
+
const extId = ensureExternal(nodes, name);
|
|
2031
|
+
addEdge(edges, "import", localId, extId, 1);
|
|
2032
|
+
n++;
|
|
2033
|
+
}
|
|
2034
|
+
return n;
|
|
2035
|
+
}
|
|
2036
|
+
function ingestGoMod(rel2, abs, nodes, edges) {
|
|
2037
|
+
const text = fs18.readFileSync(abs, "utf8");
|
|
2038
|
+
let moduleName = path19.posix.dirname(rel2) || ".";
|
|
2039
|
+
const moduleMatch = /^\s*module\s+(\S+)/m.exec(text);
|
|
2040
|
+
if (moduleMatch) moduleName = moduleMatch[1];
|
|
2041
|
+
const localId = nodeId({ kind: "package", qualifiedName: moduleName, file: rel2 });
|
|
2042
|
+
nodes.set(
|
|
2043
|
+
localId,
|
|
2044
|
+
emptyNode({
|
|
2045
|
+
id: localId,
|
|
2046
|
+
kind: "package",
|
|
2047
|
+
name: moduleName.split("/").pop() ?? moduleName,
|
|
2048
|
+
qualifiedName: moduleName,
|
|
2049
|
+
file: rel2,
|
|
2050
|
+
lang: "go"
|
|
2051
|
+
})
|
|
2052
|
+
);
|
|
2053
|
+
const fileId = nodeId({ kind: "file", qualifiedName: rel2, file: rel2 });
|
|
2054
|
+
nodes.set(
|
|
2055
|
+
fileId,
|
|
2056
|
+
emptyNode({
|
|
2057
|
+
id: fileId,
|
|
2058
|
+
kind: "file",
|
|
2059
|
+
name: path19.posix.basename(rel2),
|
|
2060
|
+
qualifiedName: rel2,
|
|
2061
|
+
file: rel2,
|
|
2062
|
+
lang: "go"
|
|
2063
|
+
})
|
|
2064
|
+
);
|
|
2065
|
+
addEdge(edges, "contains", fileId, localId, 1);
|
|
2066
|
+
const reqNames = /* @__PURE__ */ new Set();
|
|
2067
|
+
const block = /require\s*\(([\s\S]*?)\)/g;
|
|
2068
|
+
let bm;
|
|
2069
|
+
while ((bm = block.exec(text)) !== null) {
|
|
2070
|
+
for (const line of bm[1].split("\n")) {
|
|
2071
|
+
const m = /^\s*(\S+)\s+v\S+/.exec(line);
|
|
2072
|
+
if (m && !line.trim().startsWith("//")) reqNames.add(m[1]);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
const single = /^\s*require\s+(\S+)\s+v\S+/gm;
|
|
2076
|
+
let sm;
|
|
2077
|
+
while ((sm = single.exec(text)) !== null) reqNames.add(sm[1]);
|
|
2078
|
+
let n = 0;
|
|
2079
|
+
for (const name of [...reqNames].sort()) {
|
|
2080
|
+
const extId = ensureExternal(nodes, name);
|
|
2081
|
+
addEdge(edges, "import", localId, extId, 1);
|
|
2082
|
+
n++;
|
|
2083
|
+
}
|
|
2084
|
+
return n;
|
|
2085
|
+
}
|
|
2086
|
+
function ensureExternal(nodes, name) {
|
|
2087
|
+
const id = nodeId({ kind: "external", qualifiedName: name, file: "" });
|
|
2088
|
+
if (!nodes.has(id)) {
|
|
2089
|
+
nodes.set(
|
|
2090
|
+
id,
|
|
2091
|
+
emptyNode({
|
|
2092
|
+
id,
|
|
2093
|
+
kind: "external",
|
|
2094
|
+
name,
|
|
2095
|
+
qualifiedName: name,
|
|
2096
|
+
file: "",
|
|
2097
|
+
lang: "external"
|
|
2098
|
+
})
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
return id;
|
|
2102
|
+
}
|
|
2103
|
+
function addEdge(edges, kind, src, dst, confidence) {
|
|
2104
|
+
if (src === dst) return;
|
|
2105
|
+
const id = edgeId(kind, src, dst);
|
|
2106
|
+
if (edges.has(id)) return;
|
|
2107
|
+
edges.set(id, {
|
|
2108
|
+
id,
|
|
2109
|
+
kind,
|
|
2110
|
+
src,
|
|
2111
|
+
dst,
|
|
2112
|
+
resolution: "heuristic",
|
|
2113
|
+
confidence,
|
|
2114
|
+
epistemic: "declared",
|
|
2115
|
+
count: 1
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
function buildRootIgnore3(root, exclude) {
|
|
2119
|
+
const ig = ignore();
|
|
2120
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
2121
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
2122
|
+
try {
|
|
2123
|
+
ig.add(fs18.readFileSync(gitignorePath, "utf8"));
|
|
2124
|
+
} catch {
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
if (exclude.length) ig.add(exclude);
|
|
2128
|
+
return ig;
|
|
2129
|
+
}
|
|
2130
|
+
function walkManifests(root, dir, ig, found) {
|
|
2131
|
+
let entries;
|
|
2132
|
+
try {
|
|
2133
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
2134
|
+
} catch {
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
for (const entry of entries) {
|
|
2138
|
+
const abs = path19.join(dir, entry.name);
|
|
2139
|
+
const rel2 = path19.relative(root, abs).split(path19.sep).join("/");
|
|
2140
|
+
if (entry.isDirectory()) {
|
|
2141
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
2142
|
+
if (rel2 && ig.ignores(`${rel2}/`)) continue;
|
|
2143
|
+
walkManifests(root, abs, ig, found);
|
|
2144
|
+
} else if (entry.isFile()) {
|
|
2145
|
+
const base = entry.name;
|
|
2146
|
+
if (base !== "package.json" && base !== "go.mod") continue;
|
|
2147
|
+
if (rel2 && ig.ignores(rel2)) continue;
|
|
2148
|
+
found.set(rel2, abs);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
async function hashFilesParallel(jobs, concurrency) {
|
|
2153
|
+
if (jobs.length === 0) return [];
|
|
2154
|
+
const limit = Math.max(
|
|
2155
|
+
1,
|
|
2156
|
+
Math.min(concurrency ?? Math.max(1, (os.cpus()?.length ?? 4) * 2), jobs.length)
|
|
2157
|
+
);
|
|
2158
|
+
const out = new Array(jobs.length);
|
|
2159
|
+
let next = 0;
|
|
2160
|
+
async function worker() {
|
|
2161
|
+
for (; ; ) {
|
|
2162
|
+
const i = next++;
|
|
2163
|
+
if (i >= jobs.length) return;
|
|
2164
|
+
const job = jobs[i];
|
|
2165
|
+
try {
|
|
2166
|
+
const buf = await fs8.readFile(job.abs);
|
|
2167
|
+
out[i] = { rel: job.rel, hash: hashBytes(buf), ok: true };
|
|
2168
|
+
} catch {
|
|
2169
|
+
out[i] = { rel: job.rel, hash: "", ok: false };
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
2174
|
+
return out;
|
|
2175
|
+
}
|
|
1790
2176
|
|
|
1791
2177
|
// src/engine/build.ts
|
|
1792
2178
|
async function buildGraph(options) {
|
|
1793
|
-
const
|
|
1794
|
-
|
|
2179
|
+
const timer = new StageTimer();
|
|
2180
|
+
timer.start("total");
|
|
2181
|
+
const root = path19.resolve(options.root);
|
|
2182
|
+
timer.start("discover");
|
|
1795
2183
|
const files = discover({
|
|
1796
2184
|
root,
|
|
1797
2185
|
only: options.only,
|
|
1798
2186
|
exclude: options.exclude,
|
|
1799
2187
|
paths: options.paths
|
|
1800
2188
|
});
|
|
2189
|
+
timer.end("discover");
|
|
1801
2190
|
const limits = resolveLimits(options.limits);
|
|
1802
2191
|
if (limits.maxFiles > 0 && files.length > limits.maxFiles) {
|
|
1803
2192
|
throw new ResourceLimitError(
|
|
@@ -1811,15 +2200,18 @@ async function buildGraph(options) {
|
|
|
1811
2200
|
grammars,
|
|
1812
2201
|
disabled: options.noCache
|
|
1813
2202
|
});
|
|
2203
|
+
timer.start("hash");
|
|
1814
2204
|
const hashes = /* @__PURE__ */ new Map();
|
|
1815
2205
|
const fileStats = [];
|
|
1816
2206
|
const toParse = [];
|
|
1817
2207
|
const reused = [];
|
|
1818
2208
|
const buildWarnings = [];
|
|
2209
|
+
let statHits = 0;
|
|
2210
|
+
const pendingHash = [];
|
|
1819
2211
|
for (const file of files) {
|
|
1820
2212
|
let stat;
|
|
1821
2213
|
try {
|
|
1822
|
-
stat =
|
|
2214
|
+
stat = fs18.statSync(file.abs);
|
|
1823
2215
|
} catch {
|
|
1824
2216
|
continue;
|
|
1825
2217
|
}
|
|
@@ -1835,18 +2227,47 @@ async function buildGraph(options) {
|
|
|
1835
2227
|
);
|
|
1836
2228
|
continue;
|
|
1837
2229
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2230
|
+
const byStat = options.noCache ? void 0 : cache.getByStat(file.rel, stat.mtimeMs, stat.size);
|
|
2231
|
+
if (byStat) {
|
|
2232
|
+
hashes.set(file.rel, byStat.hash);
|
|
2233
|
+
if (byStat.parse.hash !== byStat.hash) byStat.parse.hash = byStat.hash;
|
|
2234
|
+
fileStats.push({
|
|
2235
|
+
rel: file.rel,
|
|
2236
|
+
size: stat.size,
|
|
2237
|
+
mtimeMs: stat.mtimeMs,
|
|
2238
|
+
hash: byStat.hash
|
|
2239
|
+
});
|
|
2240
|
+
reused.push(byStat.parse);
|
|
2241
|
+
statHits++;
|
|
1842
2242
|
continue;
|
|
1843
2243
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2244
|
+
pendingHash.push({ file, size: stat.size, mtimeMs: stat.mtimeMs });
|
|
2245
|
+
}
|
|
2246
|
+
if (pendingHash.length) {
|
|
2247
|
+
const jobs = pendingHash.map((p) => ({ rel: p.file.rel, abs: p.file.abs }));
|
|
2248
|
+
const results = await hashFilesParallel(jobs, options.jobs);
|
|
2249
|
+
const byRel = new Map(results.map((r) => [r.rel, r]));
|
|
2250
|
+
for (const p of pendingHash) {
|
|
2251
|
+
const r = byRel.get(p.file.rel);
|
|
2252
|
+
if (!r || !r.ok) continue;
|
|
2253
|
+
hashes.set(p.file.rel, r.hash);
|
|
2254
|
+
fileStats.push({
|
|
2255
|
+
rel: p.file.rel,
|
|
2256
|
+
size: p.size,
|
|
2257
|
+
mtimeMs: p.mtimeMs,
|
|
2258
|
+
hash: r.hash
|
|
2259
|
+
});
|
|
2260
|
+
const cached2 = cache.get(p.file.rel, r.hash);
|
|
2261
|
+
if (cached2) {
|
|
2262
|
+
reused.push(cached2);
|
|
2263
|
+
cache.set(p.file.rel, cached2, { mtimeMs: p.mtimeMs, size: p.size });
|
|
2264
|
+
} else {
|
|
2265
|
+
toParse.push(p.file);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
1849
2268
|
}
|
|
2269
|
+
timer.end("hash");
|
|
2270
|
+
timer.start("parse");
|
|
1850
2271
|
const parsedNew = await parseFiles(toParse, {
|
|
1851
2272
|
jobs: options.jobs,
|
|
1852
2273
|
inline: options.inline,
|
|
@@ -1854,8 +2275,12 @@ async function buildGraph(options) {
|
|
|
1854
2275
|
grammarsDir: options.grammarsDir,
|
|
1855
2276
|
memoryBudgetMb: limits.memoryBudgetMb
|
|
1856
2277
|
});
|
|
2278
|
+
timer.end("parse");
|
|
1857
2279
|
checkMemoryBudget("parse", limits.memoryBudgetMb);
|
|
1858
|
-
for (const p of parsedNew)
|
|
2280
|
+
for (const p of parsedNew) {
|
|
2281
|
+
const st = fileStats.find((f) => f.rel === p.rel);
|
|
2282
|
+
cache.set(p.rel, p, st ? { mtimeMs: st.mtimeMs, size: st.size } : void 0);
|
|
2283
|
+
}
|
|
1859
2284
|
const currentRels = new Set(files.map((f) => f.rel));
|
|
1860
2285
|
cache.prune(currentRels);
|
|
1861
2286
|
cache.save();
|
|
@@ -1863,31 +2288,85 @@ async function buildGraph(options) {
|
|
|
1863
2288
|
(a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0
|
|
1864
2289
|
);
|
|
1865
2290
|
const warnings = [...buildWarnings, ...parses.flatMap((p) => p.warnings ?? [])];
|
|
2291
|
+
timer.start("resolve");
|
|
1866
2292
|
const moduleResolver = buildModuleResolver(root, new Set(parses.map((p) => p.rel)));
|
|
1867
2293
|
const resolved = resolve(parses, moduleResolver);
|
|
2294
|
+
timer.end("resolve");
|
|
1868
2295
|
checkMemoryBudget("resolve", limits.memoryBudgetMb);
|
|
2296
|
+
const manifests = extractManifests(root, {
|
|
2297
|
+
exclude: options.exclude,
|
|
2298
|
+
paths: options.paths
|
|
2299
|
+
});
|
|
2300
|
+
if (manifests.files > 0) {
|
|
2301
|
+
const byId = new Map(resolved.nodes.map((n) => [n.id, n]));
|
|
2302
|
+
for (const n of manifests.nodes) {
|
|
2303
|
+
if (!byId.has(n.id)) {
|
|
2304
|
+
byId.set(n.id, n);
|
|
2305
|
+
resolved.nodes.push(n);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
const edgeById = new Map(resolved.edges.map((e) => [e.id, e]));
|
|
2309
|
+
for (const e of manifests.edges) {
|
|
2310
|
+
if (!edgeById.has(e.id)) {
|
|
2311
|
+
edgeById.set(e.id, e);
|
|
2312
|
+
resolved.edges.push(e);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
resolved.nodes.sort((a, b) => a.id.localeCompare(b.id));
|
|
2316
|
+
resolved.edges.sort(
|
|
2317
|
+
(a, b) => a.kind.localeCompare(b.kind) || a.src.localeCompare(b.src) || a.dst.localeCompare(b.dst)
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
1869
2320
|
const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
|
|
1870
2321
|
let edges = resolved.edges;
|
|
1871
2322
|
const resolvers = [...resolved.stats.resolvers];
|
|
1872
2323
|
const preciseCoveredFiles = /* @__PURE__ */ new Set();
|
|
1873
2324
|
let tscStats;
|
|
1874
|
-
|
|
2325
|
+
const skipTsc = options.noTsc || options.fast;
|
|
2326
|
+
let tsFiles = skipTsc ? [] : 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 }));
|
|
1875
2327
|
if (limits.tscMaxFiles > 0 && tsFiles.length > limits.tscMaxFiles) {
|
|
1876
2328
|
warnings.push(
|
|
1877
2329
|
`typescript resolver skipped \u2014 ${tsFiles.length.toLocaleString()} TS/JS files exceed the ${limits.tscMaxFiles.toLocaleString()}-file limit; calls use the heuristic resolver (set VG_TSC_MAX_FILES to raise it, 0 to disable)`
|
|
1878
2330
|
);
|
|
1879
2331
|
tsFiles = [];
|
|
1880
2332
|
}
|
|
2333
|
+
timer.start("tsc");
|
|
1881
2334
|
if (tsFiles.length) {
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2335
|
+
const shards = shardTsProjects(root, tsFiles);
|
|
2336
|
+
let filesCovered = 0;
|
|
2337
|
+
let calls = 0;
|
|
2338
|
+
let jsx = 0;
|
|
2339
|
+
let heritage = 0;
|
|
2340
|
+
let resolvedCount = 0;
|
|
2341
|
+
const coveredAll = /* @__PURE__ */ new Set();
|
|
2342
|
+
const preciseAll = [];
|
|
2343
|
+
for (const shard of shards) {
|
|
2344
|
+
const res = tsResolveEdges(root, shard.files, resolved.nodes);
|
|
2345
|
+
if (res.stats.files === 0) continue;
|
|
2346
|
+
filesCovered += res.stats.files;
|
|
2347
|
+
calls += res.stats.calls;
|
|
2348
|
+
jsx += res.stats.jsx;
|
|
2349
|
+
heritage += res.stats.heritage;
|
|
2350
|
+
resolvedCount += res.stats.resolved;
|
|
2351
|
+
for (const f of res.coveredFiles) coveredAll.add(f);
|
|
2352
|
+
preciseAll.push(...res.edges);
|
|
2353
|
+
}
|
|
2354
|
+
if (filesCovered > 0) {
|
|
2355
|
+
edges = mergePreciseEdges(edges, preciseAll, coveredAll, nodeFileById);
|
|
2356
|
+
for (const f of coveredAll) preciseCoveredFiles.add(f);
|
|
1886
2357
|
if (!resolvers.includes("tsc")) resolvers.unshift("tsc");
|
|
1887
|
-
tscStats =
|
|
2358
|
+
tscStats = {
|
|
2359
|
+
files: filesCovered,
|
|
2360
|
+
calls,
|
|
2361
|
+
jsx,
|
|
2362
|
+
heritage,
|
|
2363
|
+
resolved: resolvedCount,
|
|
2364
|
+
shards: shards.length
|
|
2365
|
+
};
|
|
1888
2366
|
}
|
|
1889
2367
|
checkMemoryBudget("typescript resolution", limits.memoryBudgetMb);
|
|
1890
2368
|
}
|
|
2369
|
+
timer.end("tsc");
|
|
1891
2370
|
const scip = options.noScip ? null : loadScipIndex(root, options.scip);
|
|
1892
2371
|
let scipStats;
|
|
1893
2372
|
if (scip) {
|
|
@@ -1908,8 +2387,8 @@ async function buildGraph(options) {
|
|
|
1908
2387
|
});
|
|
1909
2388
|
for (const d of docs) {
|
|
1910
2389
|
try {
|
|
1911
|
-
const st =
|
|
1912
|
-
const hash = hashBytes(
|
|
2390
|
+
const st = fs18.statSync(d.abs);
|
|
2391
|
+
const hash = hashBytes(fs18.readFileSync(d.abs));
|
|
1913
2392
|
hashes.set(d.rel, hash);
|
|
1914
2393
|
fileStats.push({ rel: d.rel, size: st.size, mtimeMs: st.mtimeMs, hash });
|
|
1915
2394
|
} catch {
|
|
@@ -1917,7 +2396,12 @@ async function buildGraph(options) {
|
|
|
1917
2396
|
}
|
|
1918
2397
|
const docNodes = documentNodesFromDocs(docs);
|
|
1919
2398
|
if (docNodes.length) nodes = [...nodes, ...docNodes];
|
|
1920
|
-
|
|
2399
|
+
timer.start("analyze");
|
|
2400
|
+
const analysis = analyze(nodes, linked.edges, {
|
|
2401
|
+
cluster: options.cluster,
|
|
2402
|
+
tier: options.analysisTier
|
|
2403
|
+
});
|
|
2404
|
+
timer.end("analyze");
|
|
1921
2405
|
checkMemoryBudget("analysis", limits.memoryBudgetMb);
|
|
1922
2406
|
const languages = [.../* @__PURE__ */ new Set([...parses.map((p) => p.lang), ...docNodes.map((n) => n.lang)])].sort();
|
|
1923
2407
|
const edgeKinds = [...new Set(analysis.edges.map((e) => e.kind))].sort();
|
|
@@ -1945,8 +2429,9 @@ async function buildGraph(options) {
|
|
|
1945
2429
|
toolchain
|
|
1946
2430
|
},
|
|
1947
2431
|
meta: {
|
|
1948
|
-
root:
|
|
2432
|
+
root: path19.basename(root) === "" ? "." : ".",
|
|
1949
2433
|
languages,
|
|
2434
|
+
analysisTier: analysis.tier,
|
|
1950
2435
|
counts: {
|
|
1951
2436
|
nodes: analysis.nodes.length,
|
|
1952
2437
|
edges: analysis.edges.length,
|
|
@@ -1964,7 +2449,8 @@ async function buildGraph(options) {
|
|
|
1964
2449
|
const survivingIds = new Set(analysis.nodes.map((n) => n.id));
|
|
1965
2450
|
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 }));
|
|
1966
2451
|
if (unknowns.length) graph.unknowns = unknowns;
|
|
1967
|
-
|
|
2452
|
+
graph.summaries = buildSummaries(graph);
|
|
2453
|
+
{
|
|
1968
2454
|
const facts = buildFacts(parses, analysis.nodes, analysis.edges);
|
|
1969
2455
|
if (facts.length) graph.facts = facts;
|
|
1970
2456
|
}
|
|
@@ -1972,16 +2458,27 @@ async function buildGraph(options) {
|
|
|
1972
2458
|
const grounding = groundGraph(analysis.nodes, analysis.edges, parses);
|
|
1973
2459
|
if (grounding.length) graph.grounding = grounding;
|
|
1974
2460
|
}
|
|
2461
|
+
let indexResult;
|
|
2462
|
+
if (!options.noIndex) {
|
|
2463
|
+
timer.start("index");
|
|
2464
|
+
const wr = writeGraphIndex(root, graph);
|
|
2465
|
+
indexResult = { ok: wr.ok, path: wr.ok ? wr.path : void 0, reason: wr.reason };
|
|
2466
|
+
timer.end("index");
|
|
2467
|
+
}
|
|
2468
|
+
timer.end("total");
|
|
2469
|
+
const stages = timer.snapshot();
|
|
1975
2470
|
return {
|
|
1976
2471
|
graph,
|
|
1977
|
-
timing: { totalMs:
|
|
2472
|
+
timing: { totalMs: stages.total ?? 0, stages },
|
|
1978
2473
|
reparsed: parsedNew.length,
|
|
1979
2474
|
reused: reused.length,
|
|
2475
|
+
statHits,
|
|
1980
2476
|
totalFiles: files.length,
|
|
1981
2477
|
fileStats,
|
|
1982
2478
|
resolveStats: resolved.stats,
|
|
1983
2479
|
tsc: tscStats,
|
|
1984
2480
|
scip: scipStats,
|
|
2481
|
+
index: indexResult,
|
|
1985
2482
|
warnings
|
|
1986
2483
|
};
|
|
1987
2484
|
}
|
|
@@ -1991,14 +2488,14 @@ function toRepoRel(p) {
|
|
|
1991
2488
|
function loadScipIndex(root, explicit) {
|
|
1992
2489
|
const candidates = [
|
|
1993
2490
|
explicit,
|
|
1994
|
-
|
|
1995
|
-
|
|
2491
|
+
path19.join(root, "index.scip"),
|
|
2492
|
+
path19.join(root, ".vibgrate", "index.scip")
|
|
1996
2493
|
].filter((p) => Boolean(p));
|
|
1997
2494
|
for (const file of candidates) {
|
|
1998
|
-
const abs =
|
|
1999
|
-
if (!
|
|
2495
|
+
const abs = path19.isAbsolute(file) ? file : path19.resolve(root, file);
|
|
2496
|
+
if (!fs18.existsSync(abs)) continue;
|
|
2000
2497
|
try {
|
|
2001
|
-
const index = decodeScipIndex(new Uint8Array(
|
|
2498
|
+
const index = decodeScipIndex(new Uint8Array(fs18.readFileSync(abs)));
|
|
2002
2499
|
if (index.documents.length) {
|
|
2003
2500
|
return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
|
|
2004
2501
|
}
|
|
@@ -2047,9 +2544,6 @@ function computeCorpusHash(parses, hashes) {
|
|
|
2047
2544
|
const list = [...byRel.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
2048
2545
|
return hashString(canonicalize(list));
|
|
2049
2546
|
}
|
|
2050
|
-
function nowMs() {
|
|
2051
|
-
return Number(process.hrtime.bigint() / 1000n) / 1e3;
|
|
2052
|
-
}
|
|
2053
2547
|
|
|
2054
2548
|
// src/engine/verify.ts
|
|
2055
2549
|
var PINNED = "1970-01-01T00:00:00.000Z";
|
|
@@ -2116,21 +2610,21 @@ function usageError(message) {
|
|
|
2116
2610
|
}
|
|
2117
2611
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2118
2612
|
function snapshotPath(root) {
|
|
2119
|
-
return
|
|
2613
|
+
return path19.join(cacheDir(root), "freshness.json");
|
|
2120
2614
|
}
|
|
2121
2615
|
function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
|
|
2122
2616
|
const files = {};
|
|
2123
2617
|
for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
|
|
2124
2618
|
const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
|
|
2125
2619
|
try {
|
|
2126
|
-
|
|
2127
|
-
|
|
2620
|
+
fs18.mkdirSync(cacheDir(root), { recursive: true });
|
|
2621
|
+
fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2128
2622
|
} catch {
|
|
2129
2623
|
}
|
|
2130
2624
|
}
|
|
2131
2625
|
function loadSnapshot(root) {
|
|
2132
2626
|
try {
|
|
2133
|
-
const loaded = JSON.parse(
|
|
2627
|
+
const loaded = JSON.parse(fs18.readFileSync(snapshotPath(root), "utf8"));
|
|
2134
2628
|
if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
|
|
2135
2629
|
return { ...loaded, scope: loaded.scope ?? {} };
|
|
2136
2630
|
}
|
|
@@ -2150,7 +2644,14 @@ function probeFreshness(root) {
|
|
|
2150
2644
|
const { scope } = snapshot;
|
|
2151
2645
|
let discovered;
|
|
2152
2646
|
try {
|
|
2153
|
-
|
|
2647
|
+
const code = discover({ root, only: scope.only, exclude: scope.exclude, paths: scope.paths });
|
|
2648
|
+
const docs = discoverDocs({ root, exclude: scope.exclude, paths: scope.paths });
|
|
2649
|
+
const byRel = /* @__PURE__ */ new Map();
|
|
2650
|
+
for (const f of code) byRel.set(f.rel, f.abs);
|
|
2651
|
+
for (const d of docs) {
|
|
2652
|
+
if (!byRel.has(d.rel)) byRel.set(d.rel, d.abs);
|
|
2653
|
+
}
|
|
2654
|
+
discovered = [...byRel.entries()].map(([rel2, abs]) => ({ rel: rel2, abs })).sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
2154
2655
|
} catch {
|
|
2155
2656
|
return null;
|
|
2156
2657
|
}
|
|
@@ -2166,15 +2667,25 @@ function probeFreshness(root) {
|
|
|
2166
2667
|
}
|
|
2167
2668
|
let stat;
|
|
2168
2669
|
try {
|
|
2169
|
-
stat =
|
|
2670
|
+
stat = fs18.statSync(file.abs);
|
|
2170
2671
|
} catch {
|
|
2171
2672
|
drift.removed.push(file.rel);
|
|
2172
2673
|
continue;
|
|
2173
2674
|
}
|
|
2174
2675
|
if (stat.size === entry.size && stat.mtimeMs === entry.mtimeMs) continue;
|
|
2676
|
+
if (entry.hash.startsWith("vg:oversize:")) {
|
|
2677
|
+
const expected = `vg:oversize:${stat.size}`;
|
|
2678
|
+
if (entry.hash === expected) {
|
|
2679
|
+
snapshot.files[file.rel] = { size: stat.size, mtimeMs: stat.mtimeMs, hash: entry.hash };
|
|
2680
|
+
absorbed = true;
|
|
2681
|
+
} else {
|
|
2682
|
+
drift.changed.push(file.rel);
|
|
2683
|
+
}
|
|
2684
|
+
continue;
|
|
2685
|
+
}
|
|
2175
2686
|
let hash = "";
|
|
2176
2687
|
try {
|
|
2177
|
-
hash = hashBytes(
|
|
2688
|
+
hash = hashBytes(fs18.readFileSync(file.abs));
|
|
2178
2689
|
} catch {
|
|
2179
2690
|
drift.removed.push(file.rel);
|
|
2180
2691
|
continue;
|
|
@@ -2191,7 +2702,7 @@ function probeFreshness(root) {
|
|
|
2191
2702
|
}
|
|
2192
2703
|
if (absorbed && !hasDrift(drift)) {
|
|
2193
2704
|
try {
|
|
2194
|
-
|
|
2705
|
+
fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2195
2706
|
} catch {
|
|
2196
2707
|
}
|
|
2197
2708
|
}
|
|
@@ -2237,6 +2748,30 @@ function shortestPath(graph, srcId, dstId) {
|
|
|
2237
2748
|
if (reverse) return { ids: reverse.slice().reverse(), direction: "reverse" };
|
|
2238
2749
|
return null;
|
|
2239
2750
|
}
|
|
2751
|
+
var NEIGHBOR_CAP = 6;
|
|
2752
|
+
function pathDisconnect(graph, srcId, dstId) {
|
|
2753
|
+
const idx = indexFor(graph);
|
|
2754
|
+
const from = neighborhood(idx, srcId);
|
|
2755
|
+
const to = neighborhood(idx, dstId);
|
|
2756
|
+
const hint = from.calls.length || from.calledBy.length || to.calls.length || to.calledBy.length ? "No direct call/import chain links these symbols. Try `vg show` / `vg impact` on each end, or path via a shared hub." : "Neither endpoint has call/import neighbors in the map \u2014 they may be isolated docs/externals, or the link needs a more precise build.";
|
|
2757
|
+
return { connected: false, from, to, hint };
|
|
2758
|
+
}
|
|
2759
|
+
function neighborhood(idx, id) {
|
|
2760
|
+
const node = idx.node(id);
|
|
2761
|
+
const name = node?.qualifiedName ?? id;
|
|
2762
|
+
const calls = uniqueNames(idx.callees(id).map((x) => x.node.qualifiedName));
|
|
2763
|
+
const calledBy = uniqueNames(idx.callers(id).map((x) => x.node.qualifiedName));
|
|
2764
|
+
const imports = uniqueNames(
|
|
2765
|
+
idx.out(id, "import").map((e) => idx.node(e.dst)?.qualifiedName).filter((n) => !!n)
|
|
2766
|
+
);
|
|
2767
|
+
const importedBy = uniqueNames(
|
|
2768
|
+
idx.in(id, "import").map((e) => idx.node(e.src)?.qualifiedName).filter((n) => !!n)
|
|
2769
|
+
);
|
|
2770
|
+
return { name, calls, calledBy, imports, importedBy };
|
|
2771
|
+
}
|
|
2772
|
+
function uniqueNames(names) {
|
|
2773
|
+
return [...new Set(names)].sort((a, b) => a.localeCompare(b)).slice(0, NEIGHBOR_CAP);
|
|
2774
|
+
}
|
|
2240
2775
|
function coveringTests(graph, node, index) {
|
|
2241
2776
|
const idx = index ?? indexFor(graph);
|
|
2242
2777
|
const byId = idx.nodeById;
|
|
@@ -2272,10 +2807,10 @@ function testsToRun(graph, rootId, depth = 4) {
|
|
|
2272
2807
|
};
|
|
2273
2808
|
}
|
|
2274
2809
|
function detectRunner(root, lang) {
|
|
2275
|
-
const pkgPath =
|
|
2276
|
-
if (
|
|
2810
|
+
const pkgPath = path19.join(root, "package.json");
|
|
2811
|
+
if (fs18.existsSync(pkgPath)) {
|
|
2277
2812
|
try {
|
|
2278
|
-
const pkg = JSON.parse(
|
|
2813
|
+
const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf8"));
|
|
2279
2814
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2280
2815
|
if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
|
|
2281
2816
|
if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
|
|
@@ -2283,10 +2818,10 @@ function detectRunner(root, lang) {
|
|
|
2283
2818
|
} catch {
|
|
2284
2819
|
}
|
|
2285
2820
|
}
|
|
2286
|
-
if (lang === "py" ||
|
|
2821
|
+
if (lang === "py" || fs18.existsSync(path19.join(root, "pyproject.toml")) || fs18.existsSync(path19.join(root, "pytest.ini"))) {
|
|
2287
2822
|
return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
|
|
2288
2823
|
}
|
|
2289
|
-
if (lang === "go" ||
|
|
2824
|
+
if (lang === "go" || fs18.existsSync(path19.join(root, "go.mod"))) {
|
|
2290
2825
|
return { name: "go test", command: () => "go test ./..." };
|
|
2291
2826
|
}
|
|
2292
2827
|
if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
|
|
@@ -2297,16 +2832,16 @@ function detectRunner(root, lang) {
|
|
|
2297
2832
|
}
|
|
2298
2833
|
function hasFile(root, re) {
|
|
2299
2834
|
try {
|
|
2300
|
-
return
|
|
2835
|
+
return fs18.readdirSync(root).some((f) => re.test(f));
|
|
2301
2836
|
} catch {
|
|
2302
2837
|
return false;
|
|
2303
2838
|
}
|
|
2304
2839
|
}
|
|
2305
2840
|
function findGitRoot(startDir = process.cwd()) {
|
|
2306
|
-
let dir =
|
|
2841
|
+
let dir = path19.resolve(startDir);
|
|
2307
2842
|
for (; ; ) {
|
|
2308
|
-
if (
|
|
2309
|
-
const parent =
|
|
2843
|
+
if (fs18.existsSync(path19.join(dir, ".git"))) return dir;
|
|
2844
|
+
const parent = path19.dirname(dir);
|
|
2310
2845
|
if (parent === dir) return null;
|
|
2311
2846
|
dir = parent;
|
|
2312
2847
|
}
|
|
@@ -2317,19 +2852,19 @@ function normalizeEntry(line) {
|
|
|
2317
2852
|
function ensureGitignored(entry, startDir = process.cwd()) {
|
|
2318
2853
|
const root = findGitRoot(startDir);
|
|
2319
2854
|
if (!root) return { status: "not-a-repo", entry };
|
|
2320
|
-
const gitignorePath =
|
|
2855
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
2321
2856
|
const target = normalizeEntry(entry);
|
|
2322
2857
|
let existing = "";
|
|
2323
2858
|
let fileExisted = true;
|
|
2324
2859
|
try {
|
|
2325
|
-
existing =
|
|
2860
|
+
existing = fs18.readFileSync(gitignorePath, "utf8");
|
|
2326
2861
|
} catch {
|
|
2327
2862
|
fileExisted = false;
|
|
2328
2863
|
}
|
|
2329
2864
|
const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
|
|
2330
2865
|
if (alreadyPresent) return { status: "present", entry, gitignorePath };
|
|
2331
2866
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
2332
|
-
|
|
2867
|
+
fs18.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
|
|
2333
2868
|
`, "utf8");
|
|
2334
2869
|
return { status: fileExisted ? "added" : "created", entry, gitignorePath };
|
|
2335
2870
|
}
|
|
@@ -2483,14 +3018,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
2483
3018
|
console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
2484
3019
|
}
|
|
2485
3020
|
if (opts.write) {
|
|
2486
|
-
const writePath =
|
|
3021
|
+
const writePath = path19.resolve(opts.write);
|
|
2487
3022
|
await writeTextFile(writePath, dsn + "\n");
|
|
2488
3023
|
console.log("");
|
|
2489
3024
|
console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
2490
|
-
const root = findGitRoot(
|
|
2491
|
-
const rel2 = root ?
|
|
3025
|
+
const root = findGitRoot(path19.dirname(writePath));
|
|
3026
|
+
const rel2 = root ? path19.relative(root, writePath).split(path19.sep).join("/") : "";
|
|
2492
3027
|
if (root && rel2 && !rel2.startsWith("..")) {
|
|
2493
|
-
const res = ensureGitignored(rel2,
|
|
3028
|
+
const res = ensureGitignored(rel2, path19.dirname(writePath));
|
|
2494
3029
|
if (res.status === "created") {
|
|
2495
3030
|
console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
|
|
2496
3031
|
} else if (res.status === "added") {
|
|
@@ -2508,30 +3043,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
2508
3043
|
var STORE_DIRNAME = ".vibgrate";
|
|
2509
3044
|
var STORE_FILENAME = "credentials.json";
|
|
2510
3045
|
function homeCredentialsPath() {
|
|
2511
|
-
return
|
|
3046
|
+
return path19.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
|
|
2512
3047
|
}
|
|
2513
3048
|
function projectCredentialsPath(cwd = process.cwd()) {
|
|
2514
3049
|
const root = findGitRoot(cwd) ?? cwd;
|
|
2515
|
-
return
|
|
3050
|
+
return path19.join(root, STORE_DIRNAME, STORE_FILENAME);
|
|
2516
3051
|
}
|
|
2517
3052
|
function credentialsPath(opts = {}) {
|
|
2518
3053
|
const override = process.env.VIBGRATE_CREDENTIALS?.trim();
|
|
2519
|
-
if (override) return
|
|
3054
|
+
if (override) return path19.resolve(override);
|
|
2520
3055
|
const local = projectCredentialsPath(opts.cwd);
|
|
2521
3056
|
if (opts.local) return local;
|
|
2522
|
-
if (
|
|
3057
|
+
if (fs18.existsSync(local)) return local;
|
|
2523
3058
|
return homeCredentialsPath();
|
|
2524
3059
|
}
|
|
2525
3060
|
function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
|
|
2526
|
-
const rel2 =
|
|
2527
|
-
if (rel2 && !rel2.startsWith("..") && !
|
|
2528
|
-
return rel2.split(
|
|
3061
|
+
const rel2 = path19.relative(repoRoot, credsFile);
|
|
3062
|
+
if (rel2 && !rel2.startsWith("..") && !path19.isAbsolute(rel2)) {
|
|
3063
|
+
return rel2.split(path19.sep).join("/");
|
|
2529
3064
|
}
|
|
2530
3065
|
return `${STORE_DIRNAME}/${STORE_FILENAME}`;
|
|
2531
3066
|
}
|
|
2532
3067
|
function readStoredCredentials(opts = {}) {
|
|
2533
3068
|
try {
|
|
2534
|
-
const raw =
|
|
3069
|
+
const raw = fs18.readFileSync(credentialsPath(opts), "utf8");
|
|
2535
3070
|
const parsed = JSON.parse(raw);
|
|
2536
3071
|
return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
|
|
2537
3072
|
} catch {
|
|
@@ -2540,16 +3075,16 @@ function readStoredCredentials(opts = {}) {
|
|
|
2540
3075
|
}
|
|
2541
3076
|
function writeStoredCredentials(creds, opts = {}) {
|
|
2542
3077
|
const file = credentialsPath(opts);
|
|
2543
|
-
|
|
2544
|
-
|
|
3078
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
3079
|
+
fs18.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
2545
3080
|
try {
|
|
2546
|
-
|
|
3081
|
+
fs18.chmodSync(file, 384);
|
|
2547
3082
|
} catch {
|
|
2548
3083
|
}
|
|
2549
3084
|
}
|
|
2550
3085
|
function clearStoredCredentials(opts = {}) {
|
|
2551
3086
|
try {
|
|
2552
|
-
|
|
3087
|
+
fs18.rmSync(credentialsPath(opts));
|
|
2553
3088
|
return true;
|
|
2554
3089
|
} catch {
|
|
2555
3090
|
return false;
|
|
@@ -2920,7 +3455,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
2920
3455
|
if (opts.strict) process.exit(1);
|
|
2921
3456
|
return;
|
|
2922
3457
|
}
|
|
2923
|
-
const filePath =
|
|
3458
|
+
const filePath = path19.resolve(opts.file);
|
|
2924
3459
|
if (!await pathExists(filePath)) {
|
|
2925
3460
|
console.error(chalk.red(`Scan artifact not found: ${filePath}`));
|
|
2926
3461
|
console.error(chalk.dim('Run "vibgrate scan" first.'));
|
|
@@ -3015,7 +3550,7 @@ function measure(root, files) {
|
|
|
3015
3550
|
for (const rel2 of files) {
|
|
3016
3551
|
let size;
|
|
3017
3552
|
try {
|
|
3018
|
-
size =
|
|
3553
|
+
size = fs18.statSync(path19.join(root, rel2)).size;
|
|
3019
3554
|
} catch {
|
|
3020
3555
|
continue;
|
|
3021
3556
|
}
|
|
@@ -3038,9 +3573,9 @@ function matchLines(text, needleLower) {
|
|
|
3038
3573
|
}
|
|
3039
3574
|
function readCandidate(abs, knownSize) {
|
|
3040
3575
|
try {
|
|
3041
|
-
const size = knownSize ??
|
|
3576
|
+
const size = knownSize ?? fs18.statSync(abs).size;
|
|
3042
3577
|
if (size > MAX_FILE_BYTES) return null;
|
|
3043
|
-
const text =
|
|
3578
|
+
const text = fs18.readFileSync(abs, "utf8");
|
|
3044
3579
|
return text.includes(NUL) ? null : text;
|
|
3045
3580
|
} catch {
|
|
3046
3581
|
return null;
|
|
@@ -3051,7 +3586,7 @@ function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
|
|
|
3051
3586
|
let total = 0;
|
|
3052
3587
|
let truncated = false;
|
|
3053
3588
|
for (const rel2 of files) {
|
|
3054
|
-
const text = readCandidate(
|
|
3589
|
+
const text = readCandidate(path19.join(root, rel2), sizes?.get(rel2));
|
|
3055
3590
|
if (text === null) continue;
|
|
3056
3591
|
for (const h of matchLines(text, needleLower)) {
|
|
3057
3592
|
total++;
|
|
@@ -3118,12 +3653,12 @@ var WORKER_SOURCE = [
|
|
|
3118
3653
|
"parentPort.postMessage({ hits, total, truncated });"
|
|
3119
3654
|
].join("\n");
|
|
3120
3655
|
function runWorker(root, files, needleLower) {
|
|
3121
|
-
return new Promise((
|
|
3656
|
+
return new Promise((resolve13, reject) => {
|
|
3122
3657
|
const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
|
|
3123
3658
|
let settled = false;
|
|
3124
3659
|
w.once("message", (m) => {
|
|
3125
3660
|
settled = true;
|
|
3126
|
-
|
|
3661
|
+
resolve13(m);
|
|
3127
3662
|
});
|
|
3128
3663
|
w.once("error", reject);
|
|
3129
3664
|
w.once("exit", (code) => {
|
|
@@ -3192,10 +3727,10 @@ var BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
|
3192
3727
|
".pdf"
|
|
3193
3728
|
]);
|
|
3194
3729
|
function hasBinaryExt(rel2) {
|
|
3195
|
-
return BINARY_EXTS.has(
|
|
3730
|
+
return BINARY_EXTS.has(path19.extname(rel2).toLowerCase());
|
|
3196
3731
|
}
|
|
3197
|
-
function
|
|
3198
|
-
return
|
|
3732
|
+
function toPosix4(rel2) {
|
|
3733
|
+
return path19.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
|
|
3199
3734
|
}
|
|
3200
3735
|
async function searchSymbols(graph, root, query, limit) {
|
|
3201
3736
|
let q = query.trim();
|
|
@@ -3334,13 +3869,13 @@ function walkCandidates(root) {
|
|
|
3334
3869
|
const dir = stack.pop();
|
|
3335
3870
|
let entries;
|
|
3336
3871
|
try {
|
|
3337
|
-
entries =
|
|
3872
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
3338
3873
|
} catch {
|
|
3339
3874
|
continue;
|
|
3340
3875
|
}
|
|
3341
3876
|
for (const e of entries) {
|
|
3342
3877
|
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
3343
|
-
const abs =
|
|
3878
|
+
const abs = path19.join(dir, e.name);
|
|
3344
3879
|
if (e.isDirectory()) {
|
|
3345
3880
|
stack.push(abs);
|
|
3346
3881
|
continue;
|
|
@@ -3350,7 +3885,7 @@ function walkCandidates(root) {
|
|
|
3350
3885
|
truncated = true;
|
|
3351
3886
|
continue;
|
|
3352
3887
|
}
|
|
3353
|
-
files.push(
|
|
3888
|
+
files.push(toPosix4(path19.relative(root, abs)));
|
|
3354
3889
|
}
|
|
3355
3890
|
}
|
|
3356
3891
|
files.sort();
|
|
@@ -3381,12 +3916,12 @@ function ripgrepCandidates(root, needle) {
|
|
|
3381
3916
|
if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
|
|
3382
3917
|
return { files, truncated: false };
|
|
3383
3918
|
}
|
|
3384
|
-
function parseRgFileList(stdout,
|
|
3919
|
+
function parseRgFileList(stdout, sep10 = path19.sep) {
|
|
3385
3920
|
const seen = /* @__PURE__ */ new Set();
|
|
3386
3921
|
for (const raw of stdout.split("\n")) {
|
|
3387
3922
|
if (!raw) continue;
|
|
3388
|
-
const
|
|
3389
|
-
const rel2 =
|
|
3923
|
+
const posix2 = sep10 === "\\" ? raw.replaceAll("\\", "/") : raw;
|
|
3924
|
+
const rel2 = posix2.startsWith("./") ? posix2.slice(2) : posix2;
|
|
3390
3925
|
if (!rel2) continue;
|
|
3391
3926
|
const segs = rel2.split("/");
|
|
3392
3927
|
if (segs.some((s) => s.startsWith(".") || IGNORE_DIRS.has(s))) continue;
|
|
@@ -3397,8 +3932,8 @@ function parseRgFileList(stdout, sep8 = path17.sep) {
|
|
|
3397
3932
|
}
|
|
3398
3933
|
function readScanArtifact(root) {
|
|
3399
3934
|
try {
|
|
3400
|
-
const file =
|
|
3401
|
-
return JSON.parse(
|
|
3935
|
+
const file = path19.join(root, ".vibgrate", "scan_result.json");
|
|
3936
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
3402
3937
|
} catch {
|
|
3403
3938
|
return null;
|
|
3404
3939
|
}
|
|
@@ -3788,7 +4323,7 @@ var CACHE_VERSION = "vg-hosted-docs/1";
|
|
|
3788
4323
|
var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3789
4324
|
var MAX_ENTRIES = 64;
|
|
3790
4325
|
function cachePath(root) {
|
|
3791
|
-
return
|
|
4326
|
+
return path19.join(cacheDir(root), "hosted-docs.json");
|
|
3792
4327
|
}
|
|
3793
4328
|
function hostedDocsCacheKey(req, opts = {}) {
|
|
3794
4329
|
const material = JSON.stringify([
|
|
@@ -3804,7 +4339,7 @@ function hostedDocsCacheKey(req, opts = {}) {
|
|
|
3804
4339
|
}
|
|
3805
4340
|
function loadFile(root) {
|
|
3806
4341
|
try {
|
|
3807
|
-
const parsed = JSON.parse(
|
|
4342
|
+
const parsed = JSON.parse(fs18.readFileSync(cachePath(root), "utf8"));
|
|
3808
4343
|
if (parsed && parsed.version === CACHE_VERSION && parsed.entries && typeof parsed.entries === "object") return parsed;
|
|
3809
4344
|
} catch {
|
|
3810
4345
|
}
|
|
@@ -3818,8 +4353,8 @@ function saveFile(root, file) {
|
|
|
3818
4353
|
delete file.entries[k];
|
|
3819
4354
|
}
|
|
3820
4355
|
}
|
|
3821
|
-
|
|
3822
|
-
|
|
4356
|
+
fs18.mkdirSync(cacheDir(root), { recursive: true });
|
|
4357
|
+
fs18.writeFileSync(cachePath(root), JSON.stringify(file));
|
|
3823
4358
|
} catch {
|
|
3824
4359
|
}
|
|
3825
4360
|
}
|
|
@@ -4107,11 +4642,11 @@ var TOOLS = [
|
|
|
4107
4642
|
area: node.area
|
|
4108
4643
|
};
|
|
4109
4644
|
if (ctx?.dedup && ctx.seen?.has(node.id)) {
|
|
4110
|
-
return { ...base, repeat: true, callsTotal:
|
|
4645
|
+
return { ...base, repeat: true, callsTotal: uniqueNames2(calleeRecs.map((x) => x.node.qualifiedName)).length, calledByTotal: uniqueNames2(callerRecs.map((x) => x.node.qualifiedName)).length };
|
|
4111
4646
|
}
|
|
4112
4647
|
const edgeCap = isDetailed(args) ? NODE_EDGE_CAP : CONCISE_MATCHES;
|
|
4113
|
-
const calls = boundList(
|
|
4114
|
-
const calledBy = boundList(
|
|
4648
|
+
const calls = boundList(uniqueNames2(calleeRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
4649
|
+
const calledBy = boundList(uniqueNames2(callerRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
4115
4650
|
ctx?.seen?.add(node.id);
|
|
4116
4651
|
return {
|
|
4117
4652
|
...base,
|
|
@@ -4140,7 +4675,7 @@ var TOOLS = [
|
|
|
4140
4675
|
if (!ra.node) return { endpoint: "a", ...unresolved(ra.candidates) };
|
|
4141
4676
|
if (!rb.node) return { endpoint: "b", ...unresolved(rb.candidates) };
|
|
4142
4677
|
const result = shortestPath(graph, ra.node.id, rb.node.id);
|
|
4143
|
-
if (!result) return
|
|
4678
|
+
if (!result) return pathDisconnect(graph, ra.node.id, rb.node.id);
|
|
4144
4679
|
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
4145
4680
|
return { connected: true, direction: result.direction, path: result.ids.map((id) => byId.get(id)?.qualifiedName ?? id) };
|
|
4146
4681
|
}
|
|
@@ -4233,18 +4768,47 @@ var TOOLS = [
|
|
|
4233
4768
|
},
|
|
4234
4769
|
{
|
|
4235
4770
|
name: "list_hubs",
|
|
4236
|
-
description: "Most-depended-on symbols.",
|
|
4771
|
+
description: "Most-depended-on symbols (includes precomputed blast-radius counts when available).",
|
|
4237
4772
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
4238
|
-
handler: (graph, args) =>
|
|
4773
|
+
handler: (graph, args) => {
|
|
4774
|
+
const limit = numOr(args.limit, 20);
|
|
4775
|
+
if (graph.summaries?.hubs?.length) {
|
|
4776
|
+
return graph.summaries.hubs.slice(0, limit).map((h) => ({
|
|
4777
|
+
name: h.name,
|
|
4778
|
+
kind: h.kind,
|
|
4779
|
+
file: h.file,
|
|
4780
|
+
importance: h.importance,
|
|
4781
|
+
isHub: true,
|
|
4782
|
+
directDependents: h.direct,
|
|
4783
|
+
depth2Dependents: h.depth2,
|
|
4784
|
+
filesAffected: h.files,
|
|
4785
|
+
tested: h.tested
|
|
4786
|
+
}));
|
|
4787
|
+
}
|
|
4788
|
+
return graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance).slice(0, limit).map((n) => ({
|
|
4789
|
+
name: n.qualifiedName,
|
|
4790
|
+
kind: n.kind,
|
|
4791
|
+
file: n.file,
|
|
4792
|
+
line: n.span.start,
|
|
4793
|
+
importance: n.importance,
|
|
4794
|
+
isHub: n.isHub
|
|
4795
|
+
}));
|
|
4796
|
+
}
|
|
4239
4797
|
},
|
|
4240
4798
|
{
|
|
4241
4799
|
name: "get_facts",
|
|
4242
|
-
description: "Deterministic facts for a node (contract/invariant
|
|
4800
|
+
description: "Deterministic facts for a node (contract / invariant / characterization).",
|
|
4243
4801
|
inputSchema: obj({ name: { type: "string" } }, ["name"]),
|
|
4244
4802
|
handler: (graph, args) => {
|
|
4245
4803
|
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
4246
4804
|
if (!node) return unresolved(candidates);
|
|
4247
|
-
if (!graph.facts)
|
|
4805
|
+
if (!graph.facts) {
|
|
4806
|
+
return {
|
|
4807
|
+
node: node.qualifiedName,
|
|
4808
|
+
facts: [],
|
|
4809
|
+
note: "map has no facts \u2014 rebuild with a current vg (facts ship on every build)"
|
|
4810
|
+
};
|
|
4811
|
+
}
|
|
4248
4812
|
return { node: node.qualifiedName, facts: graph.facts.filter((f) => f.subjectIds.includes(node.id)) };
|
|
4249
4813
|
}
|
|
4250
4814
|
},
|
|
@@ -4536,14 +5100,22 @@ function conciseSummary(graph) {
|
|
|
4536
5100
|
return { counts: graph.meta.counts, languages: graph.meta.languages };
|
|
4537
5101
|
}
|
|
4538
5102
|
function summarize(graph, top = 10) {
|
|
5103
|
+
const topHubs = graph.summaries?.hubs?.length ? graph.summaries.hubs.slice(0, top).map((h) => ({
|
|
5104
|
+
name: h.name,
|
|
5105
|
+
file: h.file,
|
|
5106
|
+
importance: h.importance,
|
|
5107
|
+
directDependents: h.direct,
|
|
5108
|
+
depth2Dependents: h.depth2
|
|
5109
|
+
})) : 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 }));
|
|
4539
5110
|
return {
|
|
4540
5111
|
counts: graph.meta.counts,
|
|
4541
5112
|
languages: graph.meta.languages,
|
|
4542
5113
|
cluster: graph.meta.cluster,
|
|
5114
|
+
analysisTier: graph.meta.analysisTier ?? "full",
|
|
4543
5115
|
resolver: graph.provenance.resolver,
|
|
4544
5116
|
generatedAt: graph.generatedAt,
|
|
4545
5117
|
topAreas: [...graph.areas].sort((a, b) => b.size - a.size).slice(0, top).map((a) => ({ id: a.id, label: a.label, size: a.size })),
|
|
4546
|
-
topHubs
|
|
5118
|
+
topHubs
|
|
4547
5119
|
};
|
|
4548
5120
|
}
|
|
4549
5121
|
function stripId(item) {
|
|
@@ -4571,7 +5143,7 @@ function numOrU(v) {
|
|
|
4571
5143
|
const n = Number(v);
|
|
4572
5144
|
return Number.isFinite(n) ? n : void 0;
|
|
4573
5145
|
}
|
|
4574
|
-
function
|
|
5146
|
+
function uniqueNames2(xs) {
|
|
4575
5147
|
return [...new Set(xs)];
|
|
4576
5148
|
}
|
|
4577
5149
|
var HOT_TOOLS = ["orient", "search_symbols", "query_graph", "get_node"];
|
|
@@ -4794,12 +5366,12 @@ function detectAssistants(root, opts = {}) {
|
|
|
4794
5366
|
const which = opts.which ?? whichOnPath;
|
|
4795
5367
|
const found = [];
|
|
4796
5368
|
for (const assistant of ASSISTANTS) {
|
|
4797
|
-
const repoHit = (assistant.markers ?? []).find((m) =>
|
|
5369
|
+
const repoHit = (assistant.markers ?? []).find((m) => fs18.existsSync(path19.join(root, m)));
|
|
4798
5370
|
if (repoHit) {
|
|
4799
5371
|
found.push({ assistant, via: "repo", marker: repoHit });
|
|
4800
5372
|
continue;
|
|
4801
5373
|
}
|
|
4802
|
-
const homeHit = (assistant.homeMarkers ?? []).find((m) =>
|
|
5374
|
+
const homeHit = (assistant.homeMarkers ?? []).find((m) => fs18.existsSync(path19.join(home, m)));
|
|
4803
5375
|
if (homeHit) {
|
|
4804
5376
|
found.push({ assistant, via: "home", marker: homeHit });
|
|
4805
5377
|
continue;
|
|
@@ -4831,19 +5403,19 @@ function installAssistant(a, opts) {
|
|
|
4831
5403
|
const skipped = [];
|
|
4832
5404
|
let note;
|
|
4833
5405
|
if (a.skill) {
|
|
4834
|
-
writeFileEnsured(
|
|
5406
|
+
writeFileEnsured(path19.join(opts.root, a.skill), skillMarkdown(a.id));
|
|
4835
5407
|
wrote.push(a.skill);
|
|
4836
5408
|
}
|
|
4837
5409
|
if (a.mcp) {
|
|
4838
5410
|
const launch = opts.launch ?? detectServeLaunch();
|
|
4839
|
-
upsertMcp(
|
|
5411
|
+
upsertMcp(path19.join(opts.root, a.mcp.file), a.mcp, launch);
|
|
4840
5412
|
wrote.push(a.mcp.file);
|
|
4841
5413
|
note = launch.note;
|
|
4842
5414
|
} else {
|
|
4843
5415
|
skipped.push("mcp (host-specific setup)");
|
|
4844
5416
|
}
|
|
4845
5417
|
if (a.nudge && opts.hook !== false) {
|
|
4846
|
-
writeNudge(
|
|
5418
|
+
writeNudge(path19.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
|
|
4847
5419
|
wrote.push(a.nudge.file);
|
|
4848
5420
|
} else if (a.nudge) {
|
|
4849
5421
|
skipped.push("nudge (--no-hook)");
|
|
@@ -4853,41 +5425,41 @@ function installAssistant(a, opts) {
|
|
|
4853
5425
|
function uninstallAssistant(a, root, purge) {
|
|
4854
5426
|
const removed = [];
|
|
4855
5427
|
if (a.mcp) {
|
|
4856
|
-
const file =
|
|
5428
|
+
const file = path19.join(root, a.mcp.file);
|
|
4857
5429
|
if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
|
|
4858
5430
|
}
|
|
4859
5431
|
if (a.nudge) {
|
|
4860
|
-
const file =
|
|
5432
|
+
const file = path19.join(root, a.nudge.file);
|
|
4861
5433
|
if (a.nudge.kind === "block") {
|
|
4862
5434
|
if (removeBlock(file)) removed.push(a.nudge.file);
|
|
4863
|
-
} else if (
|
|
4864
|
-
|
|
5435
|
+
} else if (fs18.existsSync(file)) {
|
|
5436
|
+
fs18.rmSync(file);
|
|
4865
5437
|
removed.push(a.nudge.file);
|
|
4866
5438
|
}
|
|
4867
5439
|
}
|
|
4868
5440
|
if (purge && a.skill) {
|
|
4869
|
-
const file =
|
|
4870
|
-
if (
|
|
4871
|
-
|
|
5441
|
+
const file = path19.join(root, a.skill);
|
|
5442
|
+
if (fs18.existsSync(file)) {
|
|
5443
|
+
fs18.rmSync(file);
|
|
4872
5444
|
removed.push(a.skill);
|
|
4873
5445
|
}
|
|
4874
5446
|
}
|
|
4875
5447
|
return removed;
|
|
4876
5448
|
}
|
|
4877
5449
|
function writeFileEnsured(file, content) {
|
|
4878
|
-
|
|
4879
|
-
|
|
5450
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
5451
|
+
fs18.writeFileSync(file, content);
|
|
4880
5452
|
}
|
|
4881
5453
|
function writeNavigationConfig(root) {
|
|
4882
5454
|
ensureVibgrateGitignore(root);
|
|
4883
|
-
const rel2 =
|
|
5455
|
+
const rel2 = path19.join(".vibgrate", "mcp-navigation.json");
|
|
4884
5456
|
const doc = {
|
|
4885
5457
|
_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.",
|
|
4886
5458
|
hot_core: [...HOT_TOOLS],
|
|
4887
5459
|
deferred: deferredToolNames(),
|
|
4888
5460
|
toolset: navigationToolsetConfig()
|
|
4889
5461
|
};
|
|
4890
|
-
writeFileEnsured(
|
|
5462
|
+
writeFileEnsured(path19.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
4891
5463
|
`);
|
|
4892
5464
|
return rel2;
|
|
4893
5465
|
}
|
|
@@ -4902,7 +5474,7 @@ function upsertMcp(file, target, launch) {
|
|
|
4902
5474
|
`);
|
|
4903
5475
|
}
|
|
4904
5476
|
function removeMcp(file, target) {
|
|
4905
|
-
if (!
|
|
5477
|
+
if (!fs18.existsSync(file)) return false;
|
|
4906
5478
|
const config = readJson(file);
|
|
4907
5479
|
const bag = config[target.key];
|
|
4908
5480
|
if (!bag || !(bag.vg !== void 0)) return false;
|
|
@@ -4921,8 +5493,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
4921
5493
|
};
|
|
4922
5494
|
for (const a of ASSISTANTS) {
|
|
4923
5495
|
if (a.skill) {
|
|
4924
|
-
const file =
|
|
4925
|
-
if (
|
|
5496
|
+
const file = path19.join(root, a.skill);
|
|
5497
|
+
if (fs18.existsSync(file)) {
|
|
4926
5498
|
const text = readTextSafe(file);
|
|
4927
5499
|
const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
|
|
4928
5500
|
if (from !== null) {
|
|
@@ -4932,8 +5504,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
4932
5504
|
}
|
|
4933
5505
|
}
|
|
4934
5506
|
if (a.nudge) {
|
|
4935
|
-
const file =
|
|
4936
|
-
if (!
|
|
5507
|
+
const file = path19.join(root, a.nudge.file);
|
|
5508
|
+
if (!fs18.existsSync(file)) continue;
|
|
4937
5509
|
const text = readTextSafe(file);
|
|
4938
5510
|
if (text === null) continue;
|
|
4939
5511
|
if (a.nudge.kind === "block") {
|
|
@@ -4957,7 +5529,7 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
4957
5529
|
}
|
|
4958
5530
|
function readTextSafe(file) {
|
|
4959
5531
|
try {
|
|
4960
|
-
return
|
|
5532
|
+
return fs18.readFileSync(file, "utf8");
|
|
4961
5533
|
} catch {
|
|
4962
5534
|
return null;
|
|
4963
5535
|
}
|
|
@@ -4976,8 +5548,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
|
|
|
4976
5548
|
upsertBlock(file, nudgeMarkdown(smallRepo, client));
|
|
4977
5549
|
}
|
|
4978
5550
|
function upsertBlock(file, block) {
|
|
4979
|
-
|
|
4980
|
-
let existing =
|
|
5551
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
5552
|
+
let existing = fs18.existsSync(file) ? fs18.readFileSync(file, "utf8") : "";
|
|
4981
5553
|
const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
|
|
4982
5554
|
if (re.test(existing)) existing = existing.replace(re, block);
|
|
4983
5555
|
else existing = existing.length ? `${existing.replace(/\s*$/, "")}
|
|
@@ -4985,20 +5557,20 @@ function upsertBlock(file, block) {
|
|
|
4985
5557
|
${block}
|
|
4986
5558
|
` : `${block}
|
|
4987
5559
|
`;
|
|
4988
|
-
|
|
5560
|
+
fs18.writeFileSync(file, existing);
|
|
4989
5561
|
}
|
|
4990
5562
|
function removeBlock(file) {
|
|
4991
|
-
if (!
|
|
4992
|
-
const existing =
|
|
5563
|
+
if (!fs18.existsSync(file)) return false;
|
|
5564
|
+
const existing = fs18.readFileSync(file, "utf8");
|
|
4993
5565
|
const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
|
|
4994
5566
|
if (!re.test(existing)) return false;
|
|
4995
|
-
|
|
5567
|
+
fs18.writeFileSync(file, existing.replace(re, "\n"));
|
|
4996
5568
|
return true;
|
|
4997
5569
|
}
|
|
4998
5570
|
function readJson(file) {
|
|
4999
|
-
if (!
|
|
5571
|
+
if (!fs18.existsSync(file)) return {};
|
|
5000
5572
|
try {
|
|
5001
|
-
const parsed = JSON.parse(
|
|
5573
|
+
const parsed = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
5002
5574
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5003
5575
|
return parsed;
|
|
5004
5576
|
}
|
|
@@ -5018,7 +5590,7 @@ function escapeRe(s) {
|
|
|
5018
5590
|
}
|
|
5019
5591
|
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
5020
5592
|
function refreshLockPath(root) {
|
|
5021
|
-
return
|
|
5593
|
+
return path19.join(cacheDir(root), "refresh.lock");
|
|
5022
5594
|
}
|
|
5023
5595
|
async function refreshIfStale(root, opts = {}) {
|
|
5024
5596
|
const probe = probeFreshness(root);
|
|
@@ -5050,8 +5622,8 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5050
5622
|
writeArtifacts(result.graph, {
|
|
5051
5623
|
root,
|
|
5052
5624
|
graphPath: opts.graphPath,
|
|
5053
|
-
report:
|
|
5054
|
-
html:
|
|
5625
|
+
report: fs18.existsSync(path19.join(dir, "GRAPH_REPORT.md")),
|
|
5626
|
+
html: fs18.existsSync(path19.join(dir, "graph.html"))
|
|
5055
5627
|
});
|
|
5056
5628
|
}
|
|
5057
5629
|
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
@@ -5071,6 +5643,6 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5071
5643
|
}
|
|
5072
5644
|
}
|
|
5073
5645
|
|
|
5074
|
-
export { ASSISTANTS, CliError, ExitCode, FREE_PACK, NPX_INVOCATION, ResourceLimitError,
|
|
5075
|
-
//# sourceMappingURL=chunk-
|
|
5076
|
-
//# sourceMappingURL=chunk-
|
|
5646
|
+
export { ASSISTANTS, CliError, ExitCode, FREE_PACK, NPX_INVOCATION, ResourceLimitError, SKIP_DIRS, SKIP_FILES, SMALL_REPO_FILES, TOOLS, UsageError, analyze, applyCoverage, assessDocQuality, assistantById, availableRegionIds, buildFacts, buildGraph, clearStoredCredentials, countTokens, coveringTests, createWorkspaceDsn, credentialsPath, dashHostForIngestHost, decodeScipIndex, detectAssistants, detectRunner, detectServeLaunch, discover, driftCount, dsnCommand, ensureGitignored, epistemicBreakdown, fetchHostedDocsCached, findGitRoot, gitignoreEntryForCredentials, groundGraph, hasDrift, homeCredentialsPath, hostedBase, ingestHostForRegionId, installAssistant, loadCoverage, loadSnapshot, parseDsn, pathDisconnect, probeFreshness, projectCredentialsPath, publishPrivateLibrary, pushCommand, readScanArtifact, readStoredCredentials, refreshIfStale, refreshInstalledInstructions, renderToolResult, resolveCliInvocation, resolveDsn, resolveIngestHost, resolveLimits, scipEdges, searchSymbols, selectForBudget, shortestPath, symbolsFromApi, testsToRun, uninstallAssistant, uploadScanArtifact, usageError, verifyDeterminism, warmEmbedderInBackground, writeNavigationConfig, writeSnapshot, writeStoredCredentials };
|
|
5647
|
+
//# sourceMappingURL=chunk-MAY5HLDH.js.map
|
|
5648
|
+
//# sourceMappingURL=chunk-MAY5HLDH.js.map
|