@vibgrate/cli 2026.727.2 → 2026.727.4
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 -1
- package/README.md +2 -1
- package/dist/baseline-ENCOSJYQ.js +8 -0
- package/dist/{baseline-TG4ATZGZ.js.map → baseline-ENCOSJYQ.js.map} +1 -1
- package/dist/chunk-3SDV6ADQ.js +78 -0
- package/dist/chunk-3SDV6ADQ.js.map +1 -0
- package/dist/{chunk-LKVMI67M.js → chunk-6AXHLQBV.js} +220 -23
- package/dist/chunk-6AXHLQBV.js.map +1 -0
- package/dist/{chunk-6ABXHZRN.js → chunk-6UK6QPTR.js} +69 -70
- package/dist/chunk-6UK6QPTR.js.map +1 -0
- package/dist/{chunk-TCDAZZNP.js → chunk-77LYGXBI.js} +5 -5
- package/dist/{chunk-TCDAZZNP.js.map → chunk-77LYGXBI.js.map} +1 -1
- package/dist/{chunk-2XQDMLKB.js → chunk-B3YUO3NW.js} +7 -3
- package/dist/chunk-B3YUO3NW.js.map +1 -0
- package/dist/{chunk-RNP3SMTV.js → chunk-FPEOIS7K.js} +61 -26
- package/dist/chunk-FPEOIS7K.js.map +1 -0
- package/dist/{chunk-OOIG3RUM.js → chunk-GYSOQH5C.js} +18 -79
- package/dist/chunk-GYSOQH5C.js.map +1 -0
- package/dist/{chunk-6AYDFFP5.js → chunk-HK3AWUXH.js} +3 -3
- package/dist/{chunk-6AYDFFP5.js.map → chunk-HK3AWUXH.js.map} +1 -1
- package/dist/{chunk-EX4PQT6W.js → chunk-HYRAILGM.js} +4 -4
- package/dist/{chunk-EX4PQT6W.js.map → chunk-HYRAILGM.js.map} +1 -1
- package/dist/{chunk-NDMDSPFX.js → chunk-KKRSQQ3A.js} +4 -3
- package/dist/chunk-KKRSQQ3A.js.map +1 -0
- package/dist/chunk-KUIBP3LU.js +101 -0
- package/dist/chunk-KUIBP3LU.js.map +1 -0
- package/dist/{chunk-OFUC6RYN.js → chunk-KXSKFQMI.js} +6 -99
- package/dist/chunk-KXSKFQMI.js.map +1 -0
- package/dist/{chunk-KCKYKDMS.js → chunk-LJP2EPKM.js} +20 -17
- package/dist/chunk-LJP2EPKM.js.map +1 -0
- package/dist/{chunk-J4RFRKZH.js → chunk-MAUJTPQW.js} +8 -8
- package/dist/{chunk-J4RFRKZH.js.map → chunk-MAUJTPQW.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-3V24O47U.js → chunk-PVZBYZZR.js} +19 -9
- package/dist/chunk-PVZBYZZR.js.map +1 -0
- package/dist/{chunk-CY4WKXB2.js → chunk-R6ZRVVZT.js} +4 -4
- package/dist/{chunk-CY4WKXB2.js.map → chunk-R6ZRVVZT.js.map} +1 -1
- package/dist/{chunk-WGUL45AV.js → chunk-T3AYRS7J.js} +14 -7
- package/dist/chunk-T3AYRS7J.js.map +1 -0
- package/dist/{chunk-2MBL43EJ.js → chunk-XPJZTSY7.js} +3 -3
- package/dist/{chunk-2MBL43EJ.js.map → chunk-XPJZTSY7.js.map} +1 -1
- package/dist/chunk-YCJIIGLP.js +356 -0
- package/dist/chunk-YCJIIGLP.js.map +1 -0
- package/dist/{chunk-KNELIQJO.js → chunk-YKVH2PGF.js} +640 -208
- package/dist/chunk-YKVH2PGF.js.map +1 -0
- package/dist/{chunk-KSBHGWRV.js → chunk-ZBTY6VV4.js} +4 -4
- package/dist/{chunk-KSBHGWRV.js.map → chunk-ZBTY6VV4.js.map} +1 -1
- package/dist/{chunk-K3MZIVZW.js → chunk-ZKNB4YH4.js} +19 -5
- package/dist/chunk-ZKNB4YH4.js.map +1 -0
- package/dist/cli.js +476 -393
- package/dist/cli.js.map +1 -1
- package/dist/{graph-backend-73URGTFY.js → graph-backend-UTOL3SHE.js} +5 -5
- package/dist/{graph-backend-73URGTFY.js.map → graph-backend-UTOL3SHE.js.map} +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/dist/{interactive-372NLI5C.js → interactive-SMHVURYK.js} +54 -24
- package/dist/interactive-SMHVURYK.js.map +1 -0
- package/dist/load-QPOWQD7C.js +8 -0
- package/dist/{load-I333TGNI.js.map → load-QPOWQD7C.js.map} +1 -1
- package/dist/model-manager-PN7HEDXD.js +9 -0
- package/dist/{model-orchestrator-TVONAUPS.js.map → model-manager-PN7HEDXD.js.map} +1 -1
- package/dist/{model-orchestrator-TVONAUPS.js → model-orchestrator-4VSYP4J4.js} +3 -3
- package/dist/model-orchestrator-4VSYP4J4.js.map +1 -0
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +1 -1
- package/dist/resolve-gguf-Y6FH3CNB.js +9 -0
- package/dist/{resolve-gguf-VPL4YQ6J.js.map → resolve-gguf-Y6FH3CNB.js.map} +1 -1
- package/dist/{runtime-session-JRKCQUHY.js → runtime-session-4AMZIAAG.js} +7 -7
- package/dist/{runtime-session-JRKCQUHY.js.map → runtime-session-4AMZIAAG.js.map} +1 -1
- package/dist/session-W3646AOV.js +12 -0
- package/dist/{session-ITPYQ5XI.js.map → session-W3646AOV.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-SLMDKLXP.js → stream-json-X7UQFB4T.js} +11 -10
- package/dist/stream-json-X7UQFB4T.js.map +1 -0
- package/dist/{types-7AHBUbFm.d.ts → types-DLH-Ritr.d.ts} +18 -1
- package/dist/{vgd-7JV5KCNJ.js → vgd-CVLKBS7K.js} +6 -6
- package/dist/vgd-CVLKBS7K.js.map +1 -0
- package/package.json +1 -1
- package/dist/baseline-TG4ATZGZ.js +0 -8
- package/dist/chunk-2XQDMLKB.js.map +0 -1
- package/dist/chunk-3Q7QBWFW.js.map +0 -1
- package/dist/chunk-3V24O47U.js.map +0 -1
- package/dist/chunk-6ABXHZRN.js.map +0 -1
- package/dist/chunk-K3MZIVZW.js.map +0 -1
- package/dist/chunk-KCKYKDMS.js.map +0 -1
- package/dist/chunk-KNELIQJO.js.map +0 -1
- package/dist/chunk-LKVMI67M.js.map +0 -1
- package/dist/chunk-NDMDSPFX.js.map +0 -1
- package/dist/chunk-OFUC6RYN.js.map +0 -1
- package/dist/chunk-OOIG3RUM.js.map +0 -1
- package/dist/chunk-RNP3SMTV.js.map +0 -1
- package/dist/chunk-WGUL45AV.js.map +0 -1
- package/dist/interactive-372NLI5C.js.map +0 -1
- package/dist/load-I333TGNI.js +0 -8
- package/dist/resolve-gguf-VPL4YQ6J.js +0 -8
- package/dist/session-ITPYQ5XI.js +0 -11
- package/dist/stream-json-SLMDKLXP.js.map +0 -1
- package/dist/vgd-7JV5KCNJ.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-6AXHLQBV.js';
|
|
2
|
+
import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem, VERSION } from './chunk-T3AYRS7J.js';
|
|
3
|
+
import { writeTextFile, pathExists, readJsonFile } from './chunk-CRAMOEBE.js';
|
|
3
4
|
import { resolveOne, findNodes } from './chunk-J7LKEB2R.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-
|
|
5
|
+
import { impactOf, indexFor, withTimeout, getNodeEmbeddings, queryGraphSemantic, queryGraph, loadEmbedder, isModelReady, acquireLock, releaseLock } from './chunk-6UK6QPTR.js';
|
|
6
|
+
import { discoverModels } from './chunk-3SDV6ADQ.js';
|
|
7
|
+
import { langById, grammarSetVersion, langForExtension, setGrammarsOverride, parseSource } from './chunk-ON25BX6T.js';
|
|
7
8
|
import { redactSecrets } from './chunk-RXP66R2E.js';
|
|
8
|
-
import { writeGraphIndex, SCHEMA_VERSION, loadGraph, ensureVibgrateGitignore, vibgrateDir, writeArtifacts } from './chunk-
|
|
9
|
-
import { cacheDir, loadCache, 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 fs16 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
|
}
|
|
@@ -769,14 +770,14 @@ var DEFAULT_PATHS = [
|
|
|
769
770
|
];
|
|
770
771
|
function loadCoverage(root, explicit) {
|
|
771
772
|
const candidates = (explicit && explicit.length ? explicit : DEFAULT_PATHS).map(
|
|
772
|
-
(p) =>
|
|
773
|
+
(p) => path19.resolve(root, p)
|
|
773
774
|
);
|
|
774
|
-
const found = candidates.filter((p) =>
|
|
775
|
+
const found = candidates.filter((p) => fs18.existsSync(p));
|
|
775
776
|
if (found.length === 0) return null;
|
|
776
777
|
const map = /* @__PURE__ */ new Map();
|
|
777
778
|
for (const file of found) {
|
|
778
779
|
try {
|
|
779
|
-
const text =
|
|
780
|
+
const text = fs18.readFileSync(file, "utf8");
|
|
780
781
|
if (file.endsWith(".json")) mergeIstanbul(map, text, root);
|
|
781
782
|
else mergeLcov(map, text, root);
|
|
782
783
|
} catch {
|
|
@@ -785,8 +786,8 @@ function loadCoverage(root, explicit) {
|
|
|
785
786
|
return map.size ? map : null;
|
|
786
787
|
}
|
|
787
788
|
function rel(root, p) {
|
|
788
|
-
const abs =
|
|
789
|
-
return
|
|
789
|
+
const abs = path19.isAbsolute(p) ? p : path19.resolve(root, p);
|
|
790
|
+
return path19.relative(root, abs).split(path19.sep).join("/");
|
|
790
791
|
}
|
|
791
792
|
function bump(map, file, line, hits) {
|
|
792
793
|
let lh = map.get(file);
|
|
@@ -1093,7 +1094,7 @@ async function parseInline(files, options) {
|
|
|
1093
1094
|
onProgress?.(0, files.length);
|
|
1094
1095
|
for (const file of files) {
|
|
1095
1096
|
try {
|
|
1096
|
-
const source =
|
|
1097
|
+
const source = fs18.readFileSync(file.abs, "utf8");
|
|
1097
1098
|
out.push(await parseSource(file.rel, file.lang.id, source));
|
|
1098
1099
|
} catch (err) {
|
|
1099
1100
|
out.push(emptyParse(file, `parse failed: ${err.message}`));
|
|
@@ -1148,9 +1149,9 @@ function isWorkerOom(err) {
|
|
|
1148
1149
|
return e?.code === "ERR_WORKER_OUT_OF_MEMORY" || /out of memory/i.test(e?.message ?? "");
|
|
1149
1150
|
}
|
|
1150
1151
|
function resolveWorkerFile() {
|
|
1151
|
-
const here =
|
|
1152
|
-
const candidate =
|
|
1153
|
-
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;
|
|
1154
1155
|
}
|
|
1155
1156
|
function chunk(items, buckets) {
|
|
1156
1157
|
const out = Array.from({ length: Math.min(buckets, items.length || 1) }, () => []);
|
|
@@ -1399,7 +1400,7 @@ function enclosing3(nodes, line) {
|
|
|
1399
1400
|
return best;
|
|
1400
1401
|
}
|
|
1401
1402
|
function normalize2(p) {
|
|
1402
|
-
return
|
|
1403
|
+
return path19.resolve(p).split(path19.sep).join("/");
|
|
1403
1404
|
}
|
|
1404
1405
|
|
|
1405
1406
|
// src/engine/epistemic.ts
|
|
@@ -1642,14 +1643,14 @@ var IMPORTANCE = {
|
|
|
1642
1643
|
"other-config": 0.08
|
|
1643
1644
|
};
|
|
1644
1645
|
function toPosix2(p) {
|
|
1645
|
-
return p.split(
|
|
1646
|
+
return p.split(path19.sep).join("/");
|
|
1646
1647
|
}
|
|
1647
1648
|
function buildRootIgnore2(root, exclude) {
|
|
1648
1649
|
const ig = ignore();
|
|
1649
|
-
const gitignorePath =
|
|
1650
|
-
if (
|
|
1650
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
1651
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
1651
1652
|
try {
|
|
1652
|
-
ig.add(
|
|
1653
|
+
ig.add(fs18.readFileSync(gitignorePath, "utf8"));
|
|
1653
1654
|
} catch {
|
|
1654
1655
|
}
|
|
1655
1656
|
}
|
|
@@ -1658,7 +1659,7 @@ function buildRootIgnore2(root, exclude) {
|
|
|
1658
1659
|
}
|
|
1659
1660
|
function classifyProjectContext(relOrName) {
|
|
1660
1661
|
const rel2 = relOrName.replace(/\\/g, "/");
|
|
1661
|
-
const base =
|
|
1662
|
+
const base = path19.basename(rel2);
|
|
1662
1663
|
const baseLower = base.toLowerCase();
|
|
1663
1664
|
if (/^\.env($|\.)/i.test(base) && !isEnvExampleName(baseLower)) return null;
|
|
1664
1665
|
if (SKIP_FILES.has(baseLower)) return null;
|
|
@@ -1671,7 +1672,7 @@ function classifyProjectContext(relOrName) {
|
|
|
1671
1672
|
for (const { re, category } of PATH_PATTERNS) {
|
|
1672
1673
|
if (re.test(rel2)) return category;
|
|
1673
1674
|
}
|
|
1674
|
-
const ext =
|
|
1675
|
+
const ext = path19.extname(baseLower);
|
|
1675
1676
|
if (DOC_EXTENSIONS.has(ext)) return ext === ".txt" ? "text" : "markdown";
|
|
1676
1677
|
if (API_EXTENSIONS.has(ext)) return "api-contract";
|
|
1677
1678
|
if ((ext === ".yaml" || ext === ".yml" || ext === ".json") && /openapi|swagger|asyncapi/i.test(base)) {
|
|
@@ -1689,24 +1690,24 @@ function isEnvExampleName(baseLower) {
|
|
|
1689
1690
|
return false;
|
|
1690
1691
|
}
|
|
1691
1692
|
function discoverDocs(options) {
|
|
1692
|
-
const root =
|
|
1693
|
+
const root = path19.resolve(options.root);
|
|
1693
1694
|
const maxFiles = options.maxFiles ?? DOC_MAX_FILES;
|
|
1694
1695
|
const rootIg = buildRootIgnore2(root, options.exclude ?? []);
|
|
1695
|
-
const scopeAbs = (options.paths?.length ? options.paths.map((p) =>
|
|
1696
|
-
(p) =>
|
|
1696
|
+
const scopeAbs = (options.paths?.length ? options.paths.map((p) => path19.resolve(root, p)) : [root]).filter(
|
|
1697
|
+
(p) => fs18.existsSync(p)
|
|
1697
1698
|
);
|
|
1698
1699
|
const found = /* @__PURE__ */ new Map();
|
|
1699
1700
|
const consider = (abs) => {
|
|
1700
1701
|
if (found.size >= maxFiles) return;
|
|
1701
|
-
const rel2 = toPosix2(
|
|
1702
|
+
const rel2 = toPosix2(path19.relative(root, abs));
|
|
1702
1703
|
if (!rel2 || rel2.startsWith("..")) return;
|
|
1703
1704
|
if (rootIg.ignores(rel2)) return;
|
|
1704
|
-
const base =
|
|
1705
|
+
const base = path19.basename(abs);
|
|
1705
1706
|
if (SKIP_FILES.has(base.toLowerCase())) return;
|
|
1706
1707
|
const category = classifyProjectContext(rel2);
|
|
1707
1708
|
if (!category) return;
|
|
1708
1709
|
try {
|
|
1709
|
-
const st =
|
|
1710
|
+
const st = fs18.statSync(abs);
|
|
1710
1711
|
if (!st.isFile() || st.size > DOC_FILE_MAX_BYTES) return;
|
|
1711
1712
|
if (st.size === 0) return;
|
|
1712
1713
|
} catch {
|
|
@@ -1718,14 +1719,14 @@ function discoverDocs(options) {
|
|
|
1718
1719
|
if (found.size >= maxFiles || depth > 12) return;
|
|
1719
1720
|
let entries;
|
|
1720
1721
|
try {
|
|
1721
|
-
entries =
|
|
1722
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
1722
1723
|
} catch {
|
|
1723
1724
|
return;
|
|
1724
1725
|
}
|
|
1725
1726
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1726
1727
|
if (found.size >= maxFiles) return;
|
|
1727
|
-
const abs =
|
|
1728
|
-
const rel2 = toPosix2(
|
|
1728
|
+
const abs = path19.join(dir, entry.name);
|
|
1729
|
+
const rel2 = toPosix2(path19.relative(root, abs));
|
|
1729
1730
|
if (entry.isDirectory()) {
|
|
1730
1731
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1731
1732
|
if (rel2 && rootIg.ignores(`${rel2}/`) && !rel2.startsWith(".github")) continue;
|
|
@@ -1737,7 +1738,7 @@ function discoverDocs(options) {
|
|
|
1737
1738
|
};
|
|
1738
1739
|
for (const scope of scopeAbs) {
|
|
1739
1740
|
try {
|
|
1740
|
-
const st =
|
|
1741
|
+
const st = fs18.statSync(scope);
|
|
1741
1742
|
if (st.isDirectory()) walk(scope, 0);
|
|
1742
1743
|
else if (st.isFile()) consider(scope);
|
|
1743
1744
|
} catch {
|
|
@@ -1748,7 +1749,7 @@ function discoverDocs(options) {
|
|
|
1748
1749
|
function prepareDocBody(rel2, raw, category) {
|
|
1749
1750
|
const scrubbed = redactSecrets(raw);
|
|
1750
1751
|
if (scrubbed.length <= DOC_BODY_MAX_CHARS) return scrubbed;
|
|
1751
|
-
if (category === "manifest" &&
|
|
1752
|
+
if (category === "manifest" && path19.basename(rel2).toLowerCase() === "package.json") {
|
|
1752
1753
|
return summarizePackageJson(scrubbed);
|
|
1753
1754
|
}
|
|
1754
1755
|
if (category === "api-contract" && /\.(ya?ml|json)$/i.test(rel2)) {
|
|
@@ -1794,14 +1795,14 @@ function documentNodesFromDocs(docs) {
|
|
|
1794
1795
|
for (const d of docs) {
|
|
1795
1796
|
let raw = "";
|
|
1796
1797
|
try {
|
|
1797
|
-
raw =
|
|
1798
|
+
raw = fs18.readFileSync(d.abs, "utf8");
|
|
1798
1799
|
} catch {
|
|
1799
1800
|
continue;
|
|
1800
1801
|
}
|
|
1801
1802
|
const category = d.category ?? d.kind;
|
|
1802
1803
|
const scrubbed = prepareDocBody(d.rel, raw, category);
|
|
1803
1804
|
if (!scrubbed.trim()) continue;
|
|
1804
|
-
const name =
|
|
1805
|
+
const name = path19.basename(d.rel);
|
|
1805
1806
|
const qualifiedName = d.rel.replace(/\\/g, "/");
|
|
1806
1807
|
const id = nodeId({
|
|
1807
1808
|
kind: "document",
|
|
@@ -1835,7 +1836,7 @@ function shardTsProjects(root, files) {
|
|
|
1835
1836
|
const cache = /* @__PURE__ */ new Map();
|
|
1836
1837
|
const byKey = /* @__PURE__ */ new Map();
|
|
1837
1838
|
for (const f of files) {
|
|
1838
|
-
const dir =
|
|
1839
|
+
const dir = path19.dirname(f.abs);
|
|
1839
1840
|
const key = nearestProjectKey(root, dir, cache);
|
|
1840
1841
|
const list = byKey.get(key);
|
|
1841
1842
|
if (list) list.push(f);
|
|
@@ -1850,22 +1851,22 @@ function nearestProjectKey(root, startDir, cache) {
|
|
|
1850
1851
|
const hit = cache.get(startDir);
|
|
1851
1852
|
if (hit !== void 0) return hit;
|
|
1852
1853
|
let dir = startDir;
|
|
1853
|
-
const rootAbs =
|
|
1854
|
+
const rootAbs = path19.resolve(root);
|
|
1854
1855
|
while (true) {
|
|
1855
|
-
if (
|
|
1856
|
-
const key = toPosix3(
|
|
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)) || ".";
|
|
1857
1858
|
cache.set(startDir, key);
|
|
1858
1859
|
return key;
|
|
1859
1860
|
}
|
|
1860
|
-
if (dir === rootAbs ||
|
|
1861
|
+
if (dir === rootAbs || path19.dirname(dir) === dir) {
|
|
1861
1862
|
cache.set(startDir, ".");
|
|
1862
1863
|
return ".";
|
|
1863
1864
|
}
|
|
1864
|
-
dir =
|
|
1865
|
+
dir = path19.dirname(dir);
|
|
1865
1866
|
}
|
|
1866
1867
|
}
|
|
1867
1868
|
function toPosix3(p) {
|
|
1868
|
-
return p.split(
|
|
1869
|
+
return p.split(path19.sep).join("/");
|
|
1869
1870
|
}
|
|
1870
1871
|
|
|
1871
1872
|
// src/engine/timing.ts
|
|
@@ -1900,11 +1901,284 @@ function nowMs() {
|
|
|
1900
1901
|
return Number(process.hrtime.bigint() / 1000n) / 1e3;
|
|
1901
1902
|
}
|
|
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
|
+
}
|
|
2176
|
+
|
|
1903
2177
|
// src/engine/build.ts
|
|
1904
2178
|
async function buildGraph(options) {
|
|
1905
2179
|
const timer = new StageTimer();
|
|
1906
2180
|
timer.start("total");
|
|
1907
|
-
const root =
|
|
2181
|
+
const root = path19.resolve(options.root);
|
|
1908
2182
|
timer.start("discover");
|
|
1909
2183
|
const files = discover({
|
|
1910
2184
|
root,
|
|
@@ -1933,10 +2207,11 @@ async function buildGraph(options) {
|
|
|
1933
2207
|
const reused = [];
|
|
1934
2208
|
const buildWarnings = [];
|
|
1935
2209
|
let statHits = 0;
|
|
2210
|
+
const pendingHash = [];
|
|
1936
2211
|
for (const file of files) {
|
|
1937
2212
|
let stat;
|
|
1938
2213
|
try {
|
|
1939
|
-
stat =
|
|
2214
|
+
stat = fs18.statSync(file.abs);
|
|
1940
2215
|
} catch {
|
|
1941
2216
|
continue;
|
|
1942
2217
|
}
|
|
@@ -1966,20 +2241,29 @@ async function buildGraph(options) {
|
|
|
1966
2241
|
statHits++;
|
|
1967
2242
|
continue;
|
|
1968
2243
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
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
|
+
}
|
|
1983
2267
|
}
|
|
1984
2268
|
}
|
|
1985
2269
|
timer.end("hash");
|
|
@@ -2009,6 +2293,30 @@ async function buildGraph(options) {
|
|
|
2009
2293
|
const resolved = resolve(parses, moduleResolver);
|
|
2010
2294
|
timer.end("resolve");
|
|
2011
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
|
+
}
|
|
2012
2320
|
const nodeFileById = new Map(resolved.nodes.map((n) => [n.id, n.file]));
|
|
2013
2321
|
let edges = resolved.edges;
|
|
2014
2322
|
const resolvers = [...resolved.stats.resolvers];
|
|
@@ -2079,8 +2387,8 @@ async function buildGraph(options) {
|
|
|
2079
2387
|
});
|
|
2080
2388
|
for (const d of docs) {
|
|
2081
2389
|
try {
|
|
2082
|
-
const st =
|
|
2083
|
-
const hash = hashBytes(
|
|
2390
|
+
const st = fs18.statSync(d.abs);
|
|
2391
|
+
const hash = hashBytes(fs18.readFileSync(d.abs));
|
|
2084
2392
|
hashes.set(d.rel, hash);
|
|
2085
2393
|
fileStats.push({ rel: d.rel, size: st.size, mtimeMs: st.mtimeMs, hash });
|
|
2086
2394
|
} catch {
|
|
@@ -2121,7 +2429,7 @@ async function buildGraph(options) {
|
|
|
2121
2429
|
toolchain
|
|
2122
2430
|
},
|
|
2123
2431
|
meta: {
|
|
2124
|
-
root:
|
|
2432
|
+
root: path19.basename(root) === "" ? "." : ".",
|
|
2125
2433
|
languages,
|
|
2126
2434
|
analysisTier: analysis.tier,
|
|
2127
2435
|
counts: {
|
|
@@ -2141,7 +2449,8 @@ async function buildGraph(options) {
|
|
|
2141
2449
|
const survivingIds = new Set(analysis.nodes.map((n) => n.id));
|
|
2142
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 }));
|
|
2143
2451
|
if (unknowns.length) graph.unknowns = unknowns;
|
|
2144
|
-
|
|
2452
|
+
graph.summaries = buildSummaries(graph);
|
|
2453
|
+
{
|
|
2145
2454
|
const facts = buildFacts(parses, analysis.nodes, analysis.edges);
|
|
2146
2455
|
if (facts.length) graph.facts = facts;
|
|
2147
2456
|
}
|
|
@@ -2179,14 +2488,14 @@ function toRepoRel(p) {
|
|
|
2179
2488
|
function loadScipIndex(root, explicit) {
|
|
2180
2489
|
const candidates = [
|
|
2181
2490
|
explicit,
|
|
2182
|
-
|
|
2183
|
-
|
|
2491
|
+
path19.join(root, "index.scip"),
|
|
2492
|
+
path19.join(root, ".vibgrate", "index.scip")
|
|
2184
2493
|
].filter((p) => Boolean(p));
|
|
2185
2494
|
for (const file of candidates) {
|
|
2186
|
-
const abs =
|
|
2187
|
-
if (!
|
|
2495
|
+
const abs = path19.isAbsolute(file) ? file : path19.resolve(root, file);
|
|
2496
|
+
if (!fs18.existsSync(abs)) continue;
|
|
2188
2497
|
try {
|
|
2189
|
-
const index = decodeScipIndex(new Uint8Array(
|
|
2498
|
+
const index = decodeScipIndex(new Uint8Array(fs18.readFileSync(abs)));
|
|
2190
2499
|
if (index.documents.length) {
|
|
2191
2500
|
return { index, tool: index.toolVersion ? `${index.toolName} ${index.toolVersion}` : index.toolName };
|
|
2192
2501
|
}
|
|
@@ -2301,21 +2610,21 @@ function usageError(message) {
|
|
|
2301
2610
|
}
|
|
2302
2611
|
var SNAPSHOT_VERSION = "vg-freshness/1";
|
|
2303
2612
|
function snapshotPath(root) {
|
|
2304
|
-
return
|
|
2613
|
+
return path19.join(cacheDir(root), "freshness.json");
|
|
2305
2614
|
}
|
|
2306
2615
|
function writeSnapshot(root, corpusHash, fileStats, scope = {}) {
|
|
2307
2616
|
const files = {};
|
|
2308
2617
|
for (const f of fileStats) files[f.rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: f.hash };
|
|
2309
2618
|
const snapshot = { version: SNAPSHOT_VERSION, corpusHash, scope: pruneScope(scope), files };
|
|
2310
2619
|
try {
|
|
2311
|
-
|
|
2312
|
-
|
|
2620
|
+
fs18.mkdirSync(cacheDir(root), { recursive: true });
|
|
2621
|
+
fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2313
2622
|
} catch {
|
|
2314
2623
|
}
|
|
2315
2624
|
}
|
|
2316
2625
|
function loadSnapshot(root) {
|
|
2317
2626
|
try {
|
|
2318
|
-
const loaded = JSON.parse(
|
|
2627
|
+
const loaded = JSON.parse(fs18.readFileSync(snapshotPath(root), "utf8"));
|
|
2319
2628
|
if (loaded.version === SNAPSHOT_VERSION && loaded.files && typeof loaded.corpusHash === "string") {
|
|
2320
2629
|
return { ...loaded, scope: loaded.scope ?? {} };
|
|
2321
2630
|
}
|
|
@@ -2335,7 +2644,14 @@ function probeFreshness(root) {
|
|
|
2335
2644
|
const { scope } = snapshot;
|
|
2336
2645
|
let discovered;
|
|
2337
2646
|
try {
|
|
2338
|
-
|
|
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);
|
|
2339
2655
|
} catch {
|
|
2340
2656
|
return null;
|
|
2341
2657
|
}
|
|
@@ -2351,15 +2667,25 @@ function probeFreshness(root) {
|
|
|
2351
2667
|
}
|
|
2352
2668
|
let stat;
|
|
2353
2669
|
try {
|
|
2354
|
-
stat =
|
|
2670
|
+
stat = fs18.statSync(file.abs);
|
|
2355
2671
|
} catch {
|
|
2356
2672
|
drift.removed.push(file.rel);
|
|
2357
2673
|
continue;
|
|
2358
2674
|
}
|
|
2359
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
|
+
}
|
|
2360
2686
|
let hash = "";
|
|
2361
2687
|
try {
|
|
2362
|
-
hash = hashBytes(
|
|
2688
|
+
hash = hashBytes(fs18.readFileSync(file.abs));
|
|
2363
2689
|
} catch {
|
|
2364
2690
|
drift.removed.push(file.rel);
|
|
2365
2691
|
continue;
|
|
@@ -2376,7 +2702,7 @@ function probeFreshness(root) {
|
|
|
2376
2702
|
}
|
|
2377
2703
|
if (absorbed && !hasDrift(drift)) {
|
|
2378
2704
|
try {
|
|
2379
|
-
|
|
2705
|
+
fs18.writeFileSync(snapshotPath(root), stableStringify(snapshot, 0));
|
|
2380
2706
|
} catch {
|
|
2381
2707
|
}
|
|
2382
2708
|
}
|
|
@@ -2422,6 +2748,30 @@ function shortestPath(graph, srcId, dstId) {
|
|
|
2422
2748
|
if (reverse) return { ids: reverse.slice().reverse(), direction: "reverse" };
|
|
2423
2749
|
return null;
|
|
2424
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
|
+
}
|
|
2425
2775
|
function coveringTests(graph, node, index) {
|
|
2426
2776
|
const idx = index ?? indexFor(graph);
|
|
2427
2777
|
const byId = idx.nodeById;
|
|
@@ -2457,10 +2807,10 @@ function testsToRun(graph, rootId, depth = 4) {
|
|
|
2457
2807
|
};
|
|
2458
2808
|
}
|
|
2459
2809
|
function detectRunner(root, lang) {
|
|
2460
|
-
const pkgPath =
|
|
2461
|
-
if (
|
|
2810
|
+
const pkgPath = path19.join(root, "package.json");
|
|
2811
|
+
if (fs18.existsSync(pkgPath)) {
|
|
2462
2812
|
try {
|
|
2463
|
-
const pkg = JSON.parse(
|
|
2813
|
+
const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf8"));
|
|
2464
2814
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2465
2815
|
if (deps.vitest) return { name: "vitest", command: (f) => `npx vitest run ${f.join(" ")}`.trim() };
|
|
2466
2816
|
if (deps.jest) return { name: "jest", command: (f) => `npx jest ${f.join(" ")}`.trim() };
|
|
@@ -2468,10 +2818,10 @@ function detectRunner(root, lang) {
|
|
|
2468
2818
|
} catch {
|
|
2469
2819
|
}
|
|
2470
2820
|
}
|
|
2471
|
-
if (lang === "py" ||
|
|
2821
|
+
if (lang === "py" || fs18.existsSync(path19.join(root, "pyproject.toml")) || fs18.existsSync(path19.join(root, "pytest.ini"))) {
|
|
2472
2822
|
return { name: "pytest", command: (f) => `pytest ${f.join(" ")}`.trim() };
|
|
2473
2823
|
}
|
|
2474
|
-
if (lang === "go" ||
|
|
2824
|
+
if (lang === "go" || fs18.existsSync(path19.join(root, "go.mod"))) {
|
|
2475
2825
|
return { name: "go test", command: () => "go test ./..." };
|
|
2476
2826
|
}
|
|
2477
2827
|
if (lang === "cs" || hasFile(root, /\.csproj$/) || hasFile(root, /\.sln$/)) {
|
|
@@ -2482,16 +2832,16 @@ function detectRunner(root, lang) {
|
|
|
2482
2832
|
}
|
|
2483
2833
|
function hasFile(root, re) {
|
|
2484
2834
|
try {
|
|
2485
|
-
return
|
|
2835
|
+
return fs18.readdirSync(root).some((f) => re.test(f));
|
|
2486
2836
|
} catch {
|
|
2487
2837
|
return false;
|
|
2488
2838
|
}
|
|
2489
2839
|
}
|
|
2490
2840
|
function findGitRoot(startDir = process.cwd()) {
|
|
2491
|
-
let dir =
|
|
2841
|
+
let dir = path19.resolve(startDir);
|
|
2492
2842
|
for (; ; ) {
|
|
2493
|
-
if (
|
|
2494
|
-
const parent =
|
|
2843
|
+
if (fs18.existsSync(path19.join(dir, ".git"))) return dir;
|
|
2844
|
+
const parent = path19.dirname(dir);
|
|
2495
2845
|
if (parent === dir) return null;
|
|
2496
2846
|
dir = parent;
|
|
2497
2847
|
}
|
|
@@ -2502,19 +2852,19 @@ function normalizeEntry(line) {
|
|
|
2502
2852
|
function ensureGitignored(entry, startDir = process.cwd()) {
|
|
2503
2853
|
const root = findGitRoot(startDir);
|
|
2504
2854
|
if (!root) return { status: "not-a-repo", entry };
|
|
2505
|
-
const gitignorePath =
|
|
2855
|
+
const gitignorePath = path19.join(root, ".gitignore");
|
|
2506
2856
|
const target = normalizeEntry(entry);
|
|
2507
2857
|
let existing = "";
|
|
2508
2858
|
let fileExisted = true;
|
|
2509
2859
|
try {
|
|
2510
|
-
existing =
|
|
2860
|
+
existing = fs18.readFileSync(gitignorePath, "utf8");
|
|
2511
2861
|
} catch {
|
|
2512
2862
|
fileExisted = false;
|
|
2513
2863
|
}
|
|
2514
2864
|
const alreadyPresent = existing.split(/\r?\n/).some((line) => normalizeEntry(line) === target);
|
|
2515
2865
|
if (alreadyPresent) return { status: "present", entry, gitignorePath };
|
|
2516
2866
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
2517
|
-
|
|
2867
|
+
fs18.appendFileSync(gitignorePath, `${needsLeadingNewline ? "\n" : ""}${entry}
|
|
2518
2868
|
`, "utf8");
|
|
2519
2869
|
return { status: fileExisted ? "added" : "created", entry, gitignorePath };
|
|
2520
2870
|
}
|
|
@@ -2668,14 +3018,14 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
2668
3018
|
console.log(chalk.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
2669
3019
|
}
|
|
2670
3020
|
if (opts.write) {
|
|
2671
|
-
const writePath =
|
|
3021
|
+
const writePath = path19.resolve(opts.write);
|
|
2672
3022
|
await writeTextFile(writePath, dsn + "\n");
|
|
2673
3023
|
console.log("");
|
|
2674
3024
|
console.log(chalk.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
2675
|
-
const root = findGitRoot(
|
|
2676
|
-
const rel2 = root ?
|
|
3025
|
+
const root = findGitRoot(path19.dirname(writePath));
|
|
3026
|
+
const rel2 = root ? path19.relative(root, writePath).split(path19.sep).join("/") : "";
|
|
2677
3027
|
if (root && rel2 && !rel2.startsWith("..")) {
|
|
2678
|
-
const res = ensureGitignored(rel2,
|
|
3028
|
+
const res = ensureGitignored(rel2, path19.dirname(writePath));
|
|
2679
3029
|
if (res.status === "created") {
|
|
2680
3030
|
console.log(chalk.green("\u2714") + ` Created .gitignore and ignored ${rel2}`);
|
|
2681
3031
|
} else if (res.status === "added") {
|
|
@@ -2693,30 +3043,30 @@ dsnCommand.command("create").description("Create a new DSN token").option("--ing
|
|
|
2693
3043
|
var STORE_DIRNAME = ".vibgrate";
|
|
2694
3044
|
var STORE_FILENAME = "credentials.json";
|
|
2695
3045
|
function homeCredentialsPath() {
|
|
2696
|
-
return
|
|
3046
|
+
return path19.join(os.homedir(), STORE_DIRNAME, STORE_FILENAME);
|
|
2697
3047
|
}
|
|
2698
3048
|
function projectCredentialsPath(cwd = process.cwd()) {
|
|
2699
3049
|
const root = findGitRoot(cwd) ?? cwd;
|
|
2700
|
-
return
|
|
3050
|
+
return path19.join(root, STORE_DIRNAME, STORE_FILENAME);
|
|
2701
3051
|
}
|
|
2702
3052
|
function credentialsPath(opts = {}) {
|
|
2703
3053
|
const override = process.env.VIBGRATE_CREDENTIALS?.trim();
|
|
2704
|
-
if (override) return
|
|
3054
|
+
if (override) return path19.resolve(override);
|
|
2705
3055
|
const local = projectCredentialsPath(opts.cwd);
|
|
2706
3056
|
if (opts.local) return local;
|
|
2707
|
-
if (
|
|
3057
|
+
if (fs18.existsSync(local)) return local;
|
|
2708
3058
|
return homeCredentialsPath();
|
|
2709
3059
|
}
|
|
2710
3060
|
function gitignoreEntryForCredentials(repoRoot, credsFile = credentialsPath()) {
|
|
2711
|
-
const rel2 =
|
|
2712
|
-
if (rel2 && !rel2.startsWith("..") && !
|
|
2713
|
-
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("/");
|
|
2714
3064
|
}
|
|
2715
3065
|
return `${STORE_DIRNAME}/${STORE_FILENAME}`;
|
|
2716
3066
|
}
|
|
2717
3067
|
function readStoredCredentials(opts = {}) {
|
|
2718
3068
|
try {
|
|
2719
|
-
const raw =
|
|
3069
|
+
const raw = fs18.readFileSync(credentialsPath(opts), "utf8");
|
|
2720
3070
|
const parsed = JSON.parse(raw);
|
|
2721
3071
|
return parsed && typeof parsed.dsn === "string" && parsed.dsn ? parsed : null;
|
|
2722
3072
|
} catch {
|
|
@@ -2725,16 +3075,16 @@ function readStoredCredentials(opts = {}) {
|
|
|
2725
3075
|
}
|
|
2726
3076
|
function writeStoredCredentials(creds, opts = {}) {
|
|
2727
3077
|
const file = credentialsPath(opts);
|
|
2728
|
-
|
|
2729
|
-
|
|
3078
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
3079
|
+
fs18.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
2730
3080
|
try {
|
|
2731
|
-
|
|
3081
|
+
fs18.chmodSync(file, 384);
|
|
2732
3082
|
} catch {
|
|
2733
3083
|
}
|
|
2734
3084
|
}
|
|
2735
3085
|
function clearStoredCredentials(opts = {}) {
|
|
2736
3086
|
try {
|
|
2737
|
-
|
|
3087
|
+
fs18.rmSync(credentialsPath(opts));
|
|
2738
3088
|
return true;
|
|
2739
3089
|
} catch {
|
|
2740
3090
|
return false;
|
|
@@ -3105,7 +3455,7 @@ var pushCommand = new Command("push").description("Push scan results to Vibgrate
|
|
|
3105
3455
|
if (opts.strict) process.exit(1);
|
|
3106
3456
|
return;
|
|
3107
3457
|
}
|
|
3108
|
-
const filePath =
|
|
3458
|
+
const filePath = path19.resolve(opts.file);
|
|
3109
3459
|
if (!await pathExists(filePath)) {
|
|
3110
3460
|
console.error(chalk.red(`Scan artifact not found: ${filePath}`));
|
|
3111
3461
|
console.error(chalk.dim('Run "vibgrate scan" first.'));
|
|
@@ -3200,7 +3550,7 @@ function measure(root, files) {
|
|
|
3200
3550
|
for (const rel2 of files) {
|
|
3201
3551
|
let size;
|
|
3202
3552
|
try {
|
|
3203
|
-
size =
|
|
3553
|
+
size = fs18.statSync(path19.join(root, rel2)).size;
|
|
3204
3554
|
} catch {
|
|
3205
3555
|
continue;
|
|
3206
3556
|
}
|
|
@@ -3223,9 +3573,9 @@ function matchLines(text, needleLower) {
|
|
|
3223
3573
|
}
|
|
3224
3574
|
function readCandidate(abs, knownSize) {
|
|
3225
3575
|
try {
|
|
3226
|
-
const size = knownSize ??
|
|
3576
|
+
const size = knownSize ?? fs18.statSync(abs).size;
|
|
3227
3577
|
if (size > MAX_FILE_BYTES) return null;
|
|
3228
|
-
const text =
|
|
3578
|
+
const text = fs18.readFileSync(abs, "utf8");
|
|
3229
3579
|
return text.includes(NUL) ? null : text;
|
|
3230
3580
|
} catch {
|
|
3231
3581
|
return null;
|
|
@@ -3236,7 +3586,7 @@ function scanInline(root, files, needleLower, cap, stopAtCap, sizes) {
|
|
|
3236
3586
|
let total = 0;
|
|
3237
3587
|
let truncated = false;
|
|
3238
3588
|
for (const rel2 of files) {
|
|
3239
|
-
const text = readCandidate(
|
|
3589
|
+
const text = readCandidate(path19.join(root, rel2), sizes?.get(rel2));
|
|
3240
3590
|
if (text === null) continue;
|
|
3241
3591
|
for (const h of matchLines(text, needleLower)) {
|
|
3242
3592
|
total++;
|
|
@@ -3303,12 +3653,12 @@ var WORKER_SOURCE = [
|
|
|
3303
3653
|
"parentPort.postMessage({ hits, total, truncated });"
|
|
3304
3654
|
].join("\n");
|
|
3305
3655
|
function runWorker(root, files, needleLower) {
|
|
3306
|
-
return new Promise((
|
|
3656
|
+
return new Promise((resolve13, reject) => {
|
|
3307
3657
|
const w = new Worker(WORKER_SOURCE, { eval: true, workerData: { root, files, needleLower } });
|
|
3308
3658
|
let settled = false;
|
|
3309
3659
|
w.once("message", (m) => {
|
|
3310
3660
|
settled = true;
|
|
3311
|
-
|
|
3661
|
+
resolve13(m);
|
|
3312
3662
|
});
|
|
3313
3663
|
w.once("error", reject);
|
|
3314
3664
|
w.once("exit", (code) => {
|
|
@@ -3377,10 +3727,10 @@ var BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
|
3377
3727
|
".pdf"
|
|
3378
3728
|
]);
|
|
3379
3729
|
function hasBinaryExt(rel2) {
|
|
3380
|
-
return BINARY_EXTS.has(
|
|
3730
|
+
return BINARY_EXTS.has(path19.extname(rel2).toLowerCase());
|
|
3381
3731
|
}
|
|
3382
3732
|
function toPosix4(rel2) {
|
|
3383
|
-
return
|
|
3733
|
+
return path19.sep === "\\" ? rel2.replaceAll("\\", "/") : rel2;
|
|
3384
3734
|
}
|
|
3385
3735
|
async function searchSymbols(graph, root, query, limit) {
|
|
3386
3736
|
let q = query.trim();
|
|
@@ -3519,13 +3869,13 @@ function walkCandidates(root) {
|
|
|
3519
3869
|
const dir = stack.pop();
|
|
3520
3870
|
let entries;
|
|
3521
3871
|
try {
|
|
3522
|
-
entries =
|
|
3872
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
3523
3873
|
} catch {
|
|
3524
3874
|
continue;
|
|
3525
3875
|
}
|
|
3526
3876
|
for (const e of entries) {
|
|
3527
3877
|
if (e.name.startsWith(".") || IGNORE_DIRS.has(e.name)) continue;
|
|
3528
|
-
const abs =
|
|
3878
|
+
const abs = path19.join(dir, e.name);
|
|
3529
3879
|
if (e.isDirectory()) {
|
|
3530
3880
|
stack.push(abs);
|
|
3531
3881
|
continue;
|
|
@@ -3535,7 +3885,7 @@ function walkCandidates(root) {
|
|
|
3535
3885
|
truncated = true;
|
|
3536
3886
|
continue;
|
|
3537
3887
|
}
|
|
3538
|
-
files.push(toPosix4(
|
|
3888
|
+
files.push(toPosix4(path19.relative(root, abs)));
|
|
3539
3889
|
}
|
|
3540
3890
|
}
|
|
3541
3891
|
files.sort();
|
|
@@ -3566,12 +3916,12 @@ function ripgrepCandidates(root, needle) {
|
|
|
3566
3916
|
if (files.length > MAX_FILES_SCANNED) return { files: files.slice(0, MAX_FILES_SCANNED), truncated: true };
|
|
3567
3917
|
return { files, truncated: false };
|
|
3568
3918
|
}
|
|
3569
|
-
function parseRgFileList(stdout,
|
|
3919
|
+
function parseRgFileList(stdout, sep10 = path19.sep) {
|
|
3570
3920
|
const seen = /* @__PURE__ */ new Set();
|
|
3571
3921
|
for (const raw of stdout.split("\n")) {
|
|
3572
3922
|
if (!raw) continue;
|
|
3573
|
-
const
|
|
3574
|
-
const rel2 =
|
|
3923
|
+
const posix2 = sep10 === "\\" ? raw.replaceAll("\\", "/") : raw;
|
|
3924
|
+
const rel2 = posix2.startsWith("./") ? posix2.slice(2) : posix2;
|
|
3575
3925
|
if (!rel2) continue;
|
|
3576
3926
|
const segs = rel2.split("/");
|
|
3577
3927
|
if (segs.some((s) => s.startsWith(".") || IGNORE_DIRS.has(s))) continue;
|
|
@@ -3582,8 +3932,8 @@ function parseRgFileList(stdout, sep9 = path18.sep) {
|
|
|
3582
3932
|
}
|
|
3583
3933
|
function readScanArtifact(root) {
|
|
3584
3934
|
try {
|
|
3585
|
-
const file =
|
|
3586
|
-
return JSON.parse(
|
|
3935
|
+
const file = path19.join(root, ".vibgrate", "scan_result.json");
|
|
3936
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
3587
3937
|
} catch {
|
|
3588
3938
|
return null;
|
|
3589
3939
|
}
|
|
@@ -3839,6 +4189,12 @@ ${chunk2}`);
|
|
|
3839
4189
|
}
|
|
3840
4190
|
|
|
3841
4191
|
// src/engine/quality.ts
|
|
4192
|
+
var IDENT_RE = /^(?:[A-Za-z][a-z0-9]*[A-Z][A-Za-z0-9]*|[a-z][a-z0-9]*(?:_[a-z0-9]+)+|[a-z][a-z0-9]*(?:-[a-z0-9]+)+)$/;
|
|
4193
|
+
function identifierTerms(query) {
|
|
4194
|
+
if (!query) return [];
|
|
4195
|
+
const raw = [...new Set(query.split(/[^A-Za-z0-9_-]+/).filter((w) => w.length > 2))];
|
|
4196
|
+
return raw.filter((t) => IDENT_RE.test(t) || t.length >= 6 && /[A-Z]/.test(t) && /[a-z]/.test(t));
|
|
4197
|
+
}
|
|
3842
4198
|
function assessDocQuality(content, opts = {}) {
|
|
3843
4199
|
const minTokens = opts.minTokens ?? 60;
|
|
3844
4200
|
const lc = content.toLowerCase();
|
|
@@ -3856,20 +4212,36 @@ function assessDocQuality(content, opts = {}) {
|
|
|
3856
4212
|
else reasons.push("library name absent");
|
|
3857
4213
|
}
|
|
3858
4214
|
const qt = tokenizeQuery(opts.query);
|
|
4215
|
+
const idents = identifierTerms(opts.query);
|
|
3859
4216
|
let onTopic = qt.length === 0;
|
|
3860
4217
|
if (qt.length) {
|
|
3861
|
-
const
|
|
3862
|
-
if (
|
|
3863
|
-
|
|
3864
|
-
onTopic =
|
|
4218
|
+
const missingIdents = idents.filter((id) => !lc.includes(id.toLowerCase()));
|
|
4219
|
+
if (missingIdents.length) {
|
|
4220
|
+
reasons.push(`API symbol(s) absent: ${missingIdents.join(", ")}`);
|
|
4221
|
+
onTopic = false;
|
|
3865
4222
|
} else {
|
|
3866
|
-
|
|
4223
|
+
const hits = qt.filter((t) => lc.includes(t)).length;
|
|
4224
|
+
const need = Math.max(1, Math.floor(qt.length / 2) + 1);
|
|
4225
|
+
if (hits >= need) {
|
|
4226
|
+
score += Math.min(hits, 2);
|
|
4227
|
+
onTopic = true;
|
|
4228
|
+
} else {
|
|
4229
|
+
reasons.push(hits === 0 ? "query terms absent" : `query terms weak (${hits}/${qt.length})`);
|
|
4230
|
+
onTopic = false;
|
|
4231
|
+
}
|
|
3867
4232
|
}
|
|
3868
4233
|
}
|
|
3869
4234
|
if (opts.symbols?.length) {
|
|
3870
4235
|
if (opts.symbols.some((s) => s.length > 2 && lc.includes(s.toLowerCase()))) score += 1;
|
|
3871
4236
|
else reasons.push("no API symbols present");
|
|
3872
4237
|
}
|
|
4238
|
+
if (opts.readmeOnly && (idents.length > 0 || qt.length >= 2)) {
|
|
4239
|
+
onTopic = false;
|
|
4240
|
+
if (!reasons.includes("README-only source cannot answer a focused API query")) {
|
|
4241
|
+
reasons.push("README-only source cannot answer a focused API query");
|
|
4242
|
+
}
|
|
4243
|
+
score = Math.min(score, 4);
|
|
4244
|
+
}
|
|
3873
4245
|
const sufficient = hasCode && tokens >= minTokens && onTopic;
|
|
3874
4246
|
return { score, sufficient, reasons };
|
|
3875
4247
|
}
|
|
@@ -3973,7 +4345,7 @@ var CACHE_VERSION = "vg-hosted-docs/1";
|
|
|
3973
4345
|
var HOSTED_DOCS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3974
4346
|
var MAX_ENTRIES = 64;
|
|
3975
4347
|
function cachePath(root) {
|
|
3976
|
-
return
|
|
4348
|
+
return path19.join(cacheDir(root), "hosted-docs.json");
|
|
3977
4349
|
}
|
|
3978
4350
|
function hostedDocsCacheKey(req, opts = {}) {
|
|
3979
4351
|
const material = JSON.stringify([
|
|
@@ -3989,7 +4361,7 @@ function hostedDocsCacheKey(req, opts = {}) {
|
|
|
3989
4361
|
}
|
|
3990
4362
|
function loadFile(root) {
|
|
3991
4363
|
try {
|
|
3992
|
-
const parsed = JSON.parse(
|
|
4364
|
+
const parsed = JSON.parse(fs18.readFileSync(cachePath(root), "utf8"));
|
|
3993
4365
|
if (parsed && parsed.version === CACHE_VERSION && parsed.entries && typeof parsed.entries === "object") return parsed;
|
|
3994
4366
|
} catch {
|
|
3995
4367
|
}
|
|
@@ -4003,8 +4375,8 @@ function saveFile(root, file) {
|
|
|
4003
4375
|
delete file.entries[k];
|
|
4004
4376
|
}
|
|
4005
4377
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4378
|
+
fs18.mkdirSync(cacheDir(root), { recursive: true });
|
|
4379
|
+
fs18.writeFileSync(cachePath(root), JSON.stringify(file));
|
|
4008
4380
|
} catch {
|
|
4009
4381
|
}
|
|
4010
4382
|
}
|
|
@@ -4292,11 +4664,11 @@ var TOOLS = [
|
|
|
4292
4664
|
area: node.area
|
|
4293
4665
|
};
|
|
4294
4666
|
if (ctx?.dedup && ctx.seen?.has(node.id)) {
|
|
4295
|
-
return { ...base, repeat: true, callsTotal:
|
|
4667
|
+
return { ...base, repeat: true, callsTotal: uniqueNames2(calleeRecs.map((x) => x.node.qualifiedName)).length, calledByTotal: uniqueNames2(callerRecs.map((x) => x.node.qualifiedName)).length };
|
|
4296
4668
|
}
|
|
4297
4669
|
const edgeCap = isDetailed(args) ? NODE_EDGE_CAP : CONCISE_MATCHES;
|
|
4298
|
-
const calls = boundList(
|
|
4299
|
-
const calledBy = boundList(
|
|
4670
|
+
const calls = boundList(uniqueNames2(calleeRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
4671
|
+
const calledBy = boundList(uniqueNames2(callerRecs.map((x) => x.node.qualifiedName)), edgeCap);
|
|
4300
4672
|
ctx?.seen?.add(node.id);
|
|
4301
4673
|
return {
|
|
4302
4674
|
...base,
|
|
@@ -4325,7 +4697,7 @@ var TOOLS = [
|
|
|
4325
4697
|
if (!ra.node) return { endpoint: "a", ...unresolved(ra.candidates) };
|
|
4326
4698
|
if (!rb.node) return { endpoint: "b", ...unresolved(rb.candidates) };
|
|
4327
4699
|
const result = shortestPath(graph, ra.node.id, rb.node.id);
|
|
4328
|
-
if (!result) return
|
|
4700
|
+
if (!result) return pathDisconnect(graph, ra.node.id, rb.node.id);
|
|
4329
4701
|
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
4330
4702
|
return { connected: true, direction: result.direction, path: result.ids.map((id) => byId.get(id)?.qualifiedName ?? id) };
|
|
4331
4703
|
}
|
|
@@ -4418,27 +4790,68 @@ var TOOLS = [
|
|
|
4418
4790
|
},
|
|
4419
4791
|
{
|
|
4420
4792
|
name: "list_hubs",
|
|
4421
|
-
description: "Most-depended-on symbols.",
|
|
4793
|
+
description: "Most-depended-on symbols (includes precomputed blast-radius counts when available).",
|
|
4422
4794
|
inputSchema: obj({ limit: { type: "number" } }),
|
|
4423
|
-
handler: (graph, args) =>
|
|
4795
|
+
handler: (graph, args) => {
|
|
4796
|
+
const limit = numOr(args.limit, 20);
|
|
4797
|
+
if (graph.summaries?.hubs?.length) {
|
|
4798
|
+
return graph.summaries.hubs.slice(0, limit).map((h) => ({
|
|
4799
|
+
name: h.name,
|
|
4800
|
+
kind: h.kind,
|
|
4801
|
+
file: h.file,
|
|
4802
|
+
importance: h.importance,
|
|
4803
|
+
isHub: true,
|
|
4804
|
+
directDependents: h.direct,
|
|
4805
|
+
depth2Dependents: h.depth2,
|
|
4806
|
+
filesAffected: h.files,
|
|
4807
|
+
tested: h.tested
|
|
4808
|
+
}));
|
|
4809
|
+
}
|
|
4810
|
+
return graph.nodes.filter((n) => n.kind !== "file" && n.kind !== "external").sort((a, b) => b.importance - a.importance).slice(0, limit).map((n) => ({
|
|
4811
|
+
name: n.qualifiedName,
|
|
4812
|
+
kind: n.kind,
|
|
4813
|
+
file: n.file,
|
|
4814
|
+
line: n.span.start,
|
|
4815
|
+
importance: n.importance,
|
|
4816
|
+
isHub: n.isHub
|
|
4817
|
+
}));
|
|
4818
|
+
}
|
|
4424
4819
|
},
|
|
4425
4820
|
{
|
|
4426
4821
|
name: "get_facts",
|
|
4427
|
-
description: "Deterministic facts for a node (contract/invariant
|
|
4428
|
-
inputSchema: obj(
|
|
4822
|
+
description: "Deterministic facts for a node (contract / invariant / characterization).",
|
|
4823
|
+
inputSchema: obj(
|
|
4824
|
+
{
|
|
4825
|
+
name: { type: "string" },
|
|
4826
|
+
pick: { type: "number", description: "nth candidate if ambiguous" }
|
|
4827
|
+
},
|
|
4828
|
+
["name"]
|
|
4829
|
+
),
|
|
4429
4830
|
handler: (graph, args) => {
|
|
4430
|
-
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
4831
|
+
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
4431
4832
|
if (!node) return unresolved(candidates);
|
|
4432
|
-
if (!graph.facts)
|
|
4833
|
+
if (!graph.facts) {
|
|
4834
|
+
return {
|
|
4835
|
+
node: node.qualifiedName,
|
|
4836
|
+
facts: [],
|
|
4837
|
+
note: "map has no facts \u2014 rebuild with a current vg (facts ship on every build)"
|
|
4838
|
+
};
|
|
4839
|
+
}
|
|
4433
4840
|
return { node: node.qualifiedName, facts: graph.facts.filter((f) => f.subjectIds.includes(node.id)) };
|
|
4434
4841
|
}
|
|
4435
4842
|
},
|
|
4436
4843
|
{
|
|
4437
4844
|
name: "guide_node",
|
|
4438
4845
|
description: "Cited standards/practices for a node (OWASP/CWE).",
|
|
4439
|
-
inputSchema: obj(
|
|
4846
|
+
inputSchema: obj(
|
|
4847
|
+
{
|
|
4848
|
+
name: { type: "string" },
|
|
4849
|
+
pick: { type: "number", description: "nth candidate if ambiguous" }
|
|
4850
|
+
},
|
|
4851
|
+
["name"]
|
|
4852
|
+
),
|
|
4440
4853
|
handler: (graph, args) => {
|
|
4441
|
-
const { node, candidates } = resolveOne(graph, String(args.name ?? ""));
|
|
4854
|
+
const { node, candidates } = resolveOne(graph, String(args.name ?? ""), numOrU(args.pick));
|
|
4442
4855
|
if (!node) return unresolved(candidates);
|
|
4443
4856
|
const entries = new Map(FREE_PACK.entries.map((e) => [e.id, e]));
|
|
4444
4857
|
return {
|
|
@@ -4632,13 +5045,24 @@ var TOOLS = [
|
|
|
4632
5045
|
const verbosity = ["concise", "balanced", "exhaustive"].includes(String(args.verbosity)) ? String(args.verbosity) : query ? "concise" : "balanced";
|
|
4633
5046
|
const budget = numOrU(args.max_tokens) ?? numOrU(args.tokens) ?? VERBOSITY_BUDGET[verbosity];
|
|
4634
5047
|
const ver = resolveVersion(ctx.root, id);
|
|
4635
|
-
const
|
|
4636
|
-
const
|
|
4637
|
-
|
|
4638
|
-
const
|
|
5048
|
+
const focusQuery = (() => {
|
|
5049
|
+
const q = query.trim();
|
|
5050
|
+
if (!q) return q;
|
|
5051
|
+
const bare = id.replace(/^@[^/]+\//, "").toLowerCase();
|
|
5052
|
+
const parts = q.split(/\s+/);
|
|
5053
|
+
if (parts.length > 1 && parts[0].toLowerCase().replace(/^@[^/]+\//, "") === bare) {
|
|
5054
|
+
return parts.slice(1).join(" ");
|
|
5055
|
+
}
|
|
5056
|
+
return q;
|
|
5057
|
+
})();
|
|
5058
|
+
const render = (readme, libName, readmeOnly) => {
|
|
5059
|
+
const apiSurface = localApiSurface(ctx.root, libName, focusQuery || void 0);
|
|
5060
|
+
const sel = selectForBudget({ readme, query: focusQuery || query, apiSurface, budget });
|
|
5061
|
+
const quality = assessDocQuality(sel.text, {
|
|
4639
5062
|
name: libName,
|
|
4640
|
-
query,
|
|
4641
|
-
symbols: symbolsFromApi(apiSurface)
|
|
5063
|
+
query: focusQuery || query || void 0,
|
|
5064
|
+
symbols: symbolsFromApi(apiSurface),
|
|
5065
|
+
readmeOnly: readmeOnly === true && !apiSurface
|
|
4642
5066
|
});
|
|
4643
5067
|
return {
|
|
4644
5068
|
content: sel.text,
|
|
@@ -4655,7 +5079,7 @@ var TOOLS = [
|
|
|
4655
5079
|
let answer = null;
|
|
4656
5080
|
let sufficient = false;
|
|
4657
5081
|
if (entry) {
|
|
4658
|
-
const r = render(readDoc(ctx.root, entry), entry.name);
|
|
5082
|
+
const r = render(readDoc(ctx.root, entry), entry.name, false);
|
|
4659
5083
|
sufficient = r.metadata.quality.sufficient;
|
|
4660
5084
|
answer = {
|
|
4661
5085
|
targetId: entry.id,
|
|
@@ -4670,9 +5094,9 @@ var TOOLS = [
|
|
|
4670
5094
|
metadata: r.metadata
|
|
4671
5095
|
};
|
|
4672
5096
|
} else {
|
|
4673
|
-
const local = localPackageDocs(ctx.root, id);
|
|
5097
|
+
const local = localPackageDocs(ctx.root, id, { query: focusQuery || query || void 0 });
|
|
4674
5098
|
if (local) {
|
|
4675
|
-
const r = render(local.docs, id);
|
|
5099
|
+
const r = render(local.docs, id, local.readmeOnly);
|
|
4676
5100
|
sufficient = r.metadata.quality.sufficient;
|
|
4677
5101
|
answer = {
|
|
4678
5102
|
targetId: id,
|
|
@@ -4721,14 +5145,22 @@ function conciseSummary(graph) {
|
|
|
4721
5145
|
return { counts: graph.meta.counts, languages: graph.meta.languages };
|
|
4722
5146
|
}
|
|
4723
5147
|
function summarize(graph, top = 10) {
|
|
5148
|
+
const topHubs = graph.summaries?.hubs?.length ? graph.summaries.hubs.slice(0, top).map((h) => ({
|
|
5149
|
+
name: h.name,
|
|
5150
|
+
file: h.file,
|
|
5151
|
+
importance: h.importance,
|
|
5152
|
+
directDependents: h.direct,
|
|
5153
|
+
depth2Dependents: h.depth2
|
|
5154
|
+
})) : 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 }));
|
|
4724
5155
|
return {
|
|
4725
5156
|
counts: graph.meta.counts,
|
|
4726
5157
|
languages: graph.meta.languages,
|
|
4727
5158
|
cluster: graph.meta.cluster,
|
|
5159
|
+
analysisTier: graph.meta.analysisTier ?? "full",
|
|
4728
5160
|
resolver: graph.provenance.resolver,
|
|
4729
5161
|
generatedAt: graph.generatedAt,
|
|
4730
5162
|
topAreas: [...graph.areas].sort((a, b) => b.size - a.size).slice(0, top).map((a) => ({ id: a.id, label: a.label, size: a.size })),
|
|
4731
|
-
topHubs
|
|
5163
|
+
topHubs
|
|
4732
5164
|
};
|
|
4733
5165
|
}
|
|
4734
5166
|
function stripId(item) {
|
|
@@ -4756,7 +5188,7 @@ function numOrU(v) {
|
|
|
4756
5188
|
const n = Number(v);
|
|
4757
5189
|
return Number.isFinite(n) ? n : void 0;
|
|
4758
5190
|
}
|
|
4759
|
-
function
|
|
5191
|
+
function uniqueNames2(xs) {
|
|
4760
5192
|
return [...new Set(xs)];
|
|
4761
5193
|
}
|
|
4762
5194
|
var HOT_TOOLS = ["orient", "search_symbols", "query_graph", "get_node"];
|
|
@@ -4979,12 +5411,12 @@ function detectAssistants(root, opts = {}) {
|
|
|
4979
5411
|
const which = opts.which ?? whichOnPath;
|
|
4980
5412
|
const found = [];
|
|
4981
5413
|
for (const assistant of ASSISTANTS) {
|
|
4982
|
-
const repoHit = (assistant.markers ?? []).find((m) =>
|
|
5414
|
+
const repoHit = (assistant.markers ?? []).find((m) => fs18.existsSync(path19.join(root, m)));
|
|
4983
5415
|
if (repoHit) {
|
|
4984
5416
|
found.push({ assistant, via: "repo", marker: repoHit });
|
|
4985
5417
|
continue;
|
|
4986
5418
|
}
|
|
4987
|
-
const homeHit = (assistant.homeMarkers ?? []).find((m) =>
|
|
5419
|
+
const homeHit = (assistant.homeMarkers ?? []).find((m) => fs18.existsSync(path19.join(home, m)));
|
|
4988
5420
|
if (homeHit) {
|
|
4989
5421
|
found.push({ assistant, via: "home", marker: homeHit });
|
|
4990
5422
|
continue;
|
|
@@ -5016,19 +5448,19 @@ function installAssistant(a, opts) {
|
|
|
5016
5448
|
const skipped = [];
|
|
5017
5449
|
let note;
|
|
5018
5450
|
if (a.skill) {
|
|
5019
|
-
writeFileEnsured(
|
|
5451
|
+
writeFileEnsured(path19.join(opts.root, a.skill), skillMarkdown(a.id));
|
|
5020
5452
|
wrote.push(a.skill);
|
|
5021
5453
|
}
|
|
5022
5454
|
if (a.mcp) {
|
|
5023
5455
|
const launch = opts.launch ?? detectServeLaunch();
|
|
5024
|
-
upsertMcp(
|
|
5456
|
+
upsertMcp(path19.join(opts.root, a.mcp.file), a.mcp, launch);
|
|
5025
5457
|
wrote.push(a.mcp.file);
|
|
5026
5458
|
note = launch.note;
|
|
5027
5459
|
} else {
|
|
5028
5460
|
skipped.push("mcp (host-specific setup)");
|
|
5029
5461
|
}
|
|
5030
5462
|
if (a.nudge && opts.hook !== false) {
|
|
5031
|
-
writeNudge(
|
|
5463
|
+
writeNudge(path19.join(opts.root, a.nudge.file), a.nudge, opts.smallRepo, a.id);
|
|
5032
5464
|
wrote.push(a.nudge.file);
|
|
5033
5465
|
} else if (a.nudge) {
|
|
5034
5466
|
skipped.push("nudge (--no-hook)");
|
|
@@ -5038,41 +5470,41 @@ function installAssistant(a, opts) {
|
|
|
5038
5470
|
function uninstallAssistant(a, root, purge) {
|
|
5039
5471
|
const removed = [];
|
|
5040
5472
|
if (a.mcp) {
|
|
5041
|
-
const file =
|
|
5473
|
+
const file = path19.join(root, a.mcp.file);
|
|
5042
5474
|
if (removeMcp(file, a.mcp)) removed.push(a.mcp.file);
|
|
5043
5475
|
}
|
|
5044
5476
|
if (a.nudge) {
|
|
5045
|
-
const file =
|
|
5477
|
+
const file = path19.join(root, a.nudge.file);
|
|
5046
5478
|
if (a.nudge.kind === "block") {
|
|
5047
5479
|
if (removeBlock(file)) removed.push(a.nudge.file);
|
|
5048
|
-
} else if (
|
|
5049
|
-
|
|
5480
|
+
} else if (fs18.existsSync(file)) {
|
|
5481
|
+
fs18.rmSync(file);
|
|
5050
5482
|
removed.push(a.nudge.file);
|
|
5051
5483
|
}
|
|
5052
5484
|
}
|
|
5053
5485
|
if (purge && a.skill) {
|
|
5054
|
-
const file =
|
|
5055
|
-
if (
|
|
5056
|
-
|
|
5486
|
+
const file = path19.join(root, a.skill);
|
|
5487
|
+
if (fs18.existsSync(file)) {
|
|
5488
|
+
fs18.rmSync(file);
|
|
5057
5489
|
removed.push(a.skill);
|
|
5058
5490
|
}
|
|
5059
5491
|
}
|
|
5060
5492
|
return removed;
|
|
5061
5493
|
}
|
|
5062
5494
|
function writeFileEnsured(file, content) {
|
|
5063
|
-
|
|
5064
|
-
|
|
5495
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
5496
|
+
fs18.writeFileSync(file, content);
|
|
5065
5497
|
}
|
|
5066
5498
|
function writeNavigationConfig(root) {
|
|
5067
5499
|
ensureVibgrateGitignore(root);
|
|
5068
|
-
const rel2 =
|
|
5500
|
+
const rel2 = path19.join(".vibgrate", "mcp-navigation.json");
|
|
5069
5501
|
const doc = {
|
|
5070
5502
|
_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.",
|
|
5071
5503
|
hot_core: [...HOT_TOOLS],
|
|
5072
5504
|
deferred: deferredToolNames(),
|
|
5073
5505
|
toolset: navigationToolsetConfig()
|
|
5074
5506
|
};
|
|
5075
|
-
writeFileEnsured(
|
|
5507
|
+
writeFileEnsured(path19.join(root, rel2), `${JSON.stringify(doc, null, 2)}
|
|
5076
5508
|
`);
|
|
5077
5509
|
return rel2;
|
|
5078
5510
|
}
|
|
@@ -5087,7 +5519,7 @@ function upsertMcp(file, target, launch) {
|
|
|
5087
5519
|
`);
|
|
5088
5520
|
}
|
|
5089
5521
|
function removeMcp(file, target) {
|
|
5090
|
-
if (!
|
|
5522
|
+
if (!fs18.existsSync(file)) return false;
|
|
5091
5523
|
const config = readJson(file);
|
|
5092
5524
|
const bag = config[target.key];
|
|
5093
5525
|
if (!bag || !(bag.vg !== void 0)) return false;
|
|
@@ -5106,8 +5538,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5106
5538
|
};
|
|
5107
5539
|
for (const a of ASSISTANTS) {
|
|
5108
5540
|
if (a.skill) {
|
|
5109
|
-
const file =
|
|
5110
|
-
if (
|
|
5541
|
+
const file = path19.join(root, a.skill);
|
|
5542
|
+
if (fs18.existsSync(file)) {
|
|
5111
5543
|
const text = readTextSafe(file);
|
|
5112
5544
|
const from = text === null ? null : staleVersion(text, "# vg \u2014 the code map");
|
|
5113
5545
|
if (from !== null) {
|
|
@@ -5117,8 +5549,8 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5117
5549
|
}
|
|
5118
5550
|
}
|
|
5119
5551
|
if (a.nudge) {
|
|
5120
|
-
const file =
|
|
5121
|
-
if (!
|
|
5552
|
+
const file = path19.join(root, a.nudge.file);
|
|
5553
|
+
if (!fs18.existsSync(file)) continue;
|
|
5122
5554
|
const text = readTextSafe(file);
|
|
5123
5555
|
if (text === null) continue;
|
|
5124
5556
|
if (a.nudge.kind === "block") {
|
|
@@ -5142,7 +5574,7 @@ function refreshInstalledInstructions(root, smallRepo) {
|
|
|
5142
5574
|
}
|
|
5143
5575
|
function readTextSafe(file) {
|
|
5144
5576
|
try {
|
|
5145
|
-
return
|
|
5577
|
+
return fs18.readFileSync(file, "utf8");
|
|
5146
5578
|
} catch {
|
|
5147
5579
|
return null;
|
|
5148
5580
|
}
|
|
@@ -5161,8 +5593,8 @@ ${stripMarkers(nudgeMarkdown(smallRepo, client))}` : stripMarkers(nudgeMarkdown(
|
|
|
5161
5593
|
upsertBlock(file, nudgeMarkdown(smallRepo, client));
|
|
5162
5594
|
}
|
|
5163
5595
|
function upsertBlock(file, block) {
|
|
5164
|
-
|
|
5165
|
-
let existing =
|
|
5596
|
+
fs18.mkdirSync(path19.dirname(file), { recursive: true });
|
|
5597
|
+
let existing = fs18.existsSync(file) ? fs18.readFileSync(file, "utf8") : "";
|
|
5166
5598
|
const re = new RegExp(`${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}`);
|
|
5167
5599
|
if (re.test(existing)) existing = existing.replace(re, block);
|
|
5168
5600
|
else existing = existing.length ? `${existing.replace(/\s*$/, "")}
|
|
@@ -5170,20 +5602,20 @@ function upsertBlock(file, block) {
|
|
|
5170
5602
|
${block}
|
|
5171
5603
|
` : `${block}
|
|
5172
5604
|
`;
|
|
5173
|
-
|
|
5605
|
+
fs18.writeFileSync(file, existing);
|
|
5174
5606
|
}
|
|
5175
5607
|
function removeBlock(file) {
|
|
5176
|
-
if (!
|
|
5177
|
-
const existing =
|
|
5608
|
+
if (!fs18.existsSync(file)) return false;
|
|
5609
|
+
const existing = fs18.readFileSync(file, "utf8");
|
|
5178
5610
|
const re = new RegExp(`\\n*${escapeRe(NUDGE_BEGIN)}[\\s\\S]*?${escapeRe(NUDGE_END)}\\n*`);
|
|
5179
5611
|
if (!re.test(existing)) return false;
|
|
5180
|
-
|
|
5612
|
+
fs18.writeFileSync(file, existing.replace(re, "\n"));
|
|
5181
5613
|
return true;
|
|
5182
5614
|
}
|
|
5183
5615
|
function readJson(file) {
|
|
5184
|
-
if (!
|
|
5616
|
+
if (!fs18.existsSync(file)) return {};
|
|
5185
5617
|
try {
|
|
5186
|
-
const parsed = JSON.parse(
|
|
5618
|
+
const parsed = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
5187
5619
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5188
5620
|
return parsed;
|
|
5189
5621
|
}
|
|
@@ -5203,7 +5635,7 @@ function escapeRe(s) {
|
|
|
5203
5635
|
}
|
|
5204
5636
|
var REFRESH_LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
5205
5637
|
function refreshLockPath(root) {
|
|
5206
|
-
return
|
|
5638
|
+
return path19.join(cacheDir(root), "refresh.lock");
|
|
5207
5639
|
}
|
|
5208
5640
|
async function refreshIfStale(root, opts = {}) {
|
|
5209
5641
|
const probe = probeFreshness(root);
|
|
@@ -5235,8 +5667,8 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5235
5667
|
writeArtifacts(result.graph, {
|
|
5236
5668
|
root,
|
|
5237
5669
|
graphPath: opts.graphPath,
|
|
5238
|
-
report:
|
|
5239
|
-
html:
|
|
5670
|
+
report: fs18.existsSync(path19.join(dir, "GRAPH_REPORT.md")),
|
|
5671
|
+
html: fs18.existsSync(path19.join(dir, "graph.html"))
|
|
5240
5672
|
});
|
|
5241
5673
|
}
|
|
5242
5674
|
writeSnapshot(root, result.graph.provenance.corpusHash, result.fileStats, scope);
|
|
@@ -5256,6 +5688,6 @@ async function refreshIfStale(root, opts = {}) {
|
|
|
5256
5688
|
}
|
|
5257
5689
|
}
|
|
5258
5690
|
|
|
5259
|
-
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, 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 };
|
|
5260
|
-
//# sourceMappingURL=chunk-
|
|
5261
|
-
//# sourceMappingURL=chunk-
|
|
5691
|
+
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 };
|
|
5692
|
+
//# sourceMappingURL=chunk-YKVH2PGF.js.map
|
|
5693
|
+
//# sourceMappingURL=chunk-YKVH2PGF.js.map
|