@vibgrate/cli 2026.801.1 → 2026.803.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-JTA4YWMQ.js +24 -0
- package/dist/{agent-4DAECASW.js.map → agent-JTA4YWMQ.js.map} +1 -1
- package/dist/baseline-2V7PKA7Q.js +9 -0
- package/dist/{baseline-4BDH4F2W.js.map → baseline-2V7PKA7Q.js.map} +1 -1
- package/dist/{chunk-76TDXGGF.js → chunk-3Q3ZXJZP.js} +218 -51
- package/dist/chunk-3Q3ZXJZP.js.map +1 -0
- package/dist/{chunk-G7W4MFPZ.js → chunk-4ACXDVON.js} +4 -4
- package/dist/{chunk-G7W4MFPZ.js.map → chunk-4ACXDVON.js.map} +1 -1
- package/dist/{chunk-E6H2F2XF.js → chunk-6DFV4WCQ.js} +3 -3
- package/dist/{chunk-E6H2F2XF.js.map → chunk-6DFV4WCQ.js.map} +1 -1
- package/dist/{chunk-FF5ZQ6KI.js → chunk-6JP7HGIF.js} +4 -4
- package/dist/{chunk-FF5ZQ6KI.js.map → chunk-6JP7HGIF.js.map} +1 -1
- package/dist/chunk-7KNFG44U.js +6 -0
- package/dist/{chunk-TGDHOAXX.js.map → chunk-7KNFG44U.js.map} +1 -1
- package/dist/{chunk-T6DRYOMT.js → chunk-CPLG4GFB.js} +4 -4
- package/dist/{chunk-T6DRYOMT.js.map → chunk-CPLG4GFB.js.map} +1 -1
- package/dist/{chunk-SDG242R4.js → chunk-HC44772H.js} +132 -365
- package/dist/chunk-HC44772H.js.map +1 -0
- package/dist/{chunk-MPP4S4OE.js → chunk-K6Q6HTFD.js} +32 -11
- package/dist/chunk-K6Q6HTFD.js.map +1 -0
- package/dist/{chunk-7G5S5KZN.js → chunk-KUZLILN3.js} +16 -3
- package/dist/chunk-KUZLILN3.js.map +1 -0
- package/dist/{chunk-XEJJGDGX.js → chunk-KYX2YCXT.js} +134 -9
- package/dist/chunk-KYX2YCXT.js.map +1 -0
- package/dist/{chunk-V2QK3IUY.js → chunk-L5PUNO3T.js} +4 -4
- package/dist/{chunk-V2QK3IUY.js.map → chunk-L5PUNO3T.js.map} +1 -1
- package/dist/{chunk-7PVCHLR2.js → chunk-M452MRWI.js} +109 -10
- package/dist/chunk-M452MRWI.js.map +1 -0
- package/dist/{chunk-WIDZ5IE7.js → chunk-M7773OUN.js} +14 -7
- package/dist/chunk-M7773OUN.js.map +1 -0
- package/dist/{chunk-XRN7DQGR.js → chunk-NJFQCDGA.js} +3 -3
- package/dist/{chunk-XRN7DQGR.js.map → chunk-NJFQCDGA.js.map} +1 -1
- package/dist/{chunk-BNEHDLVZ.js → chunk-NSWEJPLV.js} +129 -20
- package/dist/chunk-NSWEJPLV.js.map +1 -0
- package/dist/{chunk-JLWI2JGA.js → chunk-NUZM7UOR.js} +117 -6
- package/dist/chunk-NUZM7UOR.js.map +1 -0
- package/dist/{chunk-B3OMGYZZ.js → chunk-PXM3VZYR.js} +4 -4
- package/dist/{chunk-B3OMGYZZ.js.map → chunk-PXM3VZYR.js.map} +1 -1
- package/dist/chunk-QMME6TB4.js +457 -0
- package/dist/chunk-QMME6TB4.js.map +1 -0
- package/dist/{chunk-G25STLE7.js → chunk-SJFBXCII.js} +30 -3
- package/dist/chunk-SJFBXCII.js.map +1 -0
- package/dist/{chunk-A43I2AJ4.js → chunk-TM5XQY3L.js} +3 -3
- package/dist/{chunk-A43I2AJ4.js.map → chunk-TM5XQY3L.js.map} +1 -1
- package/dist/{chunk-KL65SSR7.js → chunk-TNADGCOX.js} +323 -152
- package/dist/chunk-TNADGCOX.js.map +1 -0
- package/dist/{chunk-V3X3DBZL.js → chunk-UXR7FBE3.js} +6 -6
- package/dist/{chunk-V3X3DBZL.js.map → chunk-UXR7FBE3.js.map} +1 -1
- package/dist/{chunk-IPICO2RV.js → chunk-VIWZ4QT7.js} +14 -3
- package/dist/chunk-VIWZ4QT7.js.map +1 -0
- package/dist/{chunk-IRCYOTRG.js → chunk-WGPS3NZQ.js} +240 -13
- package/dist/chunk-WGPS3NZQ.js.map +1 -0
- package/dist/{chunk-I6RBAIS3.js → chunk-XK5LKOBF.js} +4 -4
- package/dist/{chunk-I6RBAIS3.js.map → chunk-XK5LKOBF.js.map} +1 -1
- package/dist/cli.js +796 -359
- package/dist/cli.js.map +1 -1
- package/dist/ensure-map-TAVYMPEG.js +29 -0
- package/dist/{ensure-map-SMWHKOUY.js.map → ensure-map-TAVYMPEG.js.map} +1 -1
- package/dist/{graph-backend-I4I72J4W.js → graph-backend-WF4LGBGD.js} +5 -5
- package/dist/{graph-backend-I4I72J4W.js.map → graph-backend-WF4LGBGD.js.map} +1 -1
- package/dist/index.d.ts +143 -7
- package/dist/index.js +17 -16
- package/dist/index.js.map +1 -1
- package/dist/{interactive-TMQR6TXM.js → interactive-6PN237LX.js} +49 -32
- package/dist/interactive-6PN237LX.js.map +1 -0
- package/dist/load-T2FOJUPI.js +10 -0
- package/dist/{load-E3OTM7BS.js.map → load-T2FOJUPI.js.map} +1 -1
- package/dist/model-manager-GJ2YSTSK.js +1 -1
- package/dist/{run-outcome-QWLNBASF.js → run-outcome-AMIKAASL.js} +6 -6
- package/dist/{run-outcome-QWLNBASF.js.map → run-outcome-AMIKAASL.js.map} +1 -1
- package/dist/runtime-session-QAVOPSSH.js +20 -0
- package/dist/{runtime-session-UQ7UVAPL.js.map → runtime-session-QAVOPSSH.js.map} +1 -1
- package/dist/session-SCHQQTHN.js +15 -0
- package/dist/{session-MG6BJVAG.js.map → session-SCHQQTHN.js.map} +1 -1
- package/dist/session-store-F7NX7SZN.js +4 -0
- package/dist/{session-store-SQECEXUU.js.map → session-store-F7NX7SZN.js.map} +1 -1
- package/dist/{stream-json-GM44SP7L.js → stream-json-A6K2MONY.js} +48 -16
- package/dist/stream-json-A6K2MONY.js.map +1 -0
- package/dist/{vg-mcp-bridge-3WFC7LGE.js → vg-mcp-bridge-3TSFOL4V.js} +13 -11
- package/dist/vg-mcp-bridge-3TSFOL4V.js.map +1 -0
- package/dist/{vgd-ZUSBEVUL.js → vgd-B67JCNJP.js} +9 -9
- package/dist/vgd-B67JCNJP.js.map +1 -0
- package/package.json +1 -1
- package/dist/agent-4DAECASW.js +0 -22
- package/dist/baseline-4BDH4F2W.js +0 -9
- package/dist/chunk-76TDXGGF.js.map +0 -1
- package/dist/chunk-7G5S5KZN.js.map +0 -1
- package/dist/chunk-7PVCHLR2.js.map +0 -1
- package/dist/chunk-BNEHDLVZ.js.map +0 -1
- package/dist/chunk-G25STLE7.js.map +0 -1
- package/dist/chunk-IPICO2RV.js.map +0 -1
- package/dist/chunk-IRCYOTRG.js.map +0 -1
- package/dist/chunk-JLWI2JGA.js.map +0 -1
- package/dist/chunk-KL65SSR7.js.map +0 -1
- package/dist/chunk-MPP4S4OE.js.map +0 -1
- package/dist/chunk-SDG242R4.js.map +0 -1
- package/dist/chunk-TGDHOAXX.js +0 -6
- package/dist/chunk-WIDZ5IE7.js.map +0 -1
- package/dist/chunk-XEJJGDGX.js.map +0 -1
- package/dist/chunk-ZKNB4YH4.js +0 -102
- package/dist/chunk-ZKNB4YH4.js.map +0 -1
- package/dist/ensure-map-SMWHKOUY.js +0 -28
- package/dist/interactive-TMQR6TXM.js.map +0 -1
- package/dist/load-E3OTM7BS.js +0 -10
- package/dist/runtime-session-UQ7UVAPL.js +0 -20
- package/dist/session-MG6BJVAG.js +0 -13
- package/dist/session-store-SQECEXUU.js +0 -4
- package/dist/stream-json-GM44SP7L.js.map +0 -1
- package/dist/vg-mcp-bridge-3WFC7LGE.js.map +0 -1
- package/dist/vgd-ZUSBEVUL.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loadAdvancedScanHook, baselineCommand } from './chunk-
|
|
3
|
-
import { fetchCatalog, resolveProviders } from './chunk-
|
|
4
|
-
import { VGD_PROTOCOL_VERSION, startVgdServer } from './chunk-
|
|
5
|
-
import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-
|
|
2
|
+
import { loadAdvancedScanHook, baselineCommand } from './chunk-6JP7HGIF.js';
|
|
3
|
+
import { fetchCatalog, resolveProviders } from './chunk-NUZM7UOR.js';
|
|
4
|
+
import { VGD_PROTOCOL_VERSION, startVgdServer } from './chunk-L5PUNO3T.js';
|
|
5
|
+
import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-PXM3VZYR.js';
|
|
6
|
+
import './chunk-7AVYKZEM.js';
|
|
6
7
|
import { buildModelInstallPlan } from './chunk-WHJMS7XC.js';
|
|
7
|
-
import { runCodeSession } from './chunk-
|
|
8
|
-
import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, readGlobal, applyGlobalOptions, ProgressBar, printLogo, runBuild } from './chunk-
|
|
9
|
-
export { runBuild } from './chunk-
|
|
8
|
+
import { runCodeSession } from './chunk-M7773OUN.js';
|
|
9
|
+
import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, readGlobal, applyGlobalOptions, ProgressBar, printLogo, runBuild } from './chunk-KYX2YCXT.js';
|
|
10
|
+
export { runBuild } from './chunk-KYX2YCXT.js';
|
|
10
11
|
import { disableColor, info, c, json, out } from './chunk-CT5BS56U.js';
|
|
11
|
-
import './chunk-5I4VSBWQ.js';
|
|
12
12
|
import './chunk-IS7VTH2Q.js';
|
|
13
13
|
import { init_resolve_ollama, gatherSystemMemory, resolveOllamaBinary, hasOllamaBinary, resolve_ollama_exports } from './chunk-OWSFZQOP.js';
|
|
14
14
|
import { readOrchestratorState, writeOrchestratorState, parseMode, setDefaultMode, recommendMode, activePin, resolveMode, providerForBackend, QUALIFIED_PACKS, pinPack, unpinPack, buildOrchestratorStatus } from './chunk-NZELMYT4.js';
|
|
@@ -16,54 +16,55 @@ import { fmt, estimateModelBytes } from './chunk-7N4Y3E47.js';
|
|
|
16
16
|
import './chunk-IZDH67DX.js';
|
|
17
17
|
import { defaultProcessEndpoint, startProcessHostServer, resolveInferenceIsolation } from './chunk-JFE7XCAE.js';
|
|
18
18
|
import { clearHostSessionPool, acquireHostSession, generateOnSession, listHostSessions, hostSessionPoolSize } from './chunk-3LOWJSAF.js';
|
|
19
|
-
import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-
|
|
19
|
+
import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-WGPS3NZQ.js';
|
|
20
20
|
import './chunk-543E6WDJ.js';
|
|
21
|
-
import { summarizeDiffs, ScriptedProvider } from './chunk-
|
|
21
|
+
import { summarizeDiffs, ScriptedProvider } from './chunk-SJFBXCII.js';
|
|
22
22
|
import { ensurePackage, ensureUnavailableMessage } from './chunk-REIGWTTH.js';
|
|
23
|
-
import './chunk-
|
|
23
|
+
import './chunk-TM5XQY3L.js';
|
|
24
24
|
import { vgdSocketPath, vgdIsRunning, vgdRequest, vgdPidPath } from './chunk-RBIRXQR6.js';
|
|
25
25
|
import { buildIdentifierTrieFromGraph } from './chunk-5HYCB55P.js';
|
|
26
|
-
import './chunk-
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, perDependencyDrift } from './chunk-XRN7DQGR.js';
|
|
26
|
+
import { serveStdio, isRelevantChange, exportGraph, GraphSource, createServer, formatForExt } from './chunk-NSWEJPLV.js';
|
|
27
|
+
import { refreshIfStale } from './chunk-UXR7FBE3.js';
|
|
28
|
+
import { CliError, ExitCode, driftCount, ASSISTANTS, detectAssistants, isAssistantInstalled, usageError, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, buildGraph, writeSnapshot, probeFreshness, discover, assistantById, SMALL_REPO_FILES, isSkippedDirName, SKIP_FILES, SKIP_DIRS } from './chunk-TNADGCOX.js';
|
|
29
|
+
import { resolveDsn, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, parseDsn as parseDsn$1, uploadScanArtifact, dsnCommand, pushCommand, countTokens, shortestPath, pathDisconnect, testsToRun, detectRunner, coveringTests, FREE_PACK, readScanArtifact, publishPrivateLibrary, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, hostedBase, readStoredCredentials, projectCredentialsPath, homeCredentialsPath } from './chunk-M452MRWI.js';
|
|
30
|
+
import { writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, perDependencyDrift } from './chunk-NJFQCDGA.js';
|
|
32
31
|
import { resolveIngestHost, dashHostForIngestHost, availableRegionIds, ingestHostForRegionId } from './chunk-HTLDXEHW.js';
|
|
32
|
+
import { discoverModels } from './chunk-CQDUBAQY.js';
|
|
33
|
+
import { freeBytesOnVolume, weightStoreDir, listCachedWeights, ensureWeightCached, catalogIntegrityReport } from './chunk-OV4KGXPD.js';
|
|
34
|
+
import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-DT4KIVRI.js';
|
|
33
35
|
import { ensureDir, pathExists, readJsonFile, writeTextFile, writeJsonFile } from './chunk-CRAMOEBE.js';
|
|
34
|
-
import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-
|
|
35
|
-
import './chunk-
|
|
36
|
-
import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-
|
|
36
|
+
import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-K6Q6HTFD.js';
|
|
37
|
+
import './chunk-VIWZ4QT7.js';
|
|
38
|
+
import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-XK5LKOBF.js';
|
|
37
39
|
import { init_cli_invocation, resolveCliInvocation, NPX_INVOCATION } from './chunk-3LSG7Z4I.js';
|
|
38
40
|
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
39
|
-
import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, countPending, indexFor, impactOf, modelCacheInfo, clearModelCache } from './chunk-
|
|
40
|
-
import { discoverModels } from './chunk-CQDUBAQY.js';
|
|
41
|
-
import { freeBytesOnVolume, weightStoreDir, listCachedWeights, ensureWeightCached, catalogIntegrityReport } from './chunk-OV4KGXPD.js';
|
|
41
|
+
import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, countPending, indexFor, impactOf, modelCacheInfo, clearModelCache, withTimeout } from './chunk-HC44772H.js';
|
|
42
42
|
import { resolvedGrammarFiles, grammarSetVersion } from './chunk-UTNNGKKX.js';
|
|
43
43
|
import './chunk-RXP66R2E.js';
|
|
44
|
-
import { loadGraph, SCHEMA_VERSION } from './chunk-
|
|
45
|
-
import { resolveGraphPath, renderReport, loadGraphFileWithSnapshot, writeArtifacts, vibgrateDir, mapFileExists, ensureVibgrateGitignore } from './chunk-
|
|
46
|
-
import { VERSION } from './chunk-
|
|
47
|
-
import { cacheDir, serializeGraph, stableStringify } from './chunk-
|
|
44
|
+
import { loadGraph, loadGraphPreferIndex, SCHEMA_VERSION } from './chunk-CPLG4GFB.js';
|
|
45
|
+
import { resolveGraphPath, analyzeQuestion, loadTopicTags, renderReport, resetRelevanceProviderCache, loadRelevanceProvider, loadGraphFileWithSnapshot, writeArtifacts, vibgrateDir, mapFileExists, relevanceModuleDir, ensureVibgrateGitignore } from './chunk-3Q3ZXJZP.js';
|
|
46
|
+
import { VERSION } from './chunk-7KNFG44U.js';
|
|
47
|
+
import { cacheDir, serializeGraph, stableStringify, parseGraph } from './chunk-QMME6TB4.js';
|
|
48
|
+
import './chunk-5I4VSBWQ.js';
|
|
48
49
|
import './chunk-BWPBB45M.js';
|
|
49
50
|
import { canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
|
|
50
51
|
import './chunk-BPF3YX2U.js';
|
|
51
|
-
import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-DT4KIVRI.js';
|
|
52
52
|
import { __toCommonJS } from './chunk-Z6ZWNWWR.js';
|
|
53
|
-
import * as
|
|
54
|
-
import
|
|
55
|
-
import * as
|
|
56
|
-
import
|
|
53
|
+
import * as fs10 from 'fs';
|
|
54
|
+
import fs10__default, { realpathSync } from 'fs';
|
|
55
|
+
import * as path39 from 'path';
|
|
56
|
+
import path39__default from 'path';
|
|
57
57
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
58
58
|
import { Command, CommanderError, Option } from 'commander';
|
|
59
59
|
import * as semver3 from 'semver';
|
|
60
60
|
import semver3__default from 'semver';
|
|
61
|
-
import { execSync, spawn, spawnSync, execFileSync } from 'child_process';
|
|
62
61
|
import * as readline from 'readline';
|
|
62
|
+
import * as zlib from 'zlib';
|
|
63
63
|
import * as crypto3 from 'crypto';
|
|
64
|
-
import { randomUUID } from 'crypto';
|
|
64
|
+
import { randomUUID, createHash } from 'crypto';
|
|
65
|
+
import { execSync, spawn, spawnSync, execFileSync } from 'child_process';
|
|
65
66
|
import chalk7 from 'chalk';
|
|
66
|
-
import * as
|
|
67
|
+
import * as fs24 from 'fs/promises';
|
|
67
68
|
import * as os from 'os';
|
|
68
69
|
|
|
69
70
|
init_cli_invocation();
|
|
@@ -74,10 +75,10 @@ function registerStatus(program) {
|
|
|
74
75
|
applyGlobalOptions(cmd);
|
|
75
76
|
}
|
|
76
77
|
async function runStatus(global) {
|
|
77
|
-
const root2 =
|
|
78
|
+
const root2 = path39.resolve(global.cwd ?? ".");
|
|
78
79
|
const graphPath = resolveGraphPath(root2, global.graph);
|
|
79
80
|
const graph = loadGraph(root2, graphPath);
|
|
80
|
-
const hasCache =
|
|
81
|
+
const hasCache = fs10.existsSync(path39.join(cacheDir(root2), "parse-cache.json"));
|
|
81
82
|
let stale = null;
|
|
82
83
|
let staleExact = false;
|
|
83
84
|
if (graph) {
|
|
@@ -97,8 +98,8 @@ async function runStatus(global) {
|
|
|
97
98
|
}
|
|
98
99
|
if (global.json) {
|
|
99
100
|
json({
|
|
100
|
-
root:
|
|
101
|
-
graphPath:
|
|
101
|
+
root: path39.relative(process.cwd(), root2) || ".",
|
|
102
|
+
graphPath: path39.relative(root2, graphPath),
|
|
102
103
|
built: graph !== null,
|
|
103
104
|
generatedAt: graph?.generatedAt ?? null,
|
|
104
105
|
counts: graph?.meta.counts ?? null,
|
|
@@ -113,11 +114,11 @@ async function runStatus(global) {
|
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
115
116
|
if (!graph) {
|
|
116
|
-
info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(
|
|
117
|
+
info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(path39.relative(root2, graphPath))}`);
|
|
117
118
|
info(` run ${c.bold("vg")} to build one`);
|
|
118
119
|
return;
|
|
119
120
|
}
|
|
120
|
-
info(`${c.cyan("vg")} \xB7 ${
|
|
121
|
+
info(`${c.cyan("vg")} \xB7 ${path39.relative(root2, graphPath)}`);
|
|
121
122
|
info(` generated ${graph.generatedAt}`);
|
|
122
123
|
info(
|
|
123
124
|
` nodes ${c.bold(String(graph.meta.counts.nodes))} edges ${c.bold(
|
|
@@ -134,7 +135,7 @@ async function runStatus(global) {
|
|
|
134
135
|
if (vibgrateDir(root2)) ;
|
|
135
136
|
}
|
|
136
137
|
function rootOf(global) {
|
|
137
|
-
return
|
|
138
|
+
return path39.resolve(global.cwd ?? ".");
|
|
138
139
|
}
|
|
139
140
|
function requireGraph(global) {
|
|
140
141
|
const root2 = rootOf(global);
|
|
@@ -170,6 +171,8 @@ function registerAsk(program) {
|
|
|
170
171
|
let result;
|
|
171
172
|
let mode = "lexical";
|
|
172
173
|
let note = "";
|
|
174
|
+
const relevance = await analyzeQuestion(q);
|
|
175
|
+
const topicTags = relevance ? await loadTopicTags(graph, root2, resolveGraphPath(root2, global.graph)) : null;
|
|
173
176
|
if (wantSemantic) {
|
|
174
177
|
const firstRun = !embeddingsCached(root2, resolveEmbedModel());
|
|
175
178
|
if (!global.json && firstRun) {
|
|
@@ -181,14 +184,14 @@ function registerAsk(program) {
|
|
|
181
184
|
const bar = !global.json && firstRun ? new ProgressBar(c.dim("embedding")) : void 0;
|
|
182
185
|
const vectors = await getNodeEmbeddings(graph, embedder, root2, bar ? (d, t) => bar.update(d, t) : void 0);
|
|
183
186
|
bar?.done();
|
|
184
|
-
result = await queryGraphSemantic(graph, q, { budget, embedder, nodeVectors: vectors });
|
|
187
|
+
result = await queryGraphSemantic(graph, q, { budget, embedder, nodeVectors: vectors, relevance, topicTags });
|
|
185
188
|
mode = `semantic (${embedder.id})`;
|
|
186
189
|
} else {
|
|
187
|
-
result = queryGraph(graph, q, { budget });
|
|
190
|
+
result = queryGraph(graph, q, { budget, relevance, topicTags });
|
|
188
191
|
note = reason ? unavailableMessage(reason) : "semantic unavailable; used lexical";
|
|
189
192
|
}
|
|
190
193
|
} else {
|
|
191
|
-
result = queryGraph(graph, q, { budget });
|
|
194
|
+
result = queryGraph(graph, q, { budget, relevance, topicTags });
|
|
192
195
|
if (global.local) note = "semantic skipped under --local; used lexical";
|
|
193
196
|
}
|
|
194
197
|
if (global.client) {
|
|
@@ -868,12 +871,12 @@ var PUBLISH_INTERVAL_MS = 2e3;
|
|
|
868
871
|
var KEEPALIVE_MS = 1e4;
|
|
869
872
|
var STALE_MS = 6e4;
|
|
870
873
|
function liveStatsDir(root2) {
|
|
871
|
-
return
|
|
874
|
+
return path39.join(cacheDir(root2), "serve-live");
|
|
872
875
|
}
|
|
873
876
|
function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid, signal) => process.kill(pid, signal), selfPid = process.pid) {
|
|
874
877
|
let entries;
|
|
875
878
|
try {
|
|
876
|
-
entries =
|
|
879
|
+
entries = fs10.readdirSync(dir);
|
|
877
880
|
} catch {
|
|
878
881
|
return [];
|
|
879
882
|
}
|
|
@@ -882,7 +885,7 @@ function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid
|
|
|
882
885
|
if (!/^serve-\d+\.json$/.test(name)) continue;
|
|
883
886
|
let parsed;
|
|
884
887
|
try {
|
|
885
|
-
parsed = JSON.parse(
|
|
888
|
+
parsed = JSON.parse(fs10.readFileSync(path39.join(dir, name), "utf8"));
|
|
886
889
|
} catch {
|
|
887
890
|
continue;
|
|
888
891
|
}
|
|
@@ -904,7 +907,7 @@ var LiveStatsBus = class {
|
|
|
904
907
|
this.dir = dir;
|
|
905
908
|
this.stats = stats;
|
|
906
909
|
this.pid = pid;
|
|
907
|
-
this.file =
|
|
910
|
+
this.file = path39.join(dir, `serve-${this.pid}.json`);
|
|
908
911
|
}
|
|
909
912
|
dir;
|
|
910
913
|
stats;
|
|
@@ -936,10 +939,10 @@ var LiveStatsBus = class {
|
|
|
936
939
|
this.lastWriteAt = now;
|
|
937
940
|
const body = { pid: this.pid, updatedAt: now, version: VERSION, snapshot: snap };
|
|
938
941
|
try {
|
|
939
|
-
|
|
942
|
+
fs10.mkdirSync(this.dir, { recursive: true });
|
|
940
943
|
const tmp = `${this.file}.tmp`;
|
|
941
|
-
|
|
942
|
-
|
|
944
|
+
fs10.writeFileSync(tmp, JSON.stringify(body));
|
|
945
|
+
fs10.renameSync(tmp, this.file);
|
|
943
946
|
} catch {
|
|
944
947
|
}
|
|
945
948
|
}
|
|
@@ -950,24 +953,24 @@ var LiveStatsBus = class {
|
|
|
950
953
|
siblings(now = Date.now()) {
|
|
951
954
|
let entries;
|
|
952
955
|
try {
|
|
953
|
-
entries =
|
|
956
|
+
entries = fs10.readdirSync(this.dir);
|
|
954
957
|
} catch {
|
|
955
958
|
return [];
|
|
956
959
|
}
|
|
957
960
|
const out2 = [];
|
|
958
961
|
for (const name of entries.sort()) {
|
|
959
962
|
if (!/^serve-\d+\.json$/.test(name)) continue;
|
|
960
|
-
const file =
|
|
963
|
+
const file = path39.join(this.dir, name);
|
|
961
964
|
let parsed;
|
|
962
965
|
try {
|
|
963
|
-
parsed = JSON.parse(
|
|
966
|
+
parsed = JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
964
967
|
} catch {
|
|
965
968
|
continue;
|
|
966
969
|
}
|
|
967
970
|
if (typeof parsed?.pid !== "number" || typeof parsed.updatedAt !== "number" || !parsed.snapshot?.totals) continue;
|
|
968
971
|
if (now - parsed.updatedAt > STALE_MS) {
|
|
969
972
|
try {
|
|
970
|
-
|
|
973
|
+
fs10.unlinkSync(file);
|
|
971
974
|
} catch {
|
|
972
975
|
}
|
|
973
976
|
continue;
|
|
@@ -979,7 +982,7 @@ var LiveStatsBus = class {
|
|
|
979
982
|
}
|
|
980
983
|
unlinkOwn = () => {
|
|
981
984
|
try {
|
|
982
|
-
|
|
985
|
+
fs10.unlinkSync(this.file);
|
|
983
986
|
} catch {
|
|
984
987
|
}
|
|
985
988
|
};
|
|
@@ -1292,12 +1295,12 @@ var LedgerTail = class {
|
|
|
1292
1295
|
if (size === this.offset) return;
|
|
1293
1296
|
let chunk;
|
|
1294
1297
|
try {
|
|
1295
|
-
const fd =
|
|
1298
|
+
const fd = fs10.openSync(this.file, "r");
|
|
1296
1299
|
try {
|
|
1297
1300
|
chunk = Buffer.alloc(size - this.offset);
|
|
1298
|
-
|
|
1301
|
+
fs10.readSync(fd, chunk, 0, chunk.length, this.offset);
|
|
1299
1302
|
} finally {
|
|
1300
|
-
|
|
1303
|
+
fs10.closeSync(fd);
|
|
1301
1304
|
}
|
|
1302
1305
|
} catch {
|
|
1303
1306
|
return;
|
|
@@ -1310,7 +1313,7 @@ var LedgerTail = class {
|
|
|
1310
1313
|
}
|
|
1311
1314
|
sizeOf() {
|
|
1312
1315
|
try {
|
|
1313
|
-
return
|
|
1316
|
+
return fs10.statSync(this.file).size;
|
|
1314
1317
|
} catch {
|
|
1315
1318
|
return 0;
|
|
1316
1319
|
}
|
|
@@ -1342,7 +1345,7 @@ function ingestLedgerLine(stats, line, now) {
|
|
|
1342
1345
|
// src/commands/serve.ts
|
|
1343
1346
|
var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
1344
1347
|
function registerServe(program) {
|
|
1345
|
-
const cmd = program.command("serve").description("start Vibgrate AI Context \u2014 local-first MCP serving your code map, drift & version-correct docs to your AI").option("--http", "serve over streamable HTTP instead of stdio").option("--port <n>", "port for --http", "7437").option("--host <h>", "host for --http", "127.0.0.1").option("--savings", "record local, counts-only usage savings (opt-in; off by default)").option("--share-stats", "ALSO upload the counts-only usage ledger to Vibgrate to improve the local MCP (opt-in; off by default; implies --savings; disabled under --local)").option("--dedup", "collapse a node's heavy relation lists on repeat reads within a session (opt-in; saves tokens)").option("--no-refresh", "serve the map as built \u2014 skip the auto-rebuild when files change").action(async function(opts) {
|
|
1348
|
+
const cmd = program.command("serve").description("start Vibgrate AI Context \u2014 local-first MCP serving your code map, drift & version-correct docs to your AI").option("--http", "serve over streamable HTTP instead of stdio").option("--port <n>", "port for --http", "7437").option("--host <h>", "host for --http", "127.0.0.1").option("--savings", "record local, counts-only usage savings (opt-in; off by default)").option("--share-stats", "ALSO upload the counts-only usage ledger to Vibgrate to improve the local MCP (opt-in; off by default; implies --savings; disabled under --local)").option("--dedup", "collapse a node's heavy relation lists on repeat reads within a session (opt-in; saves tokens)").option("--no-refresh", "serve the map as built \u2014 skip the auto-rebuild when files change").option("--no-watch", "disable the event-driven file watcher \u2014 freshness falls back to the periodic poll").option("--surface <mode>", 'tool listing surface: "hot" lists only the navigation core (orient/search_symbols/query_graph/get_node); "full" lists all tools. Every tool stays callable either way. Env: VG_MCP_SURFACE').option("--tools <names>", "comma-separated tool names to list (listing only \u2014 all tools remain callable). Env: VG_MCP_TOOLS").action(async function(opts) {
|
|
1346
1349
|
const global = readGlobal(this);
|
|
1347
1350
|
const root2 = rootOf(global);
|
|
1348
1351
|
const graphPath = resolveGraphPath(root2, global.graph);
|
|
@@ -1365,8 +1368,15 @@ function registerServe(program) {
|
|
|
1365
1368
|
local,
|
|
1366
1369
|
dedup: opts.dedup === true,
|
|
1367
1370
|
refresh,
|
|
1371
|
+
watch: opts.watch !== false,
|
|
1368
1372
|
root: root2,
|
|
1369
|
-
stats
|
|
1373
|
+
stats,
|
|
1374
|
+
// Listing surface: flags win over env; unknown values fall back to the
|
|
1375
|
+
// full surface inside listedToolNames (fail-open).
|
|
1376
|
+
toolSurface: {
|
|
1377
|
+
surface: (opts.surface ?? process.env.VG_MCP_SURFACE) === "hot" ? "hot" : "full",
|
|
1378
|
+
tools: (opts.tools ?? process.env.VG_MCP_TOOLS)?.split(",").map((s) => s.trim()).filter(Boolean)
|
|
1379
|
+
}
|
|
1370
1380
|
};
|
|
1371
1381
|
await ensureServableGraph(root2, graphPath, global, refresh);
|
|
1372
1382
|
if (opts.shareStats === true && local) {
|
|
@@ -1455,6 +1465,7 @@ async function serveHttp(graphPath, host, port, opts, freshness, onReady) {
|
|
|
1455
1465
|
const { createServer: createHttp } = await import('http');
|
|
1456
1466
|
const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
1457
1467
|
const source = new GraphSource(graphPath, opts.refresh !== false, { root: opts.root });
|
|
1468
|
+
if (opts.refresh !== false && opts.watch !== false) source.startWatching();
|
|
1458
1469
|
const httpServer = createHttp(async (req, res) => {
|
|
1459
1470
|
if (req.url !== "/mcp") {
|
|
1460
1471
|
res.writeHead(404).end("not found");
|
|
@@ -1695,19 +1706,19 @@ function endOfLine(text, line) {
|
|
|
1695
1706
|
}
|
|
1696
1707
|
var CACHE_VERSION = "vg-lsp-scan-cache/2";
|
|
1697
1708
|
function cachePath(root2) {
|
|
1698
|
-
return
|
|
1709
|
+
return path39.join(cacheDir(root2), "lsp-scan.json");
|
|
1699
1710
|
}
|
|
1700
1711
|
function manifestHash(root2) {
|
|
1701
1712
|
const files = [];
|
|
1702
1713
|
const walk2 = (dir) => {
|
|
1703
1714
|
let entries;
|
|
1704
1715
|
try {
|
|
1705
|
-
entries =
|
|
1716
|
+
entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
1706
1717
|
} catch {
|
|
1707
1718
|
return;
|
|
1708
1719
|
}
|
|
1709
1720
|
for (const entry of entries) {
|
|
1710
|
-
const abs =
|
|
1721
|
+
const abs = path39.join(dir, entry.name);
|
|
1711
1722
|
if (entry.isDirectory()) {
|
|
1712
1723
|
if (isSkippedDirName(entry.name)) continue;
|
|
1713
1724
|
walk2(abs);
|
|
@@ -1717,8 +1728,8 @@ function manifestHash(root2) {
|
|
|
1717
1728
|
const base = entry.name.toLowerCase();
|
|
1718
1729
|
if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
|
|
1719
1730
|
try {
|
|
1720
|
-
const rel =
|
|
1721
|
-
files.push({ rel, hash: hashBytes(
|
|
1731
|
+
const rel = path39.relative(root2, abs).split(path39.sep).join("/");
|
|
1732
|
+
files.push({ rel, hash: hashBytes(fs10.readFileSync(abs)) });
|
|
1722
1733
|
} catch {
|
|
1723
1734
|
}
|
|
1724
1735
|
}
|
|
@@ -1728,16 +1739,16 @@ function manifestHash(root2) {
|
|
|
1728
1739
|
return hashString(canonicalize(files));
|
|
1729
1740
|
}
|
|
1730
1741
|
function isDependencyFile(root2, filePath) {
|
|
1731
|
-
const rel =
|
|
1732
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
1733
|
-
const parts = rel.split(
|
|
1742
|
+
const rel = path39.relative(path39.resolve(root2), path39.resolve(filePath));
|
|
1743
|
+
if (rel === "" || rel.startsWith("..") || path39.isAbsolute(rel)) return false;
|
|
1744
|
+
const parts = rel.split(path39.sep);
|
|
1734
1745
|
if (parts.slice(0, -1).some((dir) => isSkippedDirName(dir))) return false;
|
|
1735
1746
|
const base = parts[parts.length - 1].toLowerCase();
|
|
1736
1747
|
return isManifest(filePath) || SKIP_FILES.has(base);
|
|
1737
1748
|
}
|
|
1738
1749
|
function loadScanCache(root2, key2) {
|
|
1739
1750
|
try {
|
|
1740
|
-
const raw = JSON.parse(
|
|
1751
|
+
const raw = JSON.parse(fs10.readFileSync(cachePath(root2), "utf8"));
|
|
1741
1752
|
if (raw.version === CACHE_VERSION && raw.manifestHash === key2.manifestHash && raw.toolVersion === key2.toolVersion && raw.offline === key2.offline && raw.artifact) {
|
|
1742
1753
|
return { manifestHash: raw.manifestHash, artifact: raw.artifact };
|
|
1743
1754
|
}
|
|
@@ -1747,8 +1758,8 @@ function loadScanCache(root2, key2) {
|
|
|
1747
1758
|
}
|
|
1748
1759
|
function writeScanCache(root2, key2, artifact) {
|
|
1749
1760
|
try {
|
|
1750
|
-
|
|
1751
|
-
|
|
1761
|
+
fs10.mkdirSync(cacheDir(root2), { recursive: true });
|
|
1762
|
+
fs10.writeFileSync(
|
|
1752
1763
|
cachePath(root2),
|
|
1753
1764
|
stableStringify(
|
|
1754
1765
|
{ version: CACHE_VERSION, manifestHash: key2.manifestHash, toolVersion: key2.toolVersion, offline: key2.offline, artifact },
|
|
@@ -1761,7 +1772,7 @@ function writeScanCache(root2, key2, artifact) {
|
|
|
1761
1772
|
var MAX_ENTRIES = 1e3;
|
|
1762
1773
|
var KEEP_ENTRIES = 500;
|
|
1763
1774
|
function historyPath(root2) {
|
|
1764
|
-
return
|
|
1775
|
+
return path39.join(root2, ".vibgrate", "score-history.jsonl");
|
|
1765
1776
|
}
|
|
1766
1777
|
function parseLine(line) {
|
|
1767
1778
|
try {
|
|
@@ -1775,7 +1786,7 @@ function parseLine(line) {
|
|
|
1775
1786
|
}
|
|
1776
1787
|
function readHistory(root2) {
|
|
1777
1788
|
try {
|
|
1778
|
-
return
|
|
1789
|
+
return fs10.readFileSync(historyPath(root2), "utf8").split("\n").filter((l) => l.trim()).map(parseLine).filter((e) => e !== null);
|
|
1779
1790
|
} catch {
|
|
1780
1791
|
return [];
|
|
1781
1792
|
}
|
|
@@ -1802,14 +1813,14 @@ function recordScore(root2, entry) {
|
|
|
1802
1813
|
}
|
|
1803
1814
|
}
|
|
1804
1815
|
const file = historyPath(root2);
|
|
1805
|
-
|
|
1816
|
+
fs10.mkdirSync(path39.dirname(file), { recursive: true });
|
|
1806
1817
|
ensureVibgrateGitignore(root2);
|
|
1807
|
-
|
|
1818
|
+
fs10.appendFileSync(file, `${JSON.stringify(entry)}
|
|
1808
1819
|
`);
|
|
1809
1820
|
const all = readHistory(root2);
|
|
1810
1821
|
if (all.length > MAX_ENTRIES) {
|
|
1811
1822
|
const kept = all.slice(-KEEP_ENTRIES);
|
|
1812
|
-
|
|
1823
|
+
fs10.writeFileSync(file, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
1813
1824
|
}
|
|
1814
1825
|
} catch {
|
|
1815
1826
|
}
|
|
@@ -1821,6 +1832,10 @@ function deltaFrom(prev, current) {
|
|
|
1821
1832
|
}
|
|
1822
1833
|
|
|
1823
1834
|
// src/lsp/graph-query.ts
|
|
1835
|
+
function semanticBudgetMs() {
|
|
1836
|
+
const n = Number(process.env.VG_SEMANTIC_BUDGET_MS);
|
|
1837
|
+
return Number.isFinite(n) && n > 0 ? n : 2e4;
|
|
1838
|
+
}
|
|
1824
1839
|
async function runGraphQuery(graph, params, ctx) {
|
|
1825
1840
|
switch (params.mode) {
|
|
1826
1841
|
case "ask":
|
|
@@ -1846,25 +1861,52 @@ async function runAsk(graph, params, ctx) {
|
|
|
1846
1861
|
if (!question) return { ok: false, mode: "ask", error: "bad-request", message: "question is required" };
|
|
1847
1862
|
const budget = params.budget ?? 2e3;
|
|
1848
1863
|
const wantSemantic = !!params.semantic && !ctx.offline && ctx.semantic !== false;
|
|
1849
|
-
|
|
1864
|
+
const log = ctx.log ?? (() => {
|
|
1865
|
+
});
|
|
1866
|
+
let result = null;
|
|
1850
1867
|
let mode = "lexical";
|
|
1851
1868
|
let note;
|
|
1852
1869
|
if (wantSemantic) {
|
|
1870
|
+
const budgetMs = semanticBudgetMs();
|
|
1871
|
+
log(`ask: trying the semantic path (budget ${Math.round(budgetMs / 1e3)}s)`);
|
|
1853
1872
|
let reason;
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1873
|
+
const started = Date.now();
|
|
1874
|
+
try {
|
|
1875
|
+
result = await withTimeout(
|
|
1876
|
+
(async () => {
|
|
1877
|
+
const embedder = await loadEmbedder({ onUnavailable: (r2) => reason = r2 });
|
|
1878
|
+
if (!embedder) return null;
|
|
1879
|
+
log(`ask: embedder "${embedder.id}" loaded in ${Date.now() - started}ms; embedding nodes\u2026`);
|
|
1880
|
+
const vectors = await getNodeEmbeddings(graph, embedder, ctx.root);
|
|
1881
|
+
log(`ask: ${vectors.size} node vectors ready in ${Date.now() - started}ms; ranking\u2026`);
|
|
1882
|
+
const r = await queryGraphSemantic(graph, question, { budget, embedder, nodeVectors: vectors });
|
|
1883
|
+
mode = `semantic (${embedder.id})`;
|
|
1884
|
+
return r;
|
|
1885
|
+
})(),
|
|
1886
|
+
budgetMs,
|
|
1887
|
+
"semantic search went over its time budget"
|
|
1888
|
+
);
|
|
1889
|
+
if (!result) {
|
|
1890
|
+
note = reason ? unavailableMessage(reason) : "semantic unavailable; used lexical";
|
|
1891
|
+
log(`ask: semantic unavailable (${reason ?? "no backend"}) \u2014 falling back to lexical`);
|
|
1892
|
+
}
|
|
1893
|
+
} catch (err) {
|
|
1894
|
+
mode = "lexical";
|
|
1895
|
+
result = null;
|
|
1896
|
+
note = "semantic search did not finish in time \u2014 answered with fast lexical search. It keeps warming in the background, so the next Ask should be semantic.";
|
|
1897
|
+
log(
|
|
1898
|
+
`ask: semantic path abandoned after ${Date.now() - started}ms (${err instanceof Error ? err.message : String(err)}) \u2014 falling back to lexical`
|
|
1899
|
+
);
|
|
1862
1900
|
}
|
|
1863
|
-
} else {
|
|
1901
|
+
} else if (params.semantic && ctx.semantic === false) {
|
|
1902
|
+
note = "semantic search is turned off \u2014 used lexical";
|
|
1903
|
+
} else if (params.semantic && ctx.offline) {
|
|
1904
|
+
note = "semantic skipped \u2014 Vibgrate is running offline";
|
|
1905
|
+
}
|
|
1906
|
+
if (!result) {
|
|
1864
1907
|
result = queryGraph(graph, question, { budget });
|
|
1865
|
-
if (params.semantic && ctx.semantic === false) note = "semantic search is turned off \u2014 used lexical";
|
|
1866
|
-
else if (params.semantic && ctx.offline) note = "semantic skipped \u2014 Vibgrate is running offline";
|
|
1867
1908
|
}
|
|
1909
|
+
log(`ask: answered with ${mode} \u2014 ${result.matches.length} match(es)`);
|
|
1868
1910
|
return {
|
|
1869
1911
|
ok: true,
|
|
1870
1912
|
mode: "ask",
|
|
@@ -2629,14 +2671,26 @@ var VibgrateLanguageServer = class {
|
|
|
2629
2671
|
}
|
|
2630
2672
|
async onGraphQuery(params) {
|
|
2631
2673
|
const p = params;
|
|
2674
|
+
const trace = (message) => this.conn.notify("window/logMessage", { type: 3, message: `Vibgrate graph: ${message}` });
|
|
2632
2675
|
if (!this.opts.graph) {
|
|
2676
|
+
trace(`${p.mode} refused \u2014 the local Vibgrate Graph is turned off (--no-graph)`);
|
|
2633
2677
|
return { ok: false, mode: p.mode, error: "disabled", message: "the local Vibgrate Graph is turned off" };
|
|
2634
2678
|
}
|
|
2679
|
+
const started = Date.now();
|
|
2635
2680
|
const graph = await this.graphForQuery();
|
|
2636
2681
|
if (!graph) {
|
|
2682
|
+
trace(`${p.mode} has no code map yet \u2014 it is still building`);
|
|
2637
2683
|
return { ok: false, mode: p.mode, error: "not-found", message: "no code map yet \u2014 it is still building" };
|
|
2638
2684
|
}
|
|
2639
|
-
|
|
2685
|
+
trace(
|
|
2686
|
+
`${p.mode} over ${graph.nodes.length} nodes / ${graph.edges.length} edges (map ready in ${Date.now() - started}ms)`
|
|
2687
|
+
);
|
|
2688
|
+
return runGraphQuery(graph, p, {
|
|
2689
|
+
root: this.opts.root,
|
|
2690
|
+
offline: this.opts.offline,
|
|
2691
|
+
semantic: this.opts.semantic,
|
|
2692
|
+
log: trace
|
|
2693
|
+
});
|
|
2640
2694
|
}
|
|
2641
2695
|
/**
|
|
2642
2696
|
* `vibgrate/enrich` — lazy vulnerability enrichment for one expanded package,
|
|
@@ -2682,10 +2736,10 @@ var VibgrateLanguageServer = class {
|
|
|
2682
2736
|
const ecosystem = osvEcosystem(proj.type);
|
|
2683
2737
|
if (!ecosystem) continue;
|
|
2684
2738
|
const relManifest = manifestRelativePath(proj);
|
|
2685
|
-
const absManifest =
|
|
2739
|
+
const absManifest = path39.resolve(this.opts.root, relManifest);
|
|
2686
2740
|
let text = null;
|
|
2687
2741
|
try {
|
|
2688
|
-
if (
|
|
2742
|
+
if (fs10.existsSync(absManifest)) text = fs10.readFileSync(absManifest, "utf8");
|
|
2689
2743
|
} catch {
|
|
2690
2744
|
text = null;
|
|
2691
2745
|
}
|
|
@@ -2815,9 +2869,9 @@ function pruneArtifactProjects(a) {
|
|
|
2815
2869
|
return { ...a, projects, findings };
|
|
2816
2870
|
}
|
|
2817
2871
|
function projectByPath(a, projectPath) {
|
|
2818
|
-
const want =
|
|
2872
|
+
const want = path39.normalize(projectPath === "" ? "." : projectPath);
|
|
2819
2873
|
for (const proj of a.projects ?? []) {
|
|
2820
|
-
const got =
|
|
2874
|
+
const got = path39.normalize(proj.path === "" ? "." : proj.path);
|
|
2821
2875
|
if (got === want) return proj;
|
|
2822
2876
|
}
|
|
2823
2877
|
return void 0;
|
|
@@ -2854,12 +2908,12 @@ function buildProjectRefs(rootDir, projects) {
|
|
|
2854
2908
|
return refs.length > 0 ? refs : void 0;
|
|
2855
2909
|
}
|
|
2856
2910
|
function projectForFile(root2, a, filePath) {
|
|
2857
|
-
const dir =
|
|
2911
|
+
const dir = path39.dirname(path39.resolve(filePath));
|
|
2858
2912
|
let best;
|
|
2859
2913
|
let bestLen = -1;
|
|
2860
2914
|
for (const proj of a.projects ?? []) {
|
|
2861
|
-
const projDir =
|
|
2862
|
-
if ((dir === projDir || dir.startsWith(projDir +
|
|
2915
|
+
const projDir = path39.resolve(root2, proj.path);
|
|
2916
|
+
if ((dir === projDir || dir.startsWith(projDir + path39.sep)) && projDir.length > bestLen) {
|
|
2863
2917
|
best = proj;
|
|
2864
2918
|
bestLen = projDir.length;
|
|
2865
2919
|
}
|
|
@@ -2867,7 +2921,7 @@ function projectForFile(root2, a, filePath) {
|
|
|
2867
2921
|
return best;
|
|
2868
2922
|
}
|
|
2869
2923
|
function sameProject(location, projectPath) {
|
|
2870
|
-
return
|
|
2924
|
+
return path39.normalize(location) === path39.normalize(projectPath);
|
|
2871
2925
|
}
|
|
2872
2926
|
function hasReleaseDates(a) {
|
|
2873
2927
|
for (const proj of a.projects ?? []) {
|
|
@@ -2937,10 +2991,10 @@ function buildBreakdown(rootDir, projects) {
|
|
|
2937
2991
|
const relManifest = manifestRelativePath(p);
|
|
2938
2992
|
if (isPrunedWorkspacePath(p.path) || isPrunedWorkspacePath(relManifest)) continue;
|
|
2939
2993
|
const ecosystem = osvEcosystem(p.type);
|
|
2940
|
-
const absManifest =
|
|
2994
|
+
const absManifest = path39.resolve(rootDir, relManifest);
|
|
2941
2995
|
let text = null;
|
|
2942
2996
|
try {
|
|
2943
|
-
if (
|
|
2997
|
+
if (fs10.existsSync(absManifest)) text = fs10.readFileSync(absManifest, "utf8");
|
|
2944
2998
|
} catch {
|
|
2945
2999
|
text = null;
|
|
2946
3000
|
}
|
|
@@ -3005,34 +3059,34 @@ function buildBreakdown(rootDir, projects) {
|
|
|
3005
3059
|
}
|
|
3006
3060
|
function manifestRelativePath(proj) {
|
|
3007
3061
|
const base = proj.path === "." || proj.path === "" ? "" : proj.path;
|
|
3008
|
-
const
|
|
3062
|
+
const join29 = (name) => base ? path39.join(base, name) : name;
|
|
3009
3063
|
switch (proj.type) {
|
|
3010
3064
|
case "node":
|
|
3011
3065
|
case "typescript":
|
|
3012
|
-
return
|
|
3066
|
+
return join29("package.json");
|
|
3013
3067
|
case "python":
|
|
3014
|
-
return
|
|
3068
|
+
return join29("pyproject.toml");
|
|
3015
3069
|
case "go":
|
|
3016
|
-
return
|
|
3070
|
+
return join29("go.mod");
|
|
3017
3071
|
case "rust":
|
|
3018
|
-
return
|
|
3072
|
+
return join29("Cargo.toml");
|
|
3019
3073
|
case "ruby":
|
|
3020
|
-
return
|
|
3074
|
+
return join29("Gemfile");
|
|
3021
3075
|
case "php":
|
|
3022
|
-
return
|
|
3076
|
+
return join29("composer.json");
|
|
3023
3077
|
case "java":
|
|
3024
3078
|
case "kotlin":
|
|
3025
3079
|
case "scala":
|
|
3026
3080
|
case "groovy":
|
|
3027
|
-
return
|
|
3081
|
+
return join29("pom.xml");
|
|
3028
3082
|
case "dotnet":
|
|
3029
|
-
return
|
|
3083
|
+
return join29(proj.name ? `${proj.name}.csproj` : "Directory.Build.props");
|
|
3030
3084
|
case "dart":
|
|
3031
|
-
return
|
|
3085
|
+
return join29("pubspec.yaml");
|
|
3032
3086
|
case "elixir":
|
|
3033
|
-
return
|
|
3087
|
+
return join29("mix.exs");
|
|
3034
3088
|
default:
|
|
3035
|
-
return
|
|
3089
|
+
return join29("package.json");
|
|
3036
3090
|
}
|
|
3037
3091
|
}
|
|
3038
3092
|
function lockfileRelativePath(rootDir, proj) {
|
|
@@ -3063,9 +3117,9 @@ function lockfileRelativePath(rootDir, proj) {
|
|
|
3063
3117
|
const dirsToTry = base ? [base, ""] : [""];
|
|
3064
3118
|
for (const dir of dirsToTry) {
|
|
3065
3119
|
for (const name of candidateNames) {
|
|
3066
|
-
const rel = dir ?
|
|
3120
|
+
const rel = dir ? path39.join(dir, name) : name;
|
|
3067
3121
|
try {
|
|
3068
|
-
if (
|
|
3122
|
+
if (fs10.existsSync(path39.join(rootDir, rel))) return rel;
|
|
3069
3123
|
} catch {
|
|
3070
3124
|
}
|
|
3071
3125
|
}
|
|
@@ -3136,10 +3190,10 @@ function lookupImportFiles(map, pkg) {
|
|
|
3136
3190
|
}
|
|
3137
3191
|
function firstPackageMentionLine(root2, relFile, pkg) {
|
|
3138
3192
|
try {
|
|
3139
|
-
const abs =
|
|
3140
|
-
const stat2 =
|
|
3193
|
+
const abs = path39.resolve(root2, relFile);
|
|
3194
|
+
const stat2 = fs10.statSync(abs);
|
|
3141
3195
|
if (!stat2.isFile() || stat2.size > 256 * 1024) return null;
|
|
3142
|
-
const text =
|
|
3196
|
+
const text = fs10.readFileSync(abs, "utf8");
|
|
3143
3197
|
const tokens = [pkg];
|
|
3144
3198
|
if (pkg.startsWith("@")) {
|
|
3145
3199
|
const parts = pkg.split("/");
|
|
@@ -3291,7 +3345,7 @@ function hoverMarkdown(dep, contribution) {
|
|
|
3291
3345
|
return lines.join("\n");
|
|
3292
3346
|
}
|
|
3293
3347
|
function startLanguageServer(opts) {
|
|
3294
|
-
if (!
|
|
3348
|
+
if (!fs10.existsSync(opts.root)) {
|
|
3295
3349
|
process.stderr.write(`vg lsp: root does not exist: ${opts.root}
|
|
3296
3350
|
`);
|
|
3297
3351
|
process.exit(1);
|
|
@@ -3333,10 +3387,90 @@ function registerLsp(program) {
|
|
|
3333
3387
|
});
|
|
3334
3388
|
applyGlobalOptions(cmd);
|
|
3335
3389
|
}
|
|
3390
|
+
var HOOK_NEEDLE = "vg hook pre-tool-use";
|
|
3391
|
+
var MATCHER = "Grep|Glob";
|
|
3392
|
+
var HOOK_TIMEOUT_S = 10;
|
|
3393
|
+
function hookCommand(vgBin) {
|
|
3394
|
+
return `${vgBin} hook pre-tool-use`;
|
|
3395
|
+
}
|
|
3396
|
+
function installClaudeHooks(root2, vgBin = "vg") {
|
|
3397
|
+
const file = path39.join(root2, ".claude", "settings.json");
|
|
3398
|
+
const rel = path39.join(".claude", "settings.json");
|
|
3399
|
+
let settings = {};
|
|
3400
|
+
if (fs10.existsSync(file)) {
|
|
3401
|
+
try {
|
|
3402
|
+
const parsed = JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
3403
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3404
|
+
return { file: rel, status: "skipped", note: "settings.json is not a JSON object \u2014 left untouched" };
|
|
3405
|
+
}
|
|
3406
|
+
settings = parsed;
|
|
3407
|
+
} catch {
|
|
3408
|
+
return { file: rel, status: "skipped", note: "settings.json is not parseable JSON \u2014 left untouched (fix it and rerun)" };
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
const hooks = settings.hooks ??= {};
|
|
3412
|
+
if (typeof hooks !== "object" || Array.isArray(hooks)) {
|
|
3413
|
+
return { file: rel, status: "skipped", note: '"hooks" is not an object \u2014 left untouched' };
|
|
3414
|
+
}
|
|
3415
|
+
const pre = hooks.PreToolUse ??= [];
|
|
3416
|
+
if (!Array.isArray(pre)) {
|
|
3417
|
+
return { file: rel, status: "skipped", note: '"hooks.PreToolUse" is not an array \u2014 left untouched' };
|
|
3418
|
+
}
|
|
3419
|
+
const desired = { type: "command", command: hookCommand(vgBin), timeout: HOOK_TIMEOUT_S };
|
|
3420
|
+
for (const group of pre) {
|
|
3421
|
+
for (const entry of group.hooks ?? []) {
|
|
3422
|
+
if (typeof entry.command === "string" && entry.command.includes(HOOK_NEEDLE)) {
|
|
3423
|
+
if (entry.command === desired.command && entry.timeout === desired.timeout && group.matcher === MATCHER) {
|
|
3424
|
+
return { file: rel, status: "unchanged" };
|
|
3425
|
+
}
|
|
3426
|
+
entry.command = desired.command;
|
|
3427
|
+
entry.timeout = desired.timeout;
|
|
3428
|
+
group.matcher = MATCHER;
|
|
3429
|
+
writeSettings(file, settings);
|
|
3430
|
+
return { file: rel, status: "written", note: "updated existing vg hook entry" };
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
pre.push({ matcher: MATCHER, hooks: [desired] });
|
|
3435
|
+
writeSettings(file, settings);
|
|
3436
|
+
return { file: rel, status: "written" };
|
|
3437
|
+
}
|
|
3438
|
+
function uninstallClaudeHooks(root2) {
|
|
3439
|
+
const file = path39.join(root2, ".claude", "settings.json");
|
|
3440
|
+
const rel = path39.join(".claude", "settings.json");
|
|
3441
|
+
if (!fs10.existsSync(file)) return { file: rel, status: "unchanged" };
|
|
3442
|
+
let settings;
|
|
3443
|
+
try {
|
|
3444
|
+
settings = JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
3445
|
+
} catch {
|
|
3446
|
+
return { file: rel, status: "skipped", note: "settings.json is not parseable JSON \u2014 left untouched" };
|
|
3447
|
+
}
|
|
3448
|
+
const hooks = settings.hooks;
|
|
3449
|
+
const pre = hooks?.PreToolUse;
|
|
3450
|
+
if (!Array.isArray(pre)) return { file: rel, status: "unchanged" };
|
|
3451
|
+
let removed = false;
|
|
3452
|
+
for (const group of pre) {
|
|
3453
|
+
const before = group.hooks?.length ?? 0;
|
|
3454
|
+
group.hooks = (group.hooks ?? []).filter((e) => !(typeof e.command === "string" && e.command.includes(HOOK_NEEDLE)));
|
|
3455
|
+
if (group.hooks.length !== before) removed = true;
|
|
3456
|
+
}
|
|
3457
|
+
const kept = pre.filter((g) => (g.hooks?.length ?? 0) > 0);
|
|
3458
|
+
if (!removed) return { file: rel, status: "unchanged" };
|
|
3459
|
+
if (kept.length) hooks.PreToolUse = kept;
|
|
3460
|
+
else delete hooks.PreToolUse;
|
|
3461
|
+
if (hooks && Object.keys(hooks).length === 0) delete settings.hooks;
|
|
3462
|
+
writeSettings(file, settings);
|
|
3463
|
+
return { file: rel, status: "written" };
|
|
3464
|
+
}
|
|
3465
|
+
function writeSettings(file, settings) {
|
|
3466
|
+
fs10.mkdirSync(path39.dirname(file), { recursive: true });
|
|
3467
|
+
fs10.writeFileSync(file, `${JSON.stringify(settings, null, 2)}
|
|
3468
|
+
`);
|
|
3469
|
+
}
|
|
3336
3470
|
|
|
3337
3471
|
// src/commands/install.ts
|
|
3338
3472
|
function registerInstall(program) {
|
|
3339
|
-
const install = program.command("install").description("add vg to your AI assistant(s): skill + MCP + advisory nudge").argument("[tools...]", `assistant ids: ${ASSISTANTS.map((a) => a.id).join(", ")}`).option("--all", "install for every supported assistant").option("--detect", "detect assistants in use (repo footprint, home config, PATH) and install for those; with --list, only report what was detected").option("--list", "show the support matrix and exit").option("--no-hook", "skip the advisory nudge").action(function(tools, opts) {
|
|
3473
|
+
const install = program.command("install").description("add vg to your AI assistant(s): skill + MCP + advisory nudge").argument("[tools...]", `assistant ids: ${ASSISTANTS.map((a) => a.id).join(", ")}`).option("--all", "install for every supported assistant").option("--detect", "detect assistants in use (repo footprint, home config, PATH) and install for those; with --list, only report what was detected").option("--list", "show the support matrix and exit").option("--no-hook", "skip the advisory nudge").option("--hooks", "also wire the PreToolUse enrichment hook (Claude Code): Grep/Glob calls get graph context via `vg hook pre-tool-use` (project .claude/settings.json; opt-in)").action(function(tools, opts) {
|
|
3340
3474
|
const global = readGlobal(this);
|
|
3341
3475
|
const root2 = rootOf(global);
|
|
3342
3476
|
const detected = opts.detect ? detectAssistants(root2) : [];
|
|
@@ -3400,6 +3534,12 @@ function registerInstall(program) {
|
|
|
3400
3534
|
for (const r of results) {
|
|
3401
3535
|
info(`${c.green("\u2714")} ${c.bold(r.id)} \u2014 wrote ${r.wrote.join(", ")}${r.skipped.length ? c.dim(` (skipped ${r.skipped.join(", ")})`) : ""}`);
|
|
3402
3536
|
}
|
|
3537
|
+
if (opts.hooks && targets.some((a) => a.id === "claude")) {
|
|
3538
|
+
const vgBin = launch.command === "npx" ? null : launch.command;
|
|
3539
|
+
const hooksResult = vgBin ? installClaudeHooks(root2, vgBin) : { file: ".claude/settings.json", status: "skipped", note: "vg is not installed on PATH \u2014 hooks need a fast binary (npm i -g @vibgrate/cli), rerun with --hooks after" };
|
|
3540
|
+
if (hooksResult.status === "written") info(`${c.green("\u2714")} ${c.bold("hooks")} \u2014 wrote PreToolUse enrichment into ${hooksResult.file}`);
|
|
3541
|
+
else if (hooksResult.note) info(`${c.yellow("!")} hooks \u2014 ${hooksResult.note}`);
|
|
3542
|
+
}
|
|
3403
3543
|
if (launch.note && results.some((r) => r.note)) info(`${c.yellow("!")} ${launch.note}`);
|
|
3404
3544
|
if (smallRepo) info(c.dim(` note: small repo (${fileCount} files) \u2014 nudge says searching is fine; vg is still used for impact/tests`));
|
|
3405
3545
|
info(c.dim(` wrote ${navConfig} \u2014 deferred-loading config for Claude-API agents (lower per-step token cost)`));
|
|
@@ -3410,6 +3550,10 @@ function registerInstall(program) {
|
|
|
3410
3550
|
const global = readGlobal(this);
|
|
3411
3551
|
const root2 = rootOf(global);
|
|
3412
3552
|
const results = tools.map(resolve5).map((a) => ({ id: a.id, removed: uninstallAssistant(a, root2, !!opts.purge) }));
|
|
3553
|
+
if (tools.map(resolve5).some((a) => a.id === "claude")) {
|
|
3554
|
+
const hooksResult = uninstallClaudeHooks(root2);
|
|
3555
|
+
if (hooksResult.status === "written") results.find((r) => r.id === "claude")?.removed.push(`${hooksResult.file} (vg hook entries)`);
|
|
3556
|
+
}
|
|
3413
3557
|
if (global.json) {
|
|
3414
3558
|
json({ root: root2, results });
|
|
3415
3559
|
return;
|
|
@@ -3434,14 +3578,289 @@ function resolve5(id) {
|
|
|
3434
3578
|
}
|
|
3435
3579
|
return a;
|
|
3436
3580
|
}
|
|
3581
|
+
var RELEVANCE_MODULE_NAME = "@vibgrate/relevance";
|
|
3582
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
3583
|
+
function registryBase(opts) {
|
|
3584
|
+
return (process.env.VIBGRATE_MODULE_REGISTRY || opts.registry || DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
3585
|
+
}
|
|
3586
|
+
function stateFile() {
|
|
3587
|
+
return path39.join(path39.dirname(relevanceModuleDir()), "consent.json");
|
|
3588
|
+
}
|
|
3589
|
+
function readConsent() {
|
|
3590
|
+
try {
|
|
3591
|
+
return JSON.parse(fs10.readFileSync(stateFile(), "utf8"));
|
|
3592
|
+
} catch {
|
|
3593
|
+
return {};
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
function writeConsent(state) {
|
|
3597
|
+
try {
|
|
3598
|
+
fs10.mkdirSync(path39.dirname(stateFile()), { recursive: true });
|
|
3599
|
+
fs10.writeFileSync(stateFile(), JSON.stringify(state));
|
|
3600
|
+
} catch {
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function kernelDisabled() {
|
|
3604
|
+
const v = process.env.VIBGRATE_NO_KERNEL;
|
|
3605
|
+
return v === "1" || v === "true";
|
|
3606
|
+
}
|
|
3607
|
+
function moduleInstalled() {
|
|
3608
|
+
try {
|
|
3609
|
+
const meta = JSON.parse(fs10.readFileSync(path39.join(relevanceModuleDir(), ".module.json"), "utf8"));
|
|
3610
|
+
if (fs10.existsSync(path39.join(relevanceModuleDir(), "index.js"))) {
|
|
3611
|
+
return { installed: true, version: meta.version };
|
|
3612
|
+
}
|
|
3613
|
+
} catch {
|
|
3614
|
+
}
|
|
3615
|
+
return { installed: false };
|
|
3616
|
+
}
|
|
3617
|
+
var DISCLOSURE = "vg can install the optional Vibgrate relevance module (proprietary license, runs fully locally, improves how questions find the code they mean). Disable any time with VIBGRATE_NO_KERNEL=1.";
|
|
3618
|
+
function verifyIntegrity(buf, integrity, shasum) {
|
|
3619
|
+
if (integrity) {
|
|
3620
|
+
const [algo, expected] = integrity.split("-", 2);
|
|
3621
|
+
if (!algo || !expected) throw new Error("malformed integrity metadata");
|
|
3622
|
+
const actual = createHash(algo).update(buf).digest("base64");
|
|
3623
|
+
if (actual !== expected) throw new Error(`integrity mismatch (${algo})`);
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
if (shasum) {
|
|
3627
|
+
const actual = createHash("sha1").update(buf).digest("hex");
|
|
3628
|
+
if (actual !== shasum) throw new Error("integrity mismatch (shasum)");
|
|
3629
|
+
return;
|
|
3630
|
+
}
|
|
3631
|
+
throw new Error("registry provided no integrity metadata");
|
|
3632
|
+
}
|
|
3633
|
+
function untar(data) {
|
|
3634
|
+
const out2 = [];
|
|
3635
|
+
let offset = 0;
|
|
3636
|
+
let pendingName = null;
|
|
3637
|
+
while (offset + 512 <= data.length) {
|
|
3638
|
+
const block = data.subarray(offset, offset + 512);
|
|
3639
|
+
offset += 512;
|
|
3640
|
+
if (block.every((b) => b === 0)) continue;
|
|
3641
|
+
const rawName = block.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
|
3642
|
+
const size = parseInt(block.subarray(124, 136).toString("utf8").replace(/\0.*$/, "").trim() || "0", 8);
|
|
3643
|
+
const type = String.fromCharCode(block[156]);
|
|
3644
|
+
const prefix = block.subarray(345, 500).toString("utf8").replace(/\0.*$/, "");
|
|
3645
|
+
const body = data.subarray(offset, offset + size);
|
|
3646
|
+
offset += Math.ceil(size / 512) * 512;
|
|
3647
|
+
if (type === "L") {
|
|
3648
|
+
pendingName = body.toString("utf8").replace(/\0.*$/, "");
|
|
3649
|
+
continue;
|
|
3650
|
+
}
|
|
3651
|
+
if (type === "x" || type === "g") {
|
|
3652
|
+
const m = /(?:^|\n)\d+ path=([^\n]+)\n/.exec(body.toString("utf8"));
|
|
3653
|
+
if (m) pendingName = m[1];
|
|
3654
|
+
continue;
|
|
3655
|
+
}
|
|
3656
|
+
if (type !== "0" && type !== "\0") continue;
|
|
3657
|
+
const name = pendingName ?? (prefix ? `${prefix}/${rawName}` : rawName);
|
|
3658
|
+
pendingName = null;
|
|
3659
|
+
if (name.split("/").includes("..") || path39.isAbsolute(name)) {
|
|
3660
|
+
throw new Error(`unsafe path in tarball: ${name}`);
|
|
3661
|
+
}
|
|
3662
|
+
out2.push({ name, body: Buffer.from(body) });
|
|
3663
|
+
}
|
|
3664
|
+
return out2;
|
|
3665
|
+
}
|
|
3666
|
+
async function installRelevanceModule(opts = {}) {
|
|
3667
|
+
if (kernelDisabled()) return { status: "disabled", detail: "VIBGRATE_NO_KERNEL is set" };
|
|
3668
|
+
const existing = moduleInstalled();
|
|
3669
|
+
if (existing.installed && !opts.force) return { status: "already-installed", version: existing.version };
|
|
3670
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
3671
|
+
const base = registryBase(opts);
|
|
3672
|
+
try {
|
|
3673
|
+
const metaRes = await doFetch(`${base}/${RELEVANCE_MODULE_NAME.replace("/", "%2f")}`);
|
|
3674
|
+
if (!metaRes.ok) return { status: "unavailable", detail: `registry ${metaRes.status}` };
|
|
3675
|
+
const meta = await metaRes.json();
|
|
3676
|
+
const version = meta["dist-tags"]?.latest;
|
|
3677
|
+
const dist = version ? meta.versions?.[version]?.dist : void 0;
|
|
3678
|
+
if (!version || !dist?.tarball) return { status: "unavailable", detail: "no published version" };
|
|
3679
|
+
const tarRes = await doFetch(dist.tarball);
|
|
3680
|
+
if (!tarRes.ok) return { status: "unavailable", detail: `tarball ${tarRes.status}` };
|
|
3681
|
+
const gz = Buffer.from(await tarRes.arrayBuffer());
|
|
3682
|
+
verifyIntegrity(gz, dist.integrity, dist.shasum);
|
|
3683
|
+
const entries = untar(zlib.gunzipSync(gz));
|
|
3684
|
+
const dir = relevanceModuleDir();
|
|
3685
|
+
fs10.rmSync(dir, { recursive: true, force: true });
|
|
3686
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
3687
|
+
let wrote = 0;
|
|
3688
|
+
for (const e of entries) {
|
|
3689
|
+
let rel = null;
|
|
3690
|
+
if (e.name.startsWith("package/dist/")) rel = e.name.slice("package/dist/".length);
|
|
3691
|
+
else if (e.name === "package/LICENSE" || e.name === "package/README.md") rel = e.name.slice("package/".length);
|
|
3692
|
+
if (!rel) continue;
|
|
3693
|
+
const target = path39.join(dir, rel);
|
|
3694
|
+
if (!target.startsWith(dir + path39.sep) && target !== dir) throw new Error(`unsafe extract path: ${rel}`);
|
|
3695
|
+
fs10.mkdirSync(path39.dirname(target), { recursive: true });
|
|
3696
|
+
fs10.writeFileSync(target, e.body);
|
|
3697
|
+
wrote += 1;
|
|
3698
|
+
}
|
|
3699
|
+
if (!wrote || !fs10.existsSync(path39.join(dir, "index.js"))) {
|
|
3700
|
+
fs10.rmSync(dir, { recursive: true, force: true });
|
|
3701
|
+
return { status: "unavailable", detail: "package layout not recognized (no dist/index.js)" };
|
|
3702
|
+
}
|
|
3703
|
+
fs10.writeFileSync(path39.join(dir, ".module.json"), JSON.stringify({ name: RELEVANCE_MODULE_NAME, version }));
|
|
3704
|
+
resetRelevanceProviderCache();
|
|
3705
|
+
return { status: "installed", version };
|
|
3706
|
+
} catch (e) {
|
|
3707
|
+
return { status: "unavailable", detail: String(e.message ?? e).slice(0, 200) };
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
function removeRelevanceModule() {
|
|
3711
|
+
fs10.rmSync(relevanceModuleDir(), { recursive: true, force: true });
|
|
3712
|
+
resetRelevanceProviderCache();
|
|
3713
|
+
}
|
|
3714
|
+
async function ensureRelevanceModule(io = {}) {
|
|
3715
|
+
try {
|
|
3716
|
+
if (kernelDisabled()) return { status: "disabled" };
|
|
3717
|
+
const existing = moduleInstalled();
|
|
3718
|
+
if (existing.installed) return { status: "already-installed", version: existing.version };
|
|
3719
|
+
if (readConsent().relevance === "denied") return { status: "declined" };
|
|
3720
|
+
return await installRelevanceModule({ fetchImpl: io.fetchImpl });
|
|
3721
|
+
} catch {
|
|
3722
|
+
return { status: "unavailable", detail: "auto-install failed" };
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
// src/commands/module.ts
|
|
3727
|
+
function registerModule(program) {
|
|
3728
|
+
const cmd = program.command("module").description("manage optional local modules (relevance)");
|
|
3729
|
+
cmd.command("install").description("install an optional module (fetches from the registry, integrity-checked)").argument("<name>", "module name (supported: relevance)").option("--yes", "skip the confirmation prompt").option("--force", "reinstall even when already present").action(async function(name, opts) {
|
|
3730
|
+
const global = readGlobal(this);
|
|
3731
|
+
requireRelevance(name);
|
|
3732
|
+
if (kernelDisabled()) {
|
|
3733
|
+
throw new CliError("VIBGRATE_NO_KERNEL is set \u2014 unset it to install the relevance module", ExitCode.USAGE_ERROR);
|
|
3734
|
+
}
|
|
3735
|
+
if (!opts.yes && process.stdin.isTTY && process.stdout.isTTY) {
|
|
3736
|
+
const ok = await confirm(`${DISCLOSURE}
|
|
3737
|
+
Install now? [Y/n] `);
|
|
3738
|
+
if (!ok) {
|
|
3739
|
+
writeConsent({ ...readConsent(), relevance: "denied" });
|
|
3740
|
+
info("not installed");
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
writeConsent({ ...readConsent(), relevance: "granted" });
|
|
3745
|
+
const result = await installRelevanceModule({ force: opts.force });
|
|
3746
|
+
if (global.json) {
|
|
3747
|
+
json(result);
|
|
3748
|
+
if (result.status === "unavailable") process.exitCode = ExitCode.ERROR;
|
|
3749
|
+
return;
|
|
3750
|
+
}
|
|
3751
|
+
if (result.status === "installed") out(`installed ${RELEVANCE_MODULE_NAME}@${result.version}`);
|
|
3752
|
+
else if (result.status === "already-installed") out(`already installed (${result.version ?? "unknown"}) \u2014 use --force to reinstall`);
|
|
3753
|
+
else throw new CliError(`install failed: ${result.detail ?? result.status} \u2014 check network access to the registry, or retry later`, ExitCode.ERROR);
|
|
3754
|
+
});
|
|
3755
|
+
cmd.command("remove").description("remove an installed module").argument("<name>", "module name (supported: relevance)").action(async function(name) {
|
|
3756
|
+
requireRelevance(name);
|
|
3757
|
+
removeRelevanceModule();
|
|
3758
|
+
out("removed");
|
|
3759
|
+
});
|
|
3760
|
+
cmd.command("status").description("show installed modules and whether they load").action(async function() {
|
|
3761
|
+
const global = readGlobal(this);
|
|
3762
|
+
const installed = moduleInstalled();
|
|
3763
|
+
resetRelevanceProviderCache();
|
|
3764
|
+
const provider = installed.installed ? await loadRelevanceProvider() : null;
|
|
3765
|
+
const status = {
|
|
3766
|
+
relevance: {
|
|
3767
|
+
installed: installed.installed,
|
|
3768
|
+
version: installed.version ?? null,
|
|
3769
|
+
loadable: !!provider,
|
|
3770
|
+
providerVersion: provider ? provider.version() : null,
|
|
3771
|
+
disabled: kernelDisabled(),
|
|
3772
|
+
consent: readConsent().relevance ?? "unset"
|
|
3773
|
+
}
|
|
3774
|
+
};
|
|
3775
|
+
if (global.json) {
|
|
3776
|
+
json(status);
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
const r = status.relevance;
|
|
3780
|
+
out(`relevance: ${r.installed ? `installed ${r.version}` : "not installed"}${r.disabled ? " (disabled via VIBGRATE_NO_KERNEL)" : ""}`);
|
|
3781
|
+
if (r.installed) out(` loads: ${r.loadable ? `yes (${r.providerVersion})` : "no"}`);
|
|
3782
|
+
info(c.dim(` consent: ${r.consent}`));
|
|
3783
|
+
});
|
|
3784
|
+
applyGlobalOptions(cmd);
|
|
3785
|
+
}
|
|
3786
|
+
function requireRelevance(name) {
|
|
3787
|
+
if (name !== "relevance") {
|
|
3788
|
+
throw usageError(`unknown module '${name}' \u2014 supported modules: relevance`);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
function confirm(question) {
|
|
3792
|
+
return new Promise((resolve21) => {
|
|
3793
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
3794
|
+
rl.question(question, (answer) => {
|
|
3795
|
+
rl.close();
|
|
3796
|
+
resolve21(answer.trim() === "" || /^y(es)?$/i.test(answer.trim()));
|
|
3797
|
+
});
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
var MAX_HOOK_MATCHES = 5;
|
|
3801
|
+
var MIN_PATTERN_CHARS = 3;
|
|
3802
|
+
function patternToQuery(pattern) {
|
|
3803
|
+
return pattern.replace(/\\[dwsbDWSB]/g, " ").replace(/[\\^$.*+?()[\]{}|]/g, " ").replace(/\s+/g, " ").trim();
|
|
3804
|
+
}
|
|
3805
|
+
function hookContext(payloadText, root2, graphOverride) {
|
|
3806
|
+
let payload;
|
|
3807
|
+
try {
|
|
3808
|
+
payload = JSON.parse(payloadText);
|
|
3809
|
+
} catch {
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
if (payload.tool_name !== "Grep" && payload.tool_name !== "Glob") return null;
|
|
3813
|
+
const raw = payload.tool_input?.pattern ?? payload.tool_input?.query ?? "";
|
|
3814
|
+
const query = patternToQuery(String(raw));
|
|
3815
|
+
if (query.length < MIN_PATTERN_CHARS) return null;
|
|
3816
|
+
const graphPath = resolveGraphPath(root2, graphOverride);
|
|
3817
|
+
let graph;
|
|
3818
|
+
try {
|
|
3819
|
+
const preferred = loadGraphPreferIndex(root2, graphPath);
|
|
3820
|
+
graph = preferred ? preferred.graph : parseGraph(fs10.readFileSync(graphPath, "utf8"));
|
|
3821
|
+
} catch {
|
|
3822
|
+
return null;
|
|
3823
|
+
}
|
|
3824
|
+
const result = queryGraph(graph, query, {});
|
|
3825
|
+
const matches = result.matches.slice(0, MAX_HOOK_MATCHES);
|
|
3826
|
+
if (!matches.length) return null;
|
|
3827
|
+
const lines = matches.map(
|
|
3828
|
+
(m) => `- \`${m.node.qualifiedName}\` (${m.node.kind}) \u2014 ${m.node.file}:${m.node.span.start}${m.node.isHub ? " [hub \u2014 check impact_of before changing]" : ""}`
|
|
3829
|
+
);
|
|
3830
|
+
return [
|
|
3831
|
+
`vg code map \u2014 symbols matching "${query}":`,
|
|
3832
|
+
...lines,
|
|
3833
|
+
"These locations come from the deterministic code graph; prefer them over scanning the raw grep output."
|
|
3834
|
+
].join("\n");
|
|
3835
|
+
}
|
|
3836
|
+
function registerHook(program) {
|
|
3837
|
+
const cmd = program.command("hook", { hidden: true }).description("assistant-host hook endpoints (wired by `vg install --hooks`)");
|
|
3838
|
+
const pre = cmd.command("pre-tool-use").description("PreToolUse enrichment: annotate Grep/Glob calls with graph context (stdin: host hook payload)").action(async function() {
|
|
3839
|
+
const global = readGlobal(this);
|
|
3840
|
+
const root2 = rootOf(global);
|
|
3841
|
+
try {
|
|
3842
|
+
const stdin = fs10.readFileSync(0, "utf8");
|
|
3843
|
+
const context = hookContext(stdin, root2, global.graph);
|
|
3844
|
+
if (context) {
|
|
3845
|
+
process.stdout.write(
|
|
3846
|
+
`${JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } })}
|
|
3847
|
+
`
|
|
3848
|
+
);
|
|
3849
|
+
}
|
|
3850
|
+
} catch {
|
|
3851
|
+
}
|
|
3852
|
+
});
|
|
3853
|
+
applyGlobalOptions(pre);
|
|
3854
|
+
applyGlobalOptions(cmd);
|
|
3855
|
+
}
|
|
3437
3856
|
var HOOK_BEGIN = "# vg:begin";
|
|
3438
3857
|
var HOOK_END = "# vg:end";
|
|
3439
3858
|
function registerShare(program) {
|
|
3440
3859
|
const cmd = program.command("share").description("make the map committable + auto-updating for your team (git)").option("--undo", "reverse what `vg share` installed").option("--reports", "also commit graph.html / GRAPH_REPORT.md (default: gitignored)").action(function(opts) {
|
|
3441
3860
|
const global = readGlobal(this);
|
|
3442
3861
|
const root2 = rootOf(global);
|
|
3443
|
-
const gitDir =
|
|
3444
|
-
if (!
|
|
3862
|
+
const gitDir = path39.join(root2, ".git");
|
|
3863
|
+
if (!fs10.existsSync(gitDir)) {
|
|
3445
3864
|
throw new CliError("not a git repository \u2014 run `vg share` inside a git repo", ExitCode.USAGE_ERROR);
|
|
3446
3865
|
}
|
|
3447
3866
|
if (opts.undo) {
|
|
@@ -3462,23 +3881,23 @@ function registerShare(program) {
|
|
|
3462
3881
|
applyGlobalOptions(cmd);
|
|
3463
3882
|
}
|
|
3464
3883
|
function writeVibgrateGitignore(root2, reports) {
|
|
3465
|
-
const dir =
|
|
3466
|
-
|
|
3884
|
+
const dir = path39.join(root2, ".vibgrate");
|
|
3885
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
3467
3886
|
const lines = ["# Managed by `vg share` \u2014 keep graph.json committed, ignore the rest", "cache/", "facts.jsonl", "graph.snap"];
|
|
3468
3887
|
if (!reports) lines.push("graph.html", "GRAPH_REPORT.md");
|
|
3469
|
-
|
|
3888
|
+
fs10.writeFileSync(path39.join(dir, ".gitignore"), `${lines.join("\n")}
|
|
3470
3889
|
`);
|
|
3471
3890
|
}
|
|
3472
3891
|
function writeGitAttributes(root2) {
|
|
3473
|
-
const file =
|
|
3892
|
+
const file = path39.join(root2, ".gitattributes");
|
|
3474
3893
|
const line = ".vibgrate/graph.json merge=vg";
|
|
3475
|
-
let content =
|
|
3894
|
+
let content = fs10.existsSync(file) ? fs10.readFileSync(file, "utf8") : "";
|
|
3476
3895
|
if (!content.split("\n").some((l) => l.trim() === line)) {
|
|
3477
3896
|
content = content.length ? `${content.replace(/\s*$/, "")}
|
|
3478
3897
|
${line}
|
|
3479
3898
|
` : `${line}
|
|
3480
3899
|
`;
|
|
3481
|
-
|
|
3900
|
+
fs10.writeFileSync(file, content);
|
|
3482
3901
|
}
|
|
3483
3902
|
}
|
|
3484
3903
|
function configureMergeDriver(root2) {
|
|
@@ -3487,8 +3906,8 @@ function configureMergeDriver(root2) {
|
|
|
3487
3906
|
}
|
|
3488
3907
|
function installPreCommitHook(root2) {
|
|
3489
3908
|
const hooksDir = hookDir(root2);
|
|
3490
|
-
|
|
3491
|
-
const file =
|
|
3909
|
+
fs10.mkdirSync(hooksDir, { recursive: true });
|
|
3910
|
+
const file = path39.join(hooksDir, "pre-commit");
|
|
3492
3911
|
const block = [
|
|
3493
3912
|
HOOK_BEGIN,
|
|
3494
3913
|
"if command -v vg >/dev/null 2>&1; then",
|
|
@@ -3497,7 +3916,7 @@ function installPreCommitHook(root2) {
|
|
|
3497
3916
|
"fi",
|
|
3498
3917
|
HOOK_END
|
|
3499
3918
|
].join("\n");
|
|
3500
|
-
let content =
|
|
3919
|
+
let content = fs10.existsSync(file) ? fs10.readFileSync(file, "utf8") : "#!/bin/sh\n";
|
|
3501
3920
|
const re = new RegExp(`${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}`);
|
|
3502
3921
|
if (re.test(content)) content = content.replace(re, block);
|
|
3503
3922
|
else content = `${content.replace(/\s*$/, "")}
|
|
@@ -3506,21 +3925,21 @@ ${block}
|
|
|
3506
3925
|
`;
|
|
3507
3926
|
if (!content.startsWith("#!")) content = `#!/bin/sh
|
|
3508
3927
|
${content}`;
|
|
3509
|
-
|
|
3510
|
-
|
|
3928
|
+
fs10.writeFileSync(file, content);
|
|
3929
|
+
fs10.chmodSync(file, 493);
|
|
3511
3930
|
}
|
|
3512
3931
|
function undo(root2) {
|
|
3513
|
-
const hookFile =
|
|
3514
|
-
if (
|
|
3932
|
+
const hookFile = path39.join(hookDir(root2), "pre-commit");
|
|
3933
|
+
if (fs10.existsSync(hookFile)) {
|
|
3515
3934
|
const re = new RegExp(`\\n*${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}\\n*`);
|
|
3516
|
-
const next2 =
|
|
3517
|
-
if (next2.trim() === "#!/bin/sh")
|
|
3518
|
-
else
|
|
3935
|
+
const next2 = fs10.readFileSync(hookFile, "utf8").replace(re, "\n");
|
|
3936
|
+
if (next2.trim() === "#!/bin/sh") fs10.rmSync(hookFile);
|
|
3937
|
+
else fs10.writeFileSync(hookFile, next2);
|
|
3519
3938
|
}
|
|
3520
|
-
const attr =
|
|
3521
|
-
if (
|
|
3522
|
-
const next2 =
|
|
3523
|
-
|
|
3939
|
+
const attr = path39.join(root2, ".gitattributes");
|
|
3940
|
+
if (fs10.existsSync(attr)) {
|
|
3941
|
+
const next2 = fs10.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
|
|
3942
|
+
fs10.writeFileSync(attr, next2);
|
|
3524
3943
|
}
|
|
3525
3944
|
try {
|
|
3526
3945
|
git(root2, ["config", "--remove-section", "merge.vg"]);
|
|
@@ -3530,10 +3949,10 @@ function undo(root2) {
|
|
|
3530
3949
|
function hookDir(root2) {
|
|
3531
3950
|
try {
|
|
3532
3951
|
const configured = git(root2, ["config", "--get", "core.hooksPath"]).trim();
|
|
3533
|
-
if (configured) return
|
|
3952
|
+
if (configured) return path39.resolve(root2, configured);
|
|
3534
3953
|
} catch {
|
|
3535
3954
|
}
|
|
3536
|
-
return
|
|
3955
|
+
return path39.join(root2, ".git", "hooks");
|
|
3537
3956
|
}
|
|
3538
3957
|
function git(root2, args) {
|
|
3539
3958
|
return execFileSync("git", args, { cwd: root2, encoding: "utf8" });
|
|
@@ -3542,28 +3961,14 @@ function escapeRe(s) {
|
|
|
3542
3961
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3543
3962
|
}
|
|
3544
3963
|
var DEFAULT_DEBOUNCE_MS = 400;
|
|
3545
|
-
var SKIP_NAME = /* @__PURE__ */ new Set([
|
|
3546
|
-
".git",
|
|
3547
|
-
"node_modules",
|
|
3548
|
-
".vibgrate",
|
|
3549
|
-
"dist",
|
|
3550
|
-
"build",
|
|
3551
|
-
"out",
|
|
3552
|
-
"coverage",
|
|
3553
|
-
".next",
|
|
3554
|
-
"target",
|
|
3555
|
-
"__pycache__",
|
|
3556
|
-
".venv",
|
|
3557
|
-
"venv"
|
|
3558
|
-
]);
|
|
3559
3964
|
function registerWatch(program) {
|
|
3560
3965
|
const cmd = program.command("watch").description("rebuild the code map when files change (debounced)").argument("[paths...]", "folders to watch (default: current folder)").option("--debounce <ms>", "settle time after a change before rebuild", String(DEFAULT_DEBOUNCE_MS)).option("--no-html", "do not write graph.html on each rebuild").option("--no-report", "do not write GRAPH_REPORT.md on each rebuild").option("--fast", "skip precise tsc resolve on rebuilds (heuristic only)").action(async function(paths, opts) {
|
|
3561
3966
|
const global = readGlobal(this);
|
|
3562
3967
|
const root2 = rootOf(global);
|
|
3563
3968
|
const debounceMs = Math.max(50, Number(opts.debounce) || DEFAULT_DEBOUNCE_MS);
|
|
3564
|
-
const watchRoots = (paths.length ? paths : ["."]).map((p) =>
|
|
3969
|
+
const watchRoots = (paths.length ? paths : ["."]).map((p) => path39.resolve(root2, p));
|
|
3565
3970
|
for (const w of watchRoots) {
|
|
3566
|
-
if (!
|
|
3971
|
+
if (!fs10.existsSync(w)) {
|
|
3567
3972
|
throw new CliError(`watch path not found: ${w}`, ExitCode.USAGE_ERROR);
|
|
3568
3973
|
}
|
|
3569
3974
|
}
|
|
@@ -3616,16 +4021,12 @@ function registerWatch(program) {
|
|
|
3616
4021
|
const watchers = [];
|
|
3617
4022
|
for (const w of watchRoots) {
|
|
3618
4023
|
try {
|
|
3619
|
-
const watcher =
|
|
4024
|
+
const watcher = fs10.watch(w, { recursive: true }, (_event, filename) => {
|
|
3620
4025
|
if (!filename) {
|
|
3621
4026
|
schedule("change");
|
|
3622
4027
|
return;
|
|
3623
4028
|
}
|
|
3624
|
-
|
|
3625
|
-
if (parts.some((p) => SKIP_NAME.has(p))) return;
|
|
3626
|
-
if (filename.includes(`${path37.sep}.vibgrate${path37.sep}`) || filename.startsWith(".vibgrate")) {
|
|
3627
|
-
return;
|
|
3628
|
-
}
|
|
4029
|
+
if (!isRelevantChange(filename)) return;
|
|
3629
4030
|
schedule(filename);
|
|
3630
4031
|
});
|
|
3631
4032
|
watchers.push(watcher);
|
|
@@ -3633,7 +4034,7 @@ function registerWatch(program) {
|
|
|
3633
4034
|
throw new CliError(`cannot watch ${w}: ${err.message}`, ExitCode.ERROR);
|
|
3634
4035
|
}
|
|
3635
4036
|
}
|
|
3636
|
-
info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) =>
|
|
4037
|
+
info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) => path39.relative(root2, p) || ".").join(", ")} (debounce ${debounceMs}ms) \xB7 Ctrl+C to stop`));
|
|
3637
4038
|
await new Promise((resolve21) => {
|
|
3638
4039
|
const stop = () => {
|
|
3639
4040
|
for (const w of watchers) w.close();
|
|
@@ -3966,10 +4367,10 @@ function normalizeStandards(raw) {
|
|
|
3966
4367
|
}
|
|
3967
4368
|
function loadStandards(root2) {
|
|
3968
4369
|
for (const rel of STANDARDS_FILES) {
|
|
3969
|
-
const p =
|
|
4370
|
+
const p = path39__default.join(root2, rel);
|
|
3970
4371
|
let text;
|
|
3971
4372
|
try {
|
|
3972
|
-
text =
|
|
4373
|
+
text = fs10__default.readFileSync(p, "utf8");
|
|
3973
4374
|
} catch {
|
|
3974
4375
|
continue;
|
|
3975
4376
|
}
|
|
@@ -4687,8 +5088,8 @@ async function runHostBench(options = {}) {
|
|
|
4687
5088
|
}
|
|
4688
5089
|
async function runArmSimulate(input) {
|
|
4689
5090
|
const { arm, lib, modelPath, turns, trie, env, live } = input;
|
|
4690
|
-
const
|
|
4691
|
-
const session = await acquireHostSession(lib,
|
|
5091
|
+
const path41 = `${modelPath}#${arm.id}`;
|
|
5092
|
+
const session = await acquireHostSession(lib, path41);
|
|
4692
5093
|
const system = "You are a coding agent with a Task Capsule.";
|
|
4693
5094
|
const capsule = "function readConfig(): Config { return load(); }";
|
|
4694
5095
|
let last = null;
|
|
@@ -5900,7 +6301,7 @@ function registerModels(program) {
|
|
|
5900
6301
|
);
|
|
5901
6302
|
}
|
|
5902
6303
|
} else {
|
|
5903
|
-
if (!target.path || !
|
|
6304
|
+
if (!target.path || !fs10.existsSync(target.path)) {
|
|
5904
6305
|
throw new CliError(
|
|
5905
6306
|
`model file not found at ${target.path || "(unknown path)"} \u2014 it may already be removed.`,
|
|
5906
6307
|
ExitCode.NOT_FOUND
|
|
@@ -5914,7 +6315,7 @@ function registerModels(program) {
|
|
|
5914
6315
|
}
|
|
5915
6316
|
if (!global.json) info(c.dim(` $ rm ${target.path}`));
|
|
5916
6317
|
try {
|
|
5917
|
-
|
|
6318
|
+
fs10.unlinkSync(target.path);
|
|
5918
6319
|
} catch (e) {
|
|
5919
6320
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5920
6321
|
throw new CliError(`could not delete ${target.path}: ${msg}`, ExitCode.ERROR);
|
|
@@ -6052,9 +6453,9 @@ function registerModels(program) {
|
|
|
6052
6453
|
}
|
|
6053
6454
|
});
|
|
6054
6455
|
if (opts.out) {
|
|
6055
|
-
const outPath =
|
|
6056
|
-
|
|
6057
|
-
|
|
6456
|
+
const outPath = path39.resolve(opts.out);
|
|
6457
|
+
fs10.mkdirSync(path39.dirname(outPath), { recursive: true });
|
|
6458
|
+
fs10.writeFileSync(outPath, `${JSON.stringify(report, null, 2)}
|
|
6058
6459
|
`);
|
|
6059
6460
|
if (!global.json) info(c.dim(` wrote ${outPath}`));
|
|
6060
6461
|
}
|
|
@@ -6203,14 +6604,14 @@ function resolveUninstallTarget(name, runtimeOpt) {
|
|
|
6203
6604
|
}
|
|
6204
6605
|
return exact[0];
|
|
6205
6606
|
}
|
|
6206
|
-
const base =
|
|
6607
|
+
const base = path39.basename(wantLc);
|
|
6207
6608
|
const soft = fleet.filter((m) => {
|
|
6208
6609
|
const n = m.name.toLowerCase();
|
|
6209
|
-
return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) ||
|
|
6610
|
+
return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) || path39.basename(n) === base;
|
|
6210
6611
|
});
|
|
6211
6612
|
if (soft.length === 1) return soft[0];
|
|
6212
6613
|
if (soft.length > 1) {
|
|
6213
|
-
const baseHits = soft.filter((m) =>
|
|
6614
|
+
const baseHits = soft.filter((m) => path39.basename(m.name).toLowerCase() === base);
|
|
6214
6615
|
const pool = baseHits.length ? baseHits : soft;
|
|
6215
6616
|
const order = ["ollama", "gguf", "lm-studio"];
|
|
6216
6617
|
for (const rt of order) {
|
|
@@ -6248,11 +6649,11 @@ function printBackends(backends) {
|
|
|
6248
6649
|
}
|
|
6249
6650
|
}
|
|
6250
6651
|
function codeConfigPath(root2) {
|
|
6251
|
-
return
|
|
6652
|
+
return path39.join(root2, ".vibgrate", "code.json");
|
|
6252
6653
|
}
|
|
6253
6654
|
function loadCodeConfig(root2) {
|
|
6254
6655
|
try {
|
|
6255
|
-
const raw = JSON.parse(
|
|
6656
|
+
const raw = JSON.parse(fs10.readFileSync(codeConfigPath(root2), "utf8"));
|
|
6256
6657
|
return sanitize(raw);
|
|
6257
6658
|
} catch {
|
|
6258
6659
|
return {};
|
|
@@ -6319,7 +6720,7 @@ function contextBudgetFor(config, modelContextWindow) {
|
|
|
6319
6720
|
var MCP_CONFIG_FILES = [".cursor/mcp.json", ".vscode/mcp.json", ".mcp.json"];
|
|
6320
6721
|
function readMcpServersFile(file) {
|
|
6321
6722
|
try {
|
|
6322
|
-
const raw = JSON.parse(
|
|
6723
|
+
const raw = JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
6323
6724
|
const map = raw.mcpServers ?? raw.servers;
|
|
6324
6725
|
return map && typeof map === "object" ? parseMcpServers(map) : {};
|
|
6325
6726
|
} catch {
|
|
@@ -6330,7 +6731,7 @@ function discoverMcpServers(root2, ownServers) {
|
|
|
6330
6731
|
const servers = {};
|
|
6331
6732
|
const sources = [];
|
|
6332
6733
|
for (const rel of MCP_CONFIG_FILES) {
|
|
6333
|
-
const found = readMcpServersFile(
|
|
6734
|
+
const found = readMcpServersFile(path39.join(root2, rel));
|
|
6334
6735
|
const names = Object.keys(found);
|
|
6335
6736
|
if (names.length) {
|
|
6336
6737
|
sources.push(rel);
|
|
@@ -6349,6 +6750,9 @@ function registerCode(program) {
|
|
|
6349
6750
|
const cmd = program.command("code").description("propose a graph-grounded code edit (guided mode with no instruction; dry-run by default)").argument("[instruction...]", "what to change, in plain language (omit for guided interactive mode)").option("--provider <id>", "backend: vibgrate-relay (Vibgrate Relay), ollama, lmstudio, foundry-local, openrouter, litellm, openai, together, llama-cpp").option("--model <id>", "model id (or set VG_CODE_MODEL). No model is hard-coded \u2014 pick the current best.").option("--mode <mode>", "Code Mode: spark | flow | forge (preferred over raw model names; auto-fits when omitted and no --model)").option("--model-path <gguf>", "gguf path for --provider llama-cpp (weights are never auto-downloaded)").option("-f, --file <path>", "restrict the edit surface to this file (repeatable)", collect, []).option("-b, --budget <n>", "approx context token budget", "3000").option("--apply", "write the change (still requires --yes or an interactive confirm)").option("--yes", "consent to write / to a first-use package install, non-interactively").option("--auto", "autonomous agent: auto-approve every edit and command (use with care)").option("--max-steps <n>", "cap the number of agent steps", "24").option("--single", "one-shot planner (single edit) instead of the multi-step agent").option("--stream", "stream the model output live").option("--stream-json", "machine protocol: NDJSON agent events on stdout, approval decisions on stdin (for host UIs like the VS Code panel)").option("--session", "with --stream-json: stay open for further turns (warm graph and tools) instead of exiting after one").option("--verify [command]", "after the agent finishes, run tests and make it fix failures (uses the config testCommand if no command is given)").option("--continue", "resume the most recent session (recap + restore /undo)").option("--capsule", "use a source-bearing Task Capsule for first context (Fusion A/B; exact source ranges from the graph)").option("--no-capsule", "disable Task Capsule context even if enabled in .vibgrate/code.json").option("--security-tier <tier>", "shell isolation: L0 (host), L1 (Seatbelt/bubblewrap when available), L2/L3 reserved").option("--mock <file>", "use a scripted reply from <file> instead of a model (offline; for tests/CI)").option("-o, --out <file>", "write the JSON result to <file> (implies --json shape; for CI/benchmarks)").action(async function(instructionParts, opts) {
|
|
6350
6751
|
const global = readGlobal(this);
|
|
6351
6752
|
const instruction = (instructionParts ?? []).join(" ").trim();
|
|
6753
|
+
if (!global.local) {
|
|
6754
|
+
await ensureRelevanceModule();
|
|
6755
|
+
}
|
|
6352
6756
|
const config = loadCodeConfig(rootOf(global));
|
|
6353
6757
|
let provider = opts.provider ?? config.provider;
|
|
6354
6758
|
let model = opts.model ?? config.model;
|
|
@@ -6359,7 +6763,7 @@ function registerCode(program) {
|
|
|
6359
6763
|
try {
|
|
6360
6764
|
const { parseMode: parseMode2, resolveMode: resolveMode2, readOrchestratorState: readOrchestratorState2, activePin: activePin2 } = await import('./model-orchestrator-LHYPJBFH.js');
|
|
6361
6765
|
const { gatherSystemMemory: gatherSystemMemory2 } = await import('./local-runtime-2ED33KK6.js');
|
|
6362
|
-
const { loadGraph: loadGraph2 } = await import('./load-
|
|
6766
|
+
const { loadGraph: loadGraph2 } = await import('./load-T2FOJUPI.js');
|
|
6363
6767
|
const state = readOrchestratorState2();
|
|
6364
6768
|
const mode = parseMode2(modeFlag) ?? state.defaultMode ?? void 0;
|
|
6365
6769
|
const pin = mode ? activePin2(mode, state) : null;
|
|
@@ -6439,11 +6843,12 @@ function registerCode(program) {
|
|
|
6439
6843
|
const verifyCommand = opts.verify === true ? config.testCommand : typeof opts.verify === "string" ? opts.verify : void 0;
|
|
6440
6844
|
const verify3 = verifyCommand ? { command: verifyCommand } : void 0;
|
|
6441
6845
|
const mcp = discoverMcpServers(rootOf(global), config.mcpServers);
|
|
6442
|
-
|
|
6846
|
+
const stdinFirstTurn = !instruction && !!opts.streamJson && !!opts.session;
|
|
6847
|
+
if (!instruction && !opts.mock && !stdinFirstTurn) {
|
|
6443
6848
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
6444
6849
|
throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"` (guided mode needs an interactive terminal)');
|
|
6445
6850
|
}
|
|
6446
|
-
const { runInteractive } = await import('./interactive-
|
|
6851
|
+
const { runInteractive } = await import('./interactive-6PN237LX.js');
|
|
6447
6852
|
await runInteractive(
|
|
6448
6853
|
rootOf(global),
|
|
6449
6854
|
global,
|
|
@@ -6471,9 +6876,9 @@ function registerCode(program) {
|
|
|
6471
6876
|
);
|
|
6472
6877
|
return;
|
|
6473
6878
|
}
|
|
6474
|
-
if (!instruction) throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"`');
|
|
6879
|
+
if (!instruction && !stdinFirstTurn) throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"`');
|
|
6475
6880
|
{
|
|
6476
|
-
const { ensureCodeMap } = await import('./ensure-map-
|
|
6881
|
+
const { ensureCodeMap } = await import('./ensure-map-TAVYMPEG.js');
|
|
6477
6882
|
const mapRoot = rootOf(global);
|
|
6478
6883
|
try {
|
|
6479
6884
|
await ensureCodeMap(mapRoot, global, {
|
|
@@ -6510,7 +6915,7 @@ function registerCode(program) {
|
|
|
6510
6915
|
let mockReply;
|
|
6511
6916
|
if (opts.mock) {
|
|
6512
6917
|
try {
|
|
6513
|
-
mockReply =
|
|
6918
|
+
mockReply = fs10.readFileSync(opts.mock, "utf8");
|
|
6514
6919
|
} catch {
|
|
6515
6920
|
throw new CliError(`couldn't read the --mock file: ${opts.mock}`, ExitCode.USAGE_ERROR);
|
|
6516
6921
|
}
|
|
@@ -6558,20 +6963,20 @@ function registerCode(program) {
|
|
|
6558
6963
|
process.stdout.write(JSON.stringify(line) + "\n");
|
|
6559
6964
|
};
|
|
6560
6965
|
try {
|
|
6561
|
-
const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-
|
|
6966
|
+
const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-AMIKAASL.js');
|
|
6562
6967
|
const primarySlug = route.providers[0]?.model.includes("/") ? route.providers[0].model.split("/")[0] : route.providers[0]?.id;
|
|
6563
|
-
const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-
|
|
6564
|
-
const { nodeCodeFs } = await import('./session-
|
|
6565
|
-
const
|
|
6968
|
+
const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-A6K2MONY.js');
|
|
6969
|
+
const { nodeCodeFs } = await import('./session-SCHQQTHN.js');
|
|
6970
|
+
const readline6 = await import('readline');
|
|
6566
6971
|
const child = await import('child_process');
|
|
6567
6972
|
const { loadOrDiscoverFederation } = await import('./federation-MDQPO3PW.js');
|
|
6568
6973
|
const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
|
|
6569
6974
|
const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
|
|
6570
|
-
const { startCodeRuntimeSession } = await import('./runtime-session-
|
|
6571
|
-
const { resolveGraphBackend } = await import('./graph-backend-
|
|
6975
|
+
const { startCodeRuntimeSession } = await import('./runtime-session-QAVOPSSH.js');
|
|
6976
|
+
const { resolveGraphBackend } = await import('./graph-backend-WF4LGBGD.js');
|
|
6572
6977
|
const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
|
|
6573
6978
|
const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
|
|
6574
|
-
const { vgdRequest: vgdRequest2 } = await import('./vgd-
|
|
6979
|
+
const { vgdRequest: vgdRequest2 } = await import('./vgd-B67JCNJP.js');
|
|
6575
6980
|
const federation = loadOrDiscoverFederation(root2);
|
|
6576
6981
|
const modelProfile = mergeModelExecutionProfile(
|
|
6577
6982
|
resolveModelExecutionProfile({
|
|
@@ -6629,7 +7034,7 @@ ${res.stderr}` : ""),
|
|
|
6629
7034
|
exitCode: res.status ?? 1
|
|
6630
7035
|
};
|
|
6631
7036
|
};
|
|
6632
|
-
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-
|
|
7037
|
+
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-3TSFOL4V.js');
|
|
6633
7038
|
const vgBuiltin = createVgBuiltinMcpTools({
|
|
6634
7039
|
getGraph: () => graph,
|
|
6635
7040
|
root: root2,
|
|
@@ -6653,7 +7058,7 @@ ${res.stderr}` : ""),
|
|
|
6653
7058
|
const os4 = await import('os');
|
|
6654
7059
|
const pathMod = await import('path');
|
|
6655
7060
|
const checkpointIndex = pathMod.join(
|
|
6656
|
-
|
|
7061
|
+
fs10.mkdtempSync(pathMod.join(os4.tmpdir(), "vg-code-cp-")),
|
|
6657
7062
|
"index"
|
|
6658
7063
|
);
|
|
6659
7064
|
const gitRunner = (gitArgs, gitEnv) => {
|
|
@@ -6703,6 +7108,8 @@ ${res.stderr}` : ""),
|
|
|
6703
7108
|
capsule,
|
|
6704
7109
|
files: opts.file.length ? opts.file : void 0,
|
|
6705
7110
|
worktreeOverlay: true,
|
|
7111
|
+
// Real-model host: compaction writes a model checkpoint summary.
|
|
7112
|
+
llmCompaction: true,
|
|
6706
7113
|
// Full coding agent: free discovery by default. Capsule is context only.
|
|
6707
7114
|
advancedMode: true,
|
|
6708
7115
|
externalTools,
|
|
@@ -6717,15 +7124,16 @@ ${res.stderr}` : ""),
|
|
|
6717
7124
|
};
|
|
6718
7125
|
try {
|
|
6719
7126
|
if (opts.session) {
|
|
6720
|
-
const { runCodeStreamJsonSession } = await import('./stream-json-
|
|
6721
|
-
const { runAgent: runAgent2 } = await import('./agent-
|
|
7127
|
+
const { runCodeStreamJsonSession } = await import('./stream-json-A6K2MONY.js');
|
|
7128
|
+
const { runAgent: runAgent2 } = await import('./agent-JTA4YWMQ.js');
|
|
6722
7129
|
const {
|
|
7130
|
+
condenseSession,
|
|
6723
7131
|
loadLatestSession,
|
|
6724
7132
|
newSession,
|
|
6725
7133
|
recordTask,
|
|
6726
7134
|
saveSession,
|
|
6727
7135
|
summarizeSession
|
|
6728
|
-
} = await import('./session-store-
|
|
7136
|
+
} = await import('./session-store-F7NX7SZN.js');
|
|
6729
7137
|
const resumedRecord = opts.continue ? loadLatestSession(root2) : void 0;
|
|
6730
7138
|
let record = resumedRecord ?? newSession(
|
|
6731
7139
|
`s${Date.now().toString(36)}`,
|
|
@@ -6736,13 +7144,13 @@ ${res.stderr}` : ""),
|
|
|
6736
7144
|
const pendingTurns = [];
|
|
6737
7145
|
let wakeTurn;
|
|
6738
7146
|
let ended = false;
|
|
6739
|
-
const pushTurn = (
|
|
7147
|
+
const pushTurn = (turn) => {
|
|
6740
7148
|
if (wakeTurn) {
|
|
6741
7149
|
const wake = wakeTurn;
|
|
6742
7150
|
wakeTurn = void 0;
|
|
6743
|
-
wake(
|
|
6744
|
-
} else if (
|
|
6745
|
-
pendingTurns.push(
|
|
7151
|
+
wake(turn);
|
|
7152
|
+
} else if (turn !== null) {
|
|
7153
|
+
pendingTurns.push(turn);
|
|
6746
7154
|
}
|
|
6747
7155
|
};
|
|
6748
7156
|
const nextTurn = () => {
|
|
@@ -6763,7 +7171,7 @@ ${res.stderr}` : ""),
|
|
|
6763
7171
|
nextTurn,
|
|
6764
7172
|
bindDecisions: (session) => {
|
|
6765
7173
|
streamApprove = (action) => session.approve(action);
|
|
6766
|
-
const rl =
|
|
7174
|
+
const rl = readline6.createInterface({ input: process.stdin });
|
|
6767
7175
|
rl.on("line", (raw) => {
|
|
6768
7176
|
const msg = parseHostMessage(raw);
|
|
6769
7177
|
if (!msg) return;
|
|
@@ -6771,7 +7179,8 @@ ${res.stderr}` : ""),
|
|
|
6771
7179
|
session.submitDecision(msg.id, msg.approve);
|
|
6772
7180
|
runOutcome.noteDecision(msg.id, msg.approve);
|
|
6773
7181
|
} else if (msg.kind === "answer") session.submitAnswer(msg.id, msg.answer);
|
|
6774
|
-
else if (msg.kind === "submit")
|
|
7182
|
+
else if (msg.kind === "submit")
|
|
7183
|
+
pushTurn({ instruction: msg.instruction, agentMode: msg.agentMode, images: msg.images });
|
|
6775
7184
|
else if (msg.kind === "restore") {
|
|
6776
7185
|
void import('./checkpoint-4WFZMKEO.js').then(({ restoreCheckpoint }) => {
|
|
6777
7186
|
const outcome = restoreCheckpoint(gitRunner, {
|
|
@@ -6780,6 +7189,14 @@ ${res.stderr}` : ""),
|
|
|
6780
7189
|
});
|
|
6781
7190
|
emitStream({ event: "checkpoint-restored", commit: msg.commit, ...outcome });
|
|
6782
7191
|
});
|
|
7192
|
+
} else if (msg.kind === "compact") {
|
|
7193
|
+
record = condenseSession(record, Date.now());
|
|
7194
|
+
saveSession(root2, record);
|
|
7195
|
+
emitStream({
|
|
7196
|
+
event: "event",
|
|
7197
|
+
type: "notice",
|
|
7198
|
+
text: "Conversation compacted \u2014 earlier turns condensed into a checkpoint."
|
|
7199
|
+
});
|
|
6783
7200
|
} else if (msg.kind === "cancel") {
|
|
6784
7201
|
session.cancelTurn();
|
|
6785
7202
|
runOutcome.cancelPending();
|
|
@@ -6795,14 +7212,24 @@ ${res.stderr}` : ""),
|
|
|
6795
7212
|
pushTurn(null);
|
|
6796
7213
|
});
|
|
6797
7214
|
},
|
|
6798
|
-
runTurn: ({ instruction: turnInstruction,
|
|
7215
|
+
runTurn: ({ instruction: turnInstruction, signal, session, agentMode, auto: turnAuto, images }) => {
|
|
7216
|
+
const priorSummary = summarizeSession(record) || void 0;
|
|
6799
7217
|
turnIndex++;
|
|
6800
7218
|
runOutcome.beginRun(turnRunId(record.id, turnIndex), turnInstruction);
|
|
7219
|
+
const plan = agentMode === "plan";
|
|
7220
|
+
streamApprove = plan ? async () => false : (action) => session.approve(action);
|
|
6801
7221
|
return runAgent2({
|
|
6802
7222
|
...baseAgentOptions,
|
|
6803
7223
|
instruction: turnInstruction,
|
|
6804
7224
|
priorSummary,
|
|
7225
|
+
// Carry-over for capsule seed ranking: the previous
|
|
7226
|
+
// turn's instruction (the current turn is recorded only
|
|
7227
|
+
// in onTurnEnd, so .at(-1) is the prior ask).
|
|
7228
|
+
priorInstruction: record.tasks.at(-1)?.instruction,
|
|
6805
7229
|
signal,
|
|
7230
|
+
auto: turnAuto,
|
|
7231
|
+
plan,
|
|
7232
|
+
images,
|
|
6806
7233
|
approve: session.approve,
|
|
6807
7234
|
askUser: session.askUser,
|
|
6808
7235
|
onEvent: session.onEvent
|
|
@@ -6831,7 +7258,7 @@ ${res.stderr}` : ""),
|
|
|
6831
7258
|
emit: emitAndRecord,
|
|
6832
7259
|
bindDecisions: (session) => {
|
|
6833
7260
|
streamApprove = (action) => session.approve(action);
|
|
6834
|
-
const rl =
|
|
7261
|
+
const rl = readline6.createInterface({ input: process.stdin });
|
|
6835
7262
|
rl.on("line", (raw) => {
|
|
6836
7263
|
const msg = parseHostMessage(raw);
|
|
6837
7264
|
if (!msg) return;
|
|
@@ -6866,17 +7293,21 @@ ${res.stderr}` : ""),
|
|
|
6866
7293
|
if (!autonomous && !tty) {
|
|
6867
7294
|
throw usageError("the agent approves each edit/command \u2014 run in a terminal to approve interactively, or pass --auto to run autonomously (or --single for a one-shot diff).");
|
|
6868
7295
|
}
|
|
6869
|
-
const { agentTask } = await import('./interactive-
|
|
6870
|
-
const { nodeCodeFs } = await import('./session-
|
|
7296
|
+
const { agentTask } = await import('./interactive-6PN237LX.js');
|
|
7297
|
+
const { nodeCodeFs } = await import('./session-SCHQQTHN.js');
|
|
6871
7298
|
const { TtyPrompter } = await import('./ui-K7D3BCHD.js');
|
|
6872
7299
|
let priorSummary;
|
|
7300
|
+
let priorInstruction;
|
|
6873
7301
|
if (opts.continue) {
|
|
6874
|
-
const { loadLatestSession, summarizeSession } = await import('./session-store-
|
|
7302
|
+
const { loadLatestSession, summarizeSession } = await import('./session-store-F7NX7SZN.js');
|
|
6875
7303
|
const prev = loadLatestSession(root2);
|
|
6876
|
-
if (prev)
|
|
7304
|
+
if (prev) {
|
|
7305
|
+
priorSummary = summarizeSession(prev);
|
|
7306
|
+
priorInstruction = prev.tasks.at(-1)?.instruction;
|
|
7307
|
+
}
|
|
6877
7308
|
}
|
|
6878
7309
|
const prompter = tty ? new TtyPrompter() : void 0;
|
|
6879
|
-
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-
|
|
7310
|
+
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-3TSFOL4V.js');
|
|
6880
7311
|
const vgBuiltin = createVgBuiltinMcpTools({
|
|
6881
7312
|
getGraph: () => graph,
|
|
6882
7313
|
root: root2,
|
|
@@ -6900,11 +7331,11 @@ ${res.stderr}` : ""),
|
|
|
6900
7331
|
const { loadFederation } = await import('./federation-MDQPO3PW.js');
|
|
6901
7332
|
const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
|
|
6902
7333
|
const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
|
|
6903
|
-
const { startCodeRuntimeSession } = await import('./runtime-session-
|
|
6904
|
-
const { resolveGraphBackend } = await import('./graph-backend-
|
|
7334
|
+
const { startCodeRuntimeSession } = await import('./runtime-session-QAVOPSSH.js');
|
|
7335
|
+
const { resolveGraphBackend } = await import('./graph-backend-WF4LGBGD.js');
|
|
6905
7336
|
const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
|
|
6906
7337
|
const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
|
|
6907
|
-
const { vgdRequest: vgdRequest2 } = await import('./vgd-
|
|
7338
|
+
const { vgdRequest: vgdRequest2 } = await import('./vgd-B67JCNJP.js');
|
|
6908
7339
|
const federation = loadFederation(root2);
|
|
6909
7340
|
const modelProfile = mergeModelExecutionProfile(
|
|
6910
7341
|
resolveModelExecutionProfile({
|
|
@@ -6954,13 +7385,15 @@ ${res.stderr}` : ""),
|
|
|
6954
7385
|
stream: opts.stream,
|
|
6955
7386
|
verify: verify3 ? { command: verify3.command, maxRounds: modelProfile.maxRepairRounds } : void 0,
|
|
6956
7387
|
priorSummary,
|
|
7388
|
+
priorInstruction,
|
|
6957
7389
|
externalTools: mcpTools,
|
|
6958
7390
|
prompter,
|
|
6959
7391
|
capsule,
|
|
6960
7392
|
files: opts.file.length ? opts.file : void 0,
|
|
6961
7393
|
executionEnv,
|
|
6962
7394
|
graphBackend,
|
|
6963
|
-
modelProfile
|
|
7395
|
+
modelProfile,
|
|
7396
|
+
llmCompaction: true
|
|
6964
7397
|
});
|
|
6965
7398
|
if (global.json) json(agentResult);
|
|
6966
7399
|
if (agentResult.stopped === "error") process.exitCode = ExitCode.ERROR;
|
|
@@ -6974,7 +7407,7 @@ ${res.stderr}` : ""),
|
|
|
6974
7407
|
}
|
|
6975
7408
|
let consent = !!opts.yes;
|
|
6976
7409
|
if (opts.apply && !consent && process.stdin.isTTY && process.stdout.isTTY) {
|
|
6977
|
-
consent = await
|
|
7410
|
+
consent = await confirm2(`Apply the proposed change to ${root2}?`);
|
|
6978
7411
|
}
|
|
6979
7412
|
const onPhase = (phase, detail) => {
|
|
6980
7413
|
if (!global.json && !global.quiet) info(c.dim(` ${phase}: ${detail}`));
|
|
@@ -6996,7 +7429,7 @@ ${res.stderr}` : ""),
|
|
|
6996
7429
|
attribution: { client: "vg-code", provider: providerSlug, model: primary?.model }
|
|
6997
7430
|
});
|
|
6998
7431
|
if (opts.out) {
|
|
6999
|
-
|
|
7432
|
+
fs10.writeFileSync(opts.out, JSON.stringify(result, null, 2) + "\n");
|
|
7000
7433
|
}
|
|
7001
7434
|
if (global.json) {
|
|
7002
7435
|
json(result);
|
|
@@ -7047,7 +7480,7 @@ function resolveSecurityTierFlag(flag, fromConfig, auto) {
|
|
|
7047
7480
|
if (fromConfig) return fromConfig;
|
|
7048
7481
|
return auto ? "L1" : "L0";
|
|
7049
7482
|
}
|
|
7050
|
-
function
|
|
7483
|
+
function confirm2(question) {
|
|
7051
7484
|
return new Promise((resolve21) => {
|
|
7052
7485
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
7053
7486
|
rl.question(`${question} [y/N] `, (answer) => {
|
|
@@ -7209,7 +7642,7 @@ async function addCmd(root2, rest, opts, asJson) {
|
|
|
7209
7642
|
}
|
|
7210
7643
|
function readPackageVersion(root2) {
|
|
7211
7644
|
try {
|
|
7212
|
-
const pkg = JSON.parse(
|
|
7645
|
+
const pkg = JSON.parse(fs10__default.readFileSync(path39__default.join(root2, "package.json"), "utf8"));
|
|
7213
7646
|
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
7214
7647
|
} catch {
|
|
7215
7648
|
return void 0;
|
|
@@ -7217,7 +7650,7 @@ function readPackageVersion(root2) {
|
|
|
7217
7650
|
}
|
|
7218
7651
|
function readOptionalFile(p) {
|
|
7219
7652
|
try {
|
|
7220
|
-
const t =
|
|
7653
|
+
const t = fs10__default.readFileSync(p, "utf8");
|
|
7221
7654
|
return t.trim() ? t : void 0;
|
|
7222
7655
|
} catch {
|
|
7223
7656
|
return void 0;
|
|
@@ -7232,8 +7665,8 @@ async function publishCmd(root2, rest, opts, global, asJson) {
|
|
|
7232
7665
|
if (!parsed) throw new CliError("publishing a private library requires a DSN \u2014 run `vibgrate login` or set VIBGRATE_DSN", ExitCode.USAGE_ERROR);
|
|
7233
7666
|
const version = (opts.version || readPackageVersion(root2) || "").trim();
|
|
7234
7667
|
if (!version) throw usageError("a --version is required (or run from a package whose package.json has a version)");
|
|
7235
|
-
const readme = readOptionalFile(opts.readme ?
|
|
7236
|
-
const dts = opts.dts ? readOptionalFile(
|
|
7668
|
+
const readme = readOptionalFile(opts.readme ? path39__default.resolve(opts.readme) : path39__default.join(root2, "README.md"));
|
|
7669
|
+
const dts = opts.dts ? readOptionalFile(path39__default.resolve(opts.dts)) : void 0;
|
|
7237
7670
|
if (!readme && !dts) throw usageError("nothing to publish \u2014 no README.md found and no --dts given");
|
|
7238
7671
|
const result = await publishPrivateLibrary(
|
|
7239
7672
|
{ name, version, readme, dts, language: opts.language },
|
|
@@ -7365,8 +7798,8 @@ function registerExport(program) {
|
|
|
7365
7798
|
out(content.trimEnd());
|
|
7366
7799
|
return;
|
|
7367
7800
|
}
|
|
7368
|
-
|
|
7369
|
-
|
|
7801
|
+
fs10.mkdirSync(path39.dirname(path39.resolve(file)), { recursive: true });
|
|
7802
|
+
fs10.writeFileSync(file, content);
|
|
7370
7803
|
info(`${c.green("\u2714")} wrote ${file} ${c.dim(`(${format})`)}`);
|
|
7371
7804
|
});
|
|
7372
7805
|
applyGlobalOptions(cmd);
|
|
@@ -7376,43 +7809,43 @@ function detectFormat(file) {
|
|
|
7376
7809
|
if (lower.endsWith(".cdx.json")) return "cyclonedx";
|
|
7377
7810
|
if (lower.endsWith(".spdx.json")) return "spdx";
|
|
7378
7811
|
if (file === "-") return "json";
|
|
7379
|
-
return formatForExt(
|
|
7812
|
+
return formatForExt(path39.extname(file));
|
|
7380
7813
|
}
|
|
7381
7814
|
function registerBundle(program) {
|
|
7382
7815
|
const cmd = program.command("bundle").description("build an air-gapped bundle (grammars + graph + library catalog)").option("--offline", "offline bundle (default; present for clarity)").option("-o, --out <dir>", "output directory", "vg-bundle").action(function(opts) {
|
|
7383
7816
|
const global = readGlobal(this);
|
|
7384
7817
|
const root2 = rootOf(global);
|
|
7385
|
-
const outDir =
|
|
7818
|
+
const outDir = path39.resolve(root2, opts.out ?? "vg-bundle");
|
|
7386
7819
|
let grammarFiles;
|
|
7387
7820
|
try {
|
|
7388
7821
|
grammarFiles = resolvedGrammarFiles();
|
|
7389
7822
|
} catch {
|
|
7390
7823
|
throw new CliError("no grammars found to bundle (run a build first)", ExitCode.ERROR);
|
|
7391
7824
|
}
|
|
7392
|
-
|
|
7825
|
+
fs10.mkdirSync(path39.join(outDir, "grammars"), { recursive: true });
|
|
7393
7826
|
let grammarCount = 0;
|
|
7394
7827
|
for (const { fileName, absPath } of grammarFiles) {
|
|
7395
|
-
|
|
7828
|
+
fs10.copyFileSync(absPath, path39.join(outDir, "grammars", fileName));
|
|
7396
7829
|
grammarCount++;
|
|
7397
7830
|
}
|
|
7398
7831
|
const included = [`grammars/ (${grammarCount} .wasm)`];
|
|
7399
7832
|
const graphSrc = resolveGraphPath(root2, global.graph);
|
|
7400
|
-
if (
|
|
7401
|
-
|
|
7833
|
+
if (fs10.existsSync(graphSrc)) {
|
|
7834
|
+
fs10.copyFileSync(graphSrc, path39.join(outDir, "graph.json"));
|
|
7402
7835
|
included.push("graph.json");
|
|
7403
7836
|
} else {
|
|
7404
7837
|
const graph = loadGraphFileWithSnapshot(graphSrc);
|
|
7405
7838
|
if (graph) {
|
|
7406
|
-
|
|
7839
|
+
fs10.writeFileSync(path39.join(outDir, "graph.json"), serializeGraph(graph));
|
|
7407
7840
|
included.push("graph.json");
|
|
7408
7841
|
}
|
|
7409
7842
|
}
|
|
7410
7843
|
const cat = catalogPath(root2);
|
|
7411
|
-
if (
|
|
7412
|
-
|
|
7844
|
+
if (fs10.existsSync(cat)) {
|
|
7845
|
+
fs10.copyFileSync(cat, path39.join(outDir, "vibgrate.lib.json"));
|
|
7413
7846
|
included.push("vibgrate.lib.json");
|
|
7414
|
-
if (
|
|
7415
|
-
|
|
7847
|
+
if (fs10.existsSync(libDir(root2))) {
|
|
7848
|
+
fs10.cpSync(libDir(root2), path39.join(outDir, "lib"), { recursive: true });
|
|
7416
7849
|
included.push("lib/");
|
|
7417
7850
|
}
|
|
7418
7851
|
}
|
|
@@ -7424,15 +7857,15 @@ function registerBundle(program) {
|
|
|
7424
7857
|
included,
|
|
7425
7858
|
usage: "Run `vg build --grammars <this dir>/grammars` to build fully offline."
|
|
7426
7859
|
};
|
|
7427
|
-
|
|
7860
|
+
fs10.writeFileSync(path39.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
|
|
7428
7861
|
`);
|
|
7429
7862
|
if (global.json) {
|
|
7430
|
-
json({ out:
|
|
7863
|
+
json({ out: path39.relative(root2, outDir), ...manifest });
|
|
7431
7864
|
return;
|
|
7432
7865
|
}
|
|
7433
|
-
info(`${c.cyan("vg bundle")} \xB7 wrote ${
|
|
7866
|
+
info(`${c.cyan("vg bundle")} \xB7 wrote ${path39.relative(root2, outDir)}/`);
|
|
7434
7867
|
for (const i of included) info(` ${c.green("\u2714")} ${i}`);
|
|
7435
|
-
info(c.dim(` offline: vg build --grammars "${
|
|
7868
|
+
info(c.dim(` offline: vg build --grammars "${path39.relative(root2, outDir)}/grammars"`));
|
|
7436
7869
|
});
|
|
7437
7870
|
applyGlobalOptions(cmd);
|
|
7438
7871
|
}
|
|
@@ -7450,7 +7883,7 @@ async function runDoctor(global) {
|
|
|
7450
7883
|
const root2 = rootOf(global);
|
|
7451
7884
|
const local = global.local === true;
|
|
7452
7885
|
const graphPath = resolveGraphPath(root2, global.graph);
|
|
7453
|
-
const configFile = CONFIG_BASENAMES.find((f) =>
|
|
7886
|
+
const configFile = CONFIG_BASENAMES.find((f) => fs10.existsSync(path39.join(root2, f))) ?? null;
|
|
7454
7887
|
const graph = loadGraph(root2, graphPath);
|
|
7455
7888
|
let staleFiles = null;
|
|
7456
7889
|
if (graph) {
|
|
@@ -7476,14 +7909,14 @@ async function runDoctor(global) {
|
|
|
7476
7909
|
root: root2,
|
|
7477
7910
|
configFile,
|
|
7478
7911
|
map: {
|
|
7479
|
-
path:
|
|
7912
|
+
path: path39.relative(root2, graphPath),
|
|
7480
7913
|
built: graph !== null,
|
|
7481
7914
|
generatedAt: graph?.generatedAt ?? null,
|
|
7482
7915
|
staleFiles
|
|
7483
7916
|
},
|
|
7484
7917
|
libCatalog: {
|
|
7485
|
-
path:
|
|
7486
|
-
present:
|
|
7918
|
+
path: path39.relative(root2, catalogPath(root2)),
|
|
7919
|
+
present: fs10.existsSync(catalogPath(root2)),
|
|
7487
7920
|
libraries: libCount
|
|
7488
7921
|
},
|
|
7489
7922
|
credentials: creds,
|
|
@@ -7540,7 +7973,7 @@ async function runDoctor(global) {
|
|
|
7540
7973
|
);
|
|
7541
7974
|
info(
|
|
7542
7975
|
` host pool ${li.hostPool.size} warm session(s)` + (li.hostPool.sessions[0] ? c.dim(
|
|
7543
|
-
` \xB7 last ${
|
|
7976
|
+
` \xB7 last ${path39.basename(li.hostPool.sessions[0].modelPath)} kv=${li.hostPool.sessions[0].kvBlocks}`
|
|
7544
7977
|
) : c.dim(" \xB7 none (load via vg code / vgd host-load)"))
|
|
7545
7978
|
);
|
|
7546
7979
|
if (li.dynamicOnToken || li.dynamicCustomSampler) {
|
|
@@ -7716,7 +8149,7 @@ function registerDaemon(program) {
|
|
|
7716
8149
|
const register = cmd.command("register").description("register the current repository with a running vgd").argument("[root]", "repository root (default: cwd)").action(async function(rootArg) {
|
|
7717
8150
|
const global = readGlobal(this);
|
|
7718
8151
|
const socketPath = socketOf(this);
|
|
7719
|
-
const root2 =
|
|
8152
|
+
const root2 = path39.resolve(rootArg ?? rootOf(global));
|
|
7720
8153
|
if (!await vgdIsRunning({ socketPath })) {
|
|
7721
8154
|
throw new CliError(`vgd is not running \u2014 start it with \`vg daemon start\` (${socketPath})`, ExitCode.NOT_FOUND);
|
|
7722
8155
|
}
|
|
@@ -7752,7 +8185,7 @@ function registerDaemon(program) {
|
|
|
7752
8185
|
const federation = cmd.command("federation").description("register a multi-root federation from .vibgrate/federation.json (or primary cwd)").argument("[root]", "primary repository root (default: cwd)").action(async function(rootArg) {
|
|
7753
8186
|
const global = readGlobal(this);
|
|
7754
8187
|
const socketPath = socketOf(this);
|
|
7755
|
-
const primary =
|
|
8188
|
+
const primary = path39.resolve(rootArg ?? rootOf(global));
|
|
7756
8189
|
if (!await vgdIsRunning({ socketPath })) {
|
|
7757
8190
|
throw new CliError(`vgd is not running \u2014 start it with \`vg daemon start\` (${socketPath})`, ExitCode.NOT_FOUND);
|
|
7758
8191
|
}
|
|
@@ -7780,7 +8213,7 @@ function registerDaemon(program) {
|
|
|
7780
8213
|
const publish = cmd.command("publish").description("load the workspace code map into vgd ActiveGraph (from global/legacy store)").argument("[root]", "repository root (default: cwd)").option("--git-ref <ref>", "branch or SHA (default: detect HEAD)").action(async function(rootArg) {
|
|
7781
8214
|
const global = readGlobal(this);
|
|
7782
8215
|
const socketPath = socketOf(this);
|
|
7783
|
-
const root2 =
|
|
8216
|
+
const root2 = path39.resolve(rootArg ?? rootOf(global));
|
|
7784
8217
|
if (!await vgdIsRunning({ socketPath })) {
|
|
7785
8218
|
throw new CliError(`vgd is not running \u2014 start it with \`vg daemon ensure\` (${socketPath})`, ExitCode.NOT_FOUND);
|
|
7786
8219
|
}
|
|
@@ -7794,7 +8227,7 @@ function registerDaemon(program) {
|
|
|
7794
8227
|
throw new CliError(loaded.error, ExitCode.NOT_FOUND);
|
|
7795
8228
|
}
|
|
7796
8229
|
if (!loaded.ok) {
|
|
7797
|
-
const { loadGraph: loadGraph2 } = await import('./load-
|
|
8230
|
+
const { loadGraph: loadGraph2 } = await import('./load-T2FOJUPI.js');
|
|
7798
8231
|
const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
|
|
7799
8232
|
const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
|
|
7800
8233
|
const graph = loadGraph2(root2, global.graph);
|
|
@@ -7910,7 +8343,7 @@ function socketOf(cmd) {
|
|
|
7910
8343
|
let c2 = cmd;
|
|
7911
8344
|
while (c2) {
|
|
7912
8345
|
const opts = c2.opts();
|
|
7913
|
-
if (opts.socket) return
|
|
8346
|
+
if (opts.socket) return path39.resolve(opts.socket);
|
|
7914
8347
|
c2 = c2.parent;
|
|
7915
8348
|
}
|
|
7916
8349
|
return vgdSocketPath();
|
|
@@ -7982,7 +8415,7 @@ async function restartVgdIfRunning(socketPath = vgdSocketPath()) {
|
|
|
7982
8415
|
}
|
|
7983
8416
|
function readVgdPid() {
|
|
7984
8417
|
try {
|
|
7985
|
-
const raw =
|
|
8418
|
+
const raw = fs10.readFileSync(vgdPidPath(), "utf8").trim();
|
|
7986
8419
|
const pid = Number.parseInt(raw, 10);
|
|
7987
8420
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
7988
8421
|
} catch {
|
|
@@ -8067,7 +8500,7 @@ function registerPolicy(program) {
|
|
|
8067
8500
|
const global = readGlobal(this);
|
|
8068
8501
|
let raw;
|
|
8069
8502
|
try {
|
|
8070
|
-
raw =
|
|
8503
|
+
raw = fs10.readFileSync(file, "utf8");
|
|
8071
8504
|
} catch {
|
|
8072
8505
|
throw new CliError(`cannot read ${file}`, ExitCode.NOT_FOUND);
|
|
8073
8506
|
}
|
|
@@ -8187,11 +8620,11 @@ function registerLlmHost(program) {
|
|
|
8187
8620
|
applyGlobalOptions(cmd);
|
|
8188
8621
|
}
|
|
8189
8622
|
var initCommand = new Command("init").description("Initialize vibgrate in a project").argument("[path]", "Path to initialize", ".").option("--baseline", "Create initial baseline after init").option("--yes", "Skip confirmation prompts").action(async (targetPath, opts) => {
|
|
8190
|
-
const rootDir =
|
|
8191
|
-
const vibgrateDir2 =
|
|
8623
|
+
const rootDir = path39.resolve(targetPath);
|
|
8624
|
+
const vibgrateDir2 = path39.join(rootDir, ".vibgrate");
|
|
8192
8625
|
await ensureDir(vibgrateDir2);
|
|
8193
8626
|
console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold(".vibgrate/")} directory`);
|
|
8194
|
-
const configPath =
|
|
8627
|
+
const configPath = path39.join(rootDir, "vibgrate.config.ts");
|
|
8195
8628
|
if (await pathExists(configPath)) {
|
|
8196
8629
|
console.log(chalk7.dim(" vibgrate.config.ts already exists, skipping"));
|
|
8197
8630
|
} else {
|
|
@@ -8199,7 +8632,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
8199
8632
|
console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold("vibgrate.config.ts")}`);
|
|
8200
8633
|
}
|
|
8201
8634
|
if (opts.baseline) {
|
|
8202
|
-
const { runBaseline } = await import('./baseline-
|
|
8635
|
+
const { runBaseline } = await import('./baseline-2V7PKA7Q.js');
|
|
8203
8636
|
await runBaseline(rootDir);
|
|
8204
8637
|
}
|
|
8205
8638
|
console.log("");
|
|
@@ -8302,7 +8735,7 @@ function collectPackageImports(graph, ecosystem, pkg) {
|
|
|
8302
8735
|
};
|
|
8303
8736
|
}
|
|
8304
8737
|
async function firstReferenceLine(rootDir, relFile, identifier, readFile3) {
|
|
8305
|
-
const content = await readFile3(
|
|
8738
|
+
const content = await readFile3(path39.join(rootDir, relFile));
|
|
8306
8739
|
if (!content) return null;
|
|
8307
8740
|
const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8308
8741
|
const re = new RegExp(`\\b${escaped}\\b`);
|
|
@@ -8340,9 +8773,9 @@ function sitesFromImportFiles(files, limit = 10) {
|
|
|
8340
8773
|
}
|
|
8341
8774
|
async function defaultReadFile(absPath) {
|
|
8342
8775
|
try {
|
|
8343
|
-
const stat2 = await
|
|
8776
|
+
const stat2 = await fs24.stat(absPath);
|
|
8344
8777
|
if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES) return null;
|
|
8345
|
-
return await
|
|
8778
|
+
return await fs24.readFile(absPath, "utf8");
|
|
8346
8779
|
} catch {
|
|
8347
8780
|
return null;
|
|
8348
8781
|
}
|
|
@@ -8538,7 +8971,7 @@ var AI_ASSISTANT_DETECTORS = [
|
|
|
8538
8971
|
async function detectAiAssistant(rootDir) {
|
|
8539
8972
|
for (const detector of AI_ASSISTANT_DETECTORS) {
|
|
8540
8973
|
for (const file of detector.files) {
|
|
8541
|
-
if (await pathExists$1(
|
|
8974
|
+
if (await pathExists$1(path39.join(rootDir, file))) {
|
|
8542
8975
|
return detector.name;
|
|
8543
8976
|
}
|
|
8544
8977
|
}
|
|
@@ -8732,7 +9165,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
8732
9165
|
collectExcludes,
|
|
8733
9166
|
[]
|
|
8734
9167
|
).option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--full", "Comprehensive scan: turns on known-vulnerability detection (= --vulns) and, when a standards policy exists, a banned-dependency report \u2014 on top of drift scoring and the code map").option("--vulns", "Also scan installed dependencies for known vulnerabilities (OSV online, or advisories from --package-manifest when offline)").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--project-scan-timeout <seconds>", "Per-project scan timeout in seconds (default: 180)").option("--drift-budget <score>", "Fail if DriftScore is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").option("--repository-name <name>", "Override the repository name recorded for this scan (defaults to the directory or package.json name)").option("--force", "Always create a fresh scan ingest, even if the repository is unchanged since the last scan (skips the unchanged/reuse optimization). Used by scheduled and dashboard-triggered scans.").option("--no-graph", "Skip building the local code map (the AI/docs index) that scan produces after scoring drift").option("--quiet", "Suppress promotional output (the free-plan tracking panel and the AI Context install prompt); scan results are unaffected").action(async (targetPath, opts) => {
|
|
8735
|
-
const rootDir =
|
|
9168
|
+
const rootDir = path39.resolve(targetPath);
|
|
8736
9169
|
if (!await pathExists$1(rootDir)) {
|
|
8737
9170
|
console.error(chalk7.red(`Path does not exist: ${rootDir}`));
|
|
8738
9171
|
process.exit(1);
|
|
@@ -8740,7 +9173,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
8740
9173
|
const hasDsn = !!resolveDsn(opts.dsn);
|
|
8741
9174
|
const willPush = !opts.offline && (opts.push || hasDsn);
|
|
8742
9175
|
const noLocalArtifacts = opts.localArtifacts === false;
|
|
8743
|
-
const isFirstRun = !await pathExists$1(
|
|
9176
|
+
const isFirstRun = !await pathExists$1(path39.join(rootDir, ".vibgrate", "scan_result.json"));
|
|
8744
9177
|
let pinnedRegion;
|
|
8745
9178
|
let planTier;
|
|
8746
9179
|
let upgradeUrl;
|
|
@@ -8999,7 +9432,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
8999
9432
|
function scanStaleness(rootDir, artifactPath) {
|
|
9000
9433
|
let ref;
|
|
9001
9434
|
try {
|
|
9002
|
-
ref =
|
|
9435
|
+
ref = fs10.statSync(artifactPath).mtimeMs;
|
|
9003
9436
|
} catch {
|
|
9004
9437
|
return { stale: false };
|
|
9005
9438
|
}
|
|
@@ -9011,7 +9444,7 @@ function scanStaleness(rootDir, artifactPath) {
|
|
|
9011
9444
|
if (depth > 8 || scanned > MAX_ENTRIES2) return;
|
|
9012
9445
|
let entries;
|
|
9013
9446
|
try {
|
|
9014
|
-
entries =
|
|
9447
|
+
entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
9015
9448
|
} catch {
|
|
9016
9449
|
return;
|
|
9017
9450
|
}
|
|
@@ -9019,20 +9452,20 @@ function scanStaleness(rootDir, artifactPath) {
|
|
|
9019
9452
|
scanned++;
|
|
9020
9453
|
if (scanned > MAX_ENTRIES2) break;
|
|
9021
9454
|
if (e.isDirectory()) {
|
|
9022
|
-
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(
|
|
9455
|
+
if (!SKIP_DIRS2.has(e.name) && !e.name.startsWith(".")) walk2(path39.join(dir, e.name), depth + 1);
|
|
9023
9456
|
continue;
|
|
9024
9457
|
}
|
|
9025
9458
|
if (!WATCHED_FILES.has(e.name) && !WATCHED_EXT.test(e.name)) continue;
|
|
9026
|
-
const abs =
|
|
9459
|
+
const abs = path39.join(dir, e.name);
|
|
9027
9460
|
let mtimeMs;
|
|
9028
9461
|
try {
|
|
9029
|
-
mtimeMs =
|
|
9462
|
+
mtimeMs = fs10.statSync(abs).mtimeMs;
|
|
9030
9463
|
} catch {
|
|
9031
9464
|
continue;
|
|
9032
9465
|
}
|
|
9033
9466
|
if (mtimeMs > newest) {
|
|
9034
9467
|
newest = mtimeMs;
|
|
9035
|
-
newestChanged =
|
|
9468
|
+
newestChanged = path39.relative(rootDir, abs) || e.name;
|
|
9036
9469
|
}
|
|
9037
9470
|
}
|
|
9038
9471
|
};
|
|
@@ -9163,7 +9596,7 @@ function analyzeTree(root2, packages) {
|
|
|
9163
9596
|
if (depth > MAX_DEPTH || filesSeen >= MAX_FILES) return;
|
|
9164
9597
|
let entries;
|
|
9165
9598
|
try {
|
|
9166
|
-
entries =
|
|
9599
|
+
entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
9167
9600
|
} catch {
|
|
9168
9601
|
return;
|
|
9169
9602
|
}
|
|
@@ -9172,17 +9605,17 @@ function analyzeTree(root2, packages) {
|
|
|
9172
9605
|
if (filesSeen >= MAX_FILES) return;
|
|
9173
9606
|
if (entry.isDirectory()) {
|
|
9174
9607
|
if (SKIP_DIRS3.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
9175
|
-
walk2(
|
|
9608
|
+
walk2(path39.join(dir, entry.name), depth + 1);
|
|
9176
9609
|
continue;
|
|
9177
9610
|
}
|
|
9178
|
-
const fileEco = EXT_ECOSYSTEM[
|
|
9611
|
+
const fileEco = EXT_ECOSYSTEM[path39.extname(entry.name)];
|
|
9179
9612
|
if (!fileEco || fileEco === "unknown") continue;
|
|
9180
9613
|
filesSeen++;
|
|
9181
|
-
const full =
|
|
9614
|
+
const full = path39.join(dir, entry.name);
|
|
9182
9615
|
let content;
|
|
9183
9616
|
try {
|
|
9184
|
-
if (
|
|
9185
|
-
content =
|
|
9617
|
+
if (fs10.statSync(full).size > MAX_FILE_BYTES2) continue;
|
|
9618
|
+
content = fs10.readFileSync(full, "utf8");
|
|
9186
9619
|
} catch {
|
|
9187
9620
|
continue;
|
|
9188
9621
|
}
|
|
@@ -9493,7 +9926,7 @@ function applyPlan(rootDir, upgrades, opts = {}) {
|
|
|
9493
9926
|
}
|
|
9494
9927
|
const outcomes = effective.map((t) => {
|
|
9495
9928
|
const c2 = pmCommandFor(u.ecosystem, u.package, u.to, opts.packageManager, { workspaceRoot: t.isWorkspaceRoot });
|
|
9496
|
-
return { t, res: run(c2,
|
|
9929
|
+
return { t, res: run(c2, path39.join(rootDir, t.dir)) };
|
|
9497
9930
|
});
|
|
9498
9931
|
const failures = outcomes.filter((o) => !o.res.ok);
|
|
9499
9932
|
if (failures.length === 0) {
|
|
@@ -9522,8 +9955,8 @@ function applyPlan(rootDir, upgrades, opts = {}) {
|
|
|
9522
9955
|
return results;
|
|
9523
9956
|
}
|
|
9524
9957
|
var REGISTRY_URL = "https://registry.npmjs.org/@vibgrate%2fcli/latest";
|
|
9525
|
-
var CACHE_DIR =
|
|
9526
|
-
var CACHE_FILE =
|
|
9958
|
+
var CACHE_DIR = path39.join(os.homedir(), ".vibgrate");
|
|
9959
|
+
var CACHE_FILE = path39.join(CACHE_DIR, "update-check.json");
|
|
9527
9960
|
async function fetchLatestVersion() {
|
|
9528
9961
|
try {
|
|
9529
9962
|
const controller = new AbortController();
|
|
@@ -9550,8 +9983,8 @@ async function fetchLatestVersion() {
|
|
|
9550
9983
|
}
|
|
9551
9984
|
async function writeCache(data) {
|
|
9552
9985
|
try {
|
|
9553
|
-
await
|
|
9554
|
-
await
|
|
9986
|
+
await fs24.mkdir(CACHE_DIR, { recursive: true });
|
|
9987
|
+
await fs24.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
9555
9988
|
} catch {
|
|
9556
9989
|
}
|
|
9557
9990
|
}
|
|
@@ -9562,7 +9995,7 @@ function detectGlobalInstall(execPath = process.argv[1] || "", cwd = process.cwd
|
|
|
9562
9995
|
if (!execPath) return null;
|
|
9563
9996
|
let resolved = execPath;
|
|
9564
9997
|
try {
|
|
9565
|
-
resolved =
|
|
9998
|
+
resolved = fs10.realpathSync(execPath);
|
|
9566
9999
|
} catch {
|
|
9567
10000
|
}
|
|
9568
10001
|
const p = resolved.replace(/\\/g, "/");
|
|
@@ -9592,14 +10025,14 @@ async function findGlobalInstall(pkg = PACKAGE_NAME, run = defaultRunner) {
|
|
|
9592
10025
|
pm: "yarn",
|
|
9593
10026
|
root: () => {
|
|
9594
10027
|
const dir = tryRun("yarn global dir");
|
|
9595
|
-
return dir ?
|
|
10028
|
+
return dir ? path39.join(dir, "node_modules") : null;
|
|
9596
10029
|
}
|
|
9597
10030
|
},
|
|
9598
|
-
{ pm: "bun", root: () =>
|
|
10031
|
+
{ pm: "bun", root: () => path39.join(os.homedir(), ".bun", "install", "global", "node_modules") }
|
|
9599
10032
|
];
|
|
9600
10033
|
for (const probe of probes) {
|
|
9601
10034
|
const root2 = probe.root();
|
|
9602
|
-
if (root2 && await pathExists(
|
|
10035
|
+
if (root2 && await pathExists(path39.join(root2, pkg))) return probe.pm;
|
|
9603
10036
|
}
|
|
9604
10037
|
return null;
|
|
9605
10038
|
}
|
|
@@ -9618,15 +10051,15 @@ function getGlobalUpdateCommand(pm, pkg, version) {
|
|
|
9618
10051
|
}
|
|
9619
10052
|
}
|
|
9620
10053
|
async function detectPackageManager(cwd) {
|
|
9621
|
-
if (await pathExists(
|
|
9622
|
-
if (await pathExists(
|
|
9623
|
-
if (await pathExists(
|
|
10054
|
+
if (await pathExists(path39.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
10055
|
+
if (await pathExists(path39.join(cwd, "bun.lockb"))) return "bun";
|
|
10056
|
+
if (await pathExists(path39.join(cwd, "yarn.lock"))) return "yarn";
|
|
9624
10057
|
return "npm";
|
|
9625
10058
|
}
|
|
9626
10059
|
async function detectWorkspaceRoot(cwd) {
|
|
9627
|
-
if (await pathExists(
|
|
10060
|
+
if (await pathExists(path39.join(cwd, "pnpm-workspace.yaml"))) return true;
|
|
9628
10061
|
try {
|
|
9629
|
-
const pkgPath =
|
|
10062
|
+
const pkgPath = path39.join(cwd, "package.json");
|
|
9630
10063
|
const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
|
|
9631
10064
|
const pkg = JSON.parse(raw);
|
|
9632
10065
|
return pkg.workspaces != null;
|
|
@@ -9651,7 +10084,7 @@ function getInstallCommand(pm, pkg, version, isDev, opts = {}) {
|
|
|
9651
10084
|
}
|
|
9652
10085
|
async function getLocalDependencyState(cwd, pkg = PACKAGE_NAME) {
|
|
9653
10086
|
try {
|
|
9654
|
-
const pkgPath =
|
|
10087
|
+
const pkgPath = path39.join(cwd, "package.json");
|
|
9655
10088
|
const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
|
|
9656
10089
|
const parsed = JSON.parse(raw);
|
|
9657
10090
|
if (parsed.devDependencies?.[pkg]) return "dev";
|
|
@@ -9820,13 +10253,13 @@ function collectCandidates(artifact) {
|
|
|
9820
10253
|
return out2;
|
|
9821
10254
|
}
|
|
9822
10255
|
var defaultFixScanner = async (rootDir) => {
|
|
9823
|
-
const scratch =
|
|
10256
|
+
const scratch = path39.join(os.tmpdir(), `vg-fix-scan-${process.pid}.json`);
|
|
9824
10257
|
const scanOpts = { vibgrateVersion: VERSION, format: "json", out: scratch, quiet: true, concurrency: 8 };
|
|
9825
10258
|
const advanced = await loadAdvancedScanHook();
|
|
9826
10259
|
return runCoreScan(rootDir, scanOpts, advanced);
|
|
9827
10260
|
};
|
|
9828
10261
|
async function loadArtifact(rootDir, inFile, scan = defaultFixScanner) {
|
|
9829
|
-
const artifactPath =
|
|
10262
|
+
const artifactPath = path39.isAbsolute(inFile) ? inFile : path39.join(rootDir, inFile);
|
|
9830
10263
|
if (await pathExists(artifactPath)) {
|
|
9831
10264
|
const staleness = scanStaleness(rootDir, artifactPath);
|
|
9832
10265
|
if (!staleness.stale) {
|
|
@@ -9853,7 +10286,7 @@ async function repositoryIdentity(rootDir, override) {
|
|
|
9853
10286
|
}
|
|
9854
10287
|
}
|
|
9855
10288
|
var fixCommand = new Command("fix").description("Get ranked, risk-tiered upgrade plans from the hosted planner and optionally apply them (prompts before changing files; --yes applies non-interactively, --dry-run previews, --no-apply only prints). Requires login/DSN.").argument("[path]", "Path to analyse", ".").option("--in <file>", "Scan artifact to read", ".vibgrate/scan_result.json").option("--format <format>", "Output format (text|json|md)", "text").option("--dsn <dsn>", 'DSN token (or use VIBGRATE_DSN env / "vg login")').option("--region <region>", "Override data residency region (us, eu)").option("--repository-name <name>", "Override the repository name recorded for this plan").option("--plan <tier>", "Apply a specific plan non-interactively (safe|balanced|aggressive)").option("--yes", "Apply the recommended plan without prompting").option("--dry-run", "Show what would change without applying").option("--no-apply", "Only print the plans; never modify the project").option("--fail-on-vulns <severity>", "Exit non-zero if the recommended plan leaves an advisory at/above this severity unresolved (low|moderate|high|critical)").action(async (targetPath, opts) => {
|
|
9856
|
-
const rootDir =
|
|
10289
|
+
const rootDir = path39.resolve(targetPath);
|
|
9857
10290
|
if (!await pathExists(rootDir)) {
|
|
9858
10291
|
console.error(chalk7.red(`Path does not exist: ${rootDir}`));
|
|
9859
10292
|
process.exit(1);
|
|
@@ -10014,7 +10447,7 @@ function emit(report, format) {
|
|
|
10014
10447
|
}
|
|
10015
10448
|
}
|
|
10016
10449
|
function relProjectDir(rootDir, projectPath) {
|
|
10017
|
-
const rel =
|
|
10450
|
+
const rel = path39.isAbsolute(projectPath) ? path39.relative(rootDir, projectPath) : projectPath;
|
|
10018
10451
|
return rel === "" ? "." : rel;
|
|
10019
10452
|
}
|
|
10020
10453
|
async function buildTargetResolver(rootDir, artifact) {
|
|
@@ -10849,7 +11282,7 @@ function formatMarkdown(artifact) {
|
|
|
10849
11282
|
|
|
10850
11283
|
// src/reporting/commands/report.ts
|
|
10851
11284
|
var reportCommand = new Command("report").description("Generate a drift report from a scan artifact").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--format <format>", "Output format (md|text|json)", "text").action(async (opts) => {
|
|
10852
|
-
const artifactPath =
|
|
11285
|
+
const artifactPath = path39.resolve(opts.in);
|
|
10853
11286
|
if (!await pathExists(artifactPath)) {
|
|
10854
11287
|
console.error(chalk7.red(`Artifact not found: ${artifactPath}`));
|
|
10855
11288
|
console.error(chalk7.dim('Run "vibgrate scan" first to generate a scan artifact.'));
|
|
@@ -10898,8 +11331,8 @@ function openUrl(url) {
|
|
|
10898
11331
|
// src/reporting/commands/login.ts
|
|
10899
11332
|
var delay = (ms) => new Promise((resolve21) => setTimeout(resolve21, ms));
|
|
10900
11333
|
function isInside(dir, file) {
|
|
10901
|
-
const rel =
|
|
10902
|
-
return !!rel && !rel.startsWith("..") && !
|
|
11334
|
+
const rel = path39.relative(dir, file);
|
|
11335
|
+
return !!rel && !rel.startsWith("..") && !path39.isAbsolute(rel);
|
|
10903
11336
|
}
|
|
10904
11337
|
var loginCommand = new Command("login").description("Authenticate the CLI with your Vibgrate workspace via the browser").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", `Data residency region (${availableRegionIds().join(", ")})`, "us").option("--no-browser", "Do not attempt to open a browser automatically").option(
|
|
10905
11338
|
"--local",
|
|
@@ -11154,7 +11587,7 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
|
|
|
11154
11587
|
try {
|
|
11155
11588
|
const statements = [];
|
|
11156
11589
|
if (opts.from) {
|
|
11157
|
-
const fromPath =
|
|
11590
|
+
const fromPath = path39.resolve(opts.from);
|
|
11158
11591
|
if (!await pathExists(fromPath)) {
|
|
11159
11592
|
process.stderr.write(chalk7.red(`Statements file not found: ${fromPath}
|
|
11160
11593
|
`));
|
|
@@ -11172,9 +11605,9 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
|
|
|
11172
11605
|
});
|
|
11173
11606
|
const json2 = JSON.stringify(doc, null, 2);
|
|
11174
11607
|
if (opts.out) {
|
|
11175
|
-
await writeTextFile(
|
|
11608
|
+
await writeTextFile(path39.resolve(opts.out), json2 + "\n");
|
|
11176
11609
|
process.stderr.write(
|
|
11177
|
-
chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${
|
|
11610
|
+
chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${path39.resolve(opts.out)}
|
|
11178
11611
|
`
|
|
11179
11612
|
);
|
|
11180
11613
|
} else {
|
|
@@ -11357,7 +11790,7 @@ function formatDeltaText(base, current) {
|
|
|
11357
11790
|
return lines.join("\n");
|
|
11358
11791
|
}
|
|
11359
11792
|
async function readArtifactOrExit(filePath) {
|
|
11360
|
-
const absolutePath =
|
|
11793
|
+
const absolutePath = path39.resolve(filePath);
|
|
11361
11794
|
if (!await pathExists(absolutePath)) {
|
|
11362
11795
|
console.error(chalk7.red(`Artifact not found: ${absolutePath}`));
|
|
11363
11796
|
process.exit(1);
|
|
@@ -11374,7 +11807,7 @@ var exportCommand = new Command("export").description("Export scan artifact as S
|
|
|
11374
11807
|
const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
|
|
11375
11808
|
const body = JSON.stringify(sbom, null, 2);
|
|
11376
11809
|
if (opts.out) {
|
|
11377
|
-
await writeTextFile(
|
|
11810
|
+
await writeTextFile(path39.resolve(opts.out), body);
|
|
11378
11811
|
console.log(chalk7.green("\u2714") + ` SBOM written to ${opts.out}`);
|
|
11379
11812
|
} else {
|
|
11380
11813
|
console.log(body);
|
|
@@ -11385,7 +11818,7 @@ var deltaCommand = new Command("delta").description("Show SBOM delta between two
|
|
|
11385
11818
|
const current = await readArtifactOrExit(opts.to);
|
|
11386
11819
|
const report = formatDeltaText(base, current);
|
|
11387
11820
|
if (opts.out) {
|
|
11388
|
-
await writeTextFile(
|
|
11821
|
+
await writeTextFile(path39.resolve(opts.out), report);
|
|
11389
11822
|
console.log(chalk7.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
|
|
11390
11823
|
} else {
|
|
11391
11824
|
console.log(report);
|
|
@@ -11516,20 +11949,20 @@ function resolveRegime(id) {
|
|
|
11516
11949
|
return regime;
|
|
11517
11950
|
}
|
|
11518
11951
|
function evidenceDir(root2) {
|
|
11519
|
-
return
|
|
11952
|
+
return path39.join(root2, ".vibgrate", "evidence");
|
|
11520
11953
|
}
|
|
11521
11954
|
function orgPath(root2) {
|
|
11522
|
-
return
|
|
11955
|
+
return path39.join(evidenceDir(root2), "org.json");
|
|
11523
11956
|
}
|
|
11524
11957
|
function productsPath(root2) {
|
|
11525
|
-
return
|
|
11958
|
+
return path39.join(evidenceDir(root2), "products.json");
|
|
11526
11959
|
}
|
|
11527
11960
|
function releasesDir(root2) {
|
|
11528
|
-
return
|
|
11961
|
+
return path39.join(evidenceDir(root2), "releases");
|
|
11529
11962
|
}
|
|
11530
11963
|
function releasePath(root2, productId, version) {
|
|
11531
11964
|
const safe = `${productId}@${version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
|
|
11532
|
-
return
|
|
11965
|
+
return path39.join(releasesDir(root2), `${safe}.json`);
|
|
11533
11966
|
}
|
|
11534
11967
|
var DEFAULT_ORG = {
|
|
11535
11968
|
responsiblePersons: [],
|
|
@@ -11560,7 +11993,7 @@ async function freezeRelease(root2, release) {
|
|
|
11560
11993
|
const p = releasePath(root2, release.productId, release.version);
|
|
11561
11994
|
if (await pathExists(p)) {
|
|
11562
11995
|
throw new CliError(
|
|
11563
|
-
`release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${
|
|
11996
|
+
`release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${path39.relative(root2, p)} only if it was frozen in error`,
|
|
11564
11997
|
ExitCode.USAGE_ERROR
|
|
11565
11998
|
);
|
|
11566
11999
|
}
|
|
@@ -11574,7 +12007,7 @@ async function loadReleases(root2, productId) {
|
|
|
11574
12007
|
const files = (await readdir(dir)).filter((f) => f.endsWith(".json")).sort();
|
|
11575
12008
|
const releases = [];
|
|
11576
12009
|
for (const f of files) {
|
|
11577
|
-
const rel = await readJsonFile(
|
|
12010
|
+
const rel = await readJsonFile(path39.join(dir, f));
|
|
11578
12011
|
if (!productId || rel.productId === productId) releases.push(rel);
|
|
11579
12012
|
}
|
|
11580
12013
|
return releases;
|
|
@@ -12269,19 +12702,19 @@ function verifyEvidenceEnvelope(env, opts = {}) {
|
|
|
12269
12702
|
var DEFAULT_KEY = "attest-key.pem";
|
|
12270
12703
|
function resolveSigningKey(root2, explicit) {
|
|
12271
12704
|
const chosen = process.env.VG_ATTEST_KEY;
|
|
12272
|
-
const keyPath = chosen ?
|
|
12705
|
+
const keyPath = chosen ? path39.resolve(chosen) : path39.join(root2, ".vibgrate", DEFAULT_KEY);
|
|
12273
12706
|
let minted = false;
|
|
12274
|
-
if (!
|
|
12707
|
+
if (!fs10.existsSync(keyPath)) {
|
|
12275
12708
|
if (chosen) throw new CliError(`signing key not found: ${chosen}`, ExitCode.USAGE_ERROR);
|
|
12276
12709
|
const kp = generateKeypair();
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12710
|
+
fs10.mkdirSync(path39.dirname(keyPath), { recursive: true });
|
|
12711
|
+
fs10.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
|
|
12712
|
+
fs10.writeFileSync(`${keyPath}.pub`, kp.publicPem);
|
|
12280
12713
|
minted = true;
|
|
12281
12714
|
}
|
|
12282
12715
|
let key2;
|
|
12283
12716
|
try {
|
|
12284
|
-
key2 = crypto3.createPrivateKey(
|
|
12717
|
+
key2 = crypto3.createPrivateKey(fs10.readFileSync(keyPath, "utf8"));
|
|
12285
12718
|
} catch {
|
|
12286
12719
|
throw new CliError(`could not read an Ed25519 private key from ${keyPath}`, ExitCode.USAGE_ERROR);
|
|
12287
12720
|
}
|
|
@@ -12292,16 +12725,16 @@ function resolveSigningKey(root2, explicit) {
|
|
|
12292
12725
|
}
|
|
12293
12726
|
function writeBundle(input) {
|
|
12294
12727
|
const dir = input.outDir;
|
|
12295
|
-
|
|
12296
|
-
const write = (rel, data) =>
|
|
12728
|
+
fs10.mkdirSync(path39.join(dir, "inputs", "releases"), { recursive: true });
|
|
12729
|
+
const write = (rel, data) => fs10.writeFileSync(path39.join(dir, rel), `${JSON.stringify(data, null, 2)}
|
|
12297
12730
|
`);
|
|
12298
12731
|
write("result.json", input.result);
|
|
12299
|
-
write(
|
|
12732
|
+
write(path39.join("inputs", "advisory.json"), input.advisory);
|
|
12300
12733
|
for (const rel of input.releases) {
|
|
12301
12734
|
const safe = `${rel.productId}@${rel.version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
|
|
12302
|
-
write(
|
|
12735
|
+
write(path39.join("inputs", "releases", `${safe}.json`), rel);
|
|
12303
12736
|
}
|
|
12304
|
-
write(
|
|
12737
|
+
write(path39.join("inputs", "datapack.lock"), { dataPackVersion: input.result.meta.dataPackVersion, kernelVersion: input.result.meta.kernelVersion });
|
|
12305
12738
|
write("provenance.json", {
|
|
12306
12739
|
method: "deterministic",
|
|
12307
12740
|
modelDrafted: [],
|
|
@@ -12317,13 +12750,13 @@ function writeBundle(input) {
|
|
|
12317
12750
|
disclaimer: input.regime.disclaimer
|
|
12318
12751
|
});
|
|
12319
12752
|
if (input.envelope) {
|
|
12320
|
-
|
|
12753
|
+
fs10.writeFileSync(path39.join(dir, "evidence.intoto.jsonl"), `${JSON.stringify(input.envelope)}
|
|
12321
12754
|
`);
|
|
12322
12755
|
}
|
|
12323
12756
|
if (input.timestampToken) {
|
|
12324
|
-
|
|
12757
|
+
fs10.writeFileSync(path39.join(dir, "timestamp.tsr"), input.timestampToken);
|
|
12325
12758
|
}
|
|
12326
|
-
|
|
12759
|
+
fs10.writeFileSync(path39.join(dir, "VERIFY.md"), verifyDoc(input));
|
|
12327
12760
|
return dir;
|
|
12328
12761
|
}
|
|
12329
12762
|
function verifyDoc(input) {
|
|
@@ -12384,13 +12817,13 @@ function undeterminedFields(result, regime) {
|
|
|
12384
12817
|
return gaps.filter((g) => regime.submission.requires.some((r) => g.startsWith(r)) || g.startsWith("exposure") || g.startsWith("member"));
|
|
12385
12818
|
}
|
|
12386
12819
|
async function recordDrill(root2, record) {
|
|
12387
|
-
const dir =
|
|
12388
|
-
const p =
|
|
12820
|
+
const dir = path39.join(evidenceDir(root2), "drills");
|
|
12821
|
+
const p = path39.join(dir, `${record.drillId}.json`);
|
|
12389
12822
|
await writeJsonFile(p, record);
|
|
12390
12823
|
return p;
|
|
12391
12824
|
}
|
|
12392
12825
|
async function hasRecentDrill(root2, asOf) {
|
|
12393
|
-
const dir =
|
|
12826
|
+
const dir = path39.join(evidenceDir(root2), "drills");
|
|
12394
12827
|
if (!await pathExists(dir)) return void 0;
|
|
12395
12828
|
const { readdir } = await import('fs/promises');
|
|
12396
12829
|
const { readJsonFile: readJsonFile2 } = await import('./fs-4G5A65KS.js');
|
|
@@ -12398,7 +12831,7 @@ async function hasRecentDrill(root2, asOf) {
|
|
|
12398
12831
|
if (files.length === 0) return void 0;
|
|
12399
12832
|
const cutoff = new Date(new Date(asOf).getTime() - 90 * 24 * 3600 * 1e3).toISOString();
|
|
12400
12833
|
for (const f of files) {
|
|
12401
|
-
const rec = await readJsonFile2(
|
|
12834
|
+
const rec = await readJsonFile2(path39.join(dir, f));
|
|
12402
12835
|
if (rec.ranAt >= cutoff) return true;
|
|
12403
12836
|
}
|
|
12404
12837
|
return false;
|
|
@@ -12478,7 +12911,7 @@ function formatRegimeList() {
|
|
|
12478
12911
|
|
|
12479
12912
|
// src/reporting/commands/evidence/index.ts
|
|
12480
12913
|
function root(opts) {
|
|
12481
|
-
return
|
|
12914
|
+
return path39.resolve(opts.cwd ?? process.cwd());
|
|
12482
12915
|
}
|
|
12483
12916
|
function slug(name) {
|
|
12484
12917
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "product";
|
|
@@ -12566,7 +12999,7 @@ var releaseCmd = new Command("release").description("Freeze a shipped release in
|
|
|
12566
12999
|
const release = await buildRelease({
|
|
12567
13000
|
productId: product.id,
|
|
12568
13001
|
version,
|
|
12569
|
-
from:
|
|
13002
|
+
from: path39.resolve(rootDir, opts.from),
|
|
12570
13003
|
shipDate: opts.shipDate,
|
|
12571
13004
|
buildId: opts.buildId,
|
|
12572
13005
|
artefactDigest: opts.digest,
|
|
@@ -12574,7 +13007,7 @@ var releaseCmd = new Command("release").description("Freeze a shipped release in
|
|
|
12574
13007
|
frozenAt: nowIso()
|
|
12575
13008
|
});
|
|
12576
13009
|
const p = await freezeRelease(rootDir, release);
|
|
12577
|
-
console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${
|
|
13010
|
+
console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${path39.relative(rootDir, p)}`);
|
|
12578
13011
|
});
|
|
12579
13012
|
async function runExposure(rootDir, vuln, opts) {
|
|
12580
13013
|
const org = await loadOrg(rootDir);
|
|
@@ -12626,7 +13059,7 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
|
|
|
12626
13059
|
console.log("\n" + buildPack(result, regime, org, stage));
|
|
12627
13060
|
}
|
|
12628
13061
|
if (opts.bundle) {
|
|
12629
|
-
const dir =
|
|
13062
|
+
const dir = path39.resolve(rootDir, opts.bundle, `evidence-${result.meta.evidenceId}`);
|
|
12630
13063
|
let envelope;
|
|
12631
13064
|
if (opts.sign !== false) {
|
|
12632
13065
|
const { key: key2, minted } = resolveSigningKey(rootDir);
|
|
@@ -12634,7 +13067,7 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
|
|
|
12634
13067
|
envelope = signEvidenceStatement(buildEvidenceStatement(result, VERSION), key2);
|
|
12635
13068
|
}
|
|
12636
13069
|
writeBundle({ outDir: dir, result, advisory, releases, regime, envelope, timestampToken, cliVersion: VERSION });
|
|
12637
|
-
console.error(chalk7.green("\u2714") + ` evidence bundle written to ${
|
|
13070
|
+
console.error(chalk7.green("\u2714") + ` evidence bundle written to ${path39.relative(rootDir, dir)}${envelope ? " (signed)" : " (unsigned)"}${timestampToken ? " \xB7 RFC 3161" : ""}`);
|
|
12638
13071
|
}
|
|
12639
13072
|
process.exitCode = exposureExit(result.overallStatus);
|
|
12640
13073
|
});
|
|
@@ -12666,7 +13099,7 @@ var packCmd = new Command("pack").description("Build the submission pack a human
|
|
|
12666
13099
|
const stage = opts.stage ?? regime.clocks[0]?.stage ?? "early-warning";
|
|
12667
13100
|
const md = buildPack(result, regime, org, stage);
|
|
12668
13101
|
if (opts.out) {
|
|
12669
|
-
|
|
13102
|
+
fs10.writeFileSync(path39.resolve(rootDir, opts.out), md);
|
|
12670
13103
|
console.error(chalk7.green("\u2714") + ` pack written to ${opts.out}`);
|
|
12671
13104
|
} else {
|
|
12672
13105
|
console.log(md);
|
|
@@ -12708,20 +13141,20 @@ var drillCmd = new Command("drill").description("Timed dry-run of the determinat
|
|
|
12708
13141
|
}
|
|
12709
13142
|
});
|
|
12710
13143
|
var verifyCmd = new Command("verify").description("Verify an evidence bundle offline (no Vibgrate needed)").argument("<bundle>", "Path to an evidence bundle directory or evidence.intoto.jsonl").option("--pub <file>", "Public key PEM to pin the signer (establish trust)").action((bundlePath, opts) => {
|
|
12711
|
-
const abs =
|
|
12712
|
-
const envPath =
|
|
12713
|
-
if (!
|
|
12714
|
-
const envelope = JSON.parse(
|
|
12715
|
-
const resultPath =
|
|
12716
|
-
const result =
|
|
12717
|
-
const publicKeyPem = opts.pub ?
|
|
13144
|
+
const abs = path39.resolve(bundlePath);
|
|
13145
|
+
const envPath = fs10.statSync(abs).isDirectory() ? path39.join(abs, "evidence.intoto.jsonl") : abs;
|
|
13146
|
+
if (!fs10.existsSync(envPath)) throw new CliError(`no evidence.intoto.jsonl at ${bundlePath}`, ExitCode.NOT_FOUND);
|
|
13147
|
+
const envelope = JSON.parse(fs10.readFileSync(envPath, "utf8").trim().split("\n")[0]);
|
|
13148
|
+
const resultPath = path39.join(path39.dirname(envPath), "result.json");
|
|
13149
|
+
const result = fs10.existsSync(resultPath) ? JSON.parse(fs10.readFileSync(resultPath, "utf8")) : void 0;
|
|
13150
|
+
const publicKeyPem = opts.pub ? fs10.readFileSync(path39.resolve(opts.pub), "utf8") : void 0;
|
|
12718
13151
|
const v = verifyEvidenceEnvelope(envelope, { publicKeyPem, result });
|
|
12719
13152
|
const color = v.status === "verified" ? chalk7.green : v.status === "failed" ? chalk7.red : chalk7.yellow;
|
|
12720
13153
|
console.log(" " + color(v.status.toUpperCase()) + ` ${v.reason}`);
|
|
12721
13154
|
if (v.evidenceId) console.log(" " + chalk7.dim(`evidence ${v.evidenceId} \xB7 regime ${v.regime} \xB7 advisory ${v.advisoryId} \xB7 ${v.overallStatus}`));
|
|
12722
|
-
const tsrPath =
|
|
12723
|
-
if (
|
|
12724
|
-
const t = verifyTimestamp(
|
|
13155
|
+
const tsrPath = path39.join(path39.dirname(envPath), "timestamp.tsr");
|
|
13156
|
+
if (fs10.existsSync(tsrPath) && result) {
|
|
13157
|
+
const t = verifyTimestamp(fs10.readFileSync(tsrPath), exposureSubjectDigest(result));
|
|
12725
13158
|
const tcolor = t.imprintMatches ? chalk7.green : chalk7.red;
|
|
12726
13159
|
console.log(" " + tcolor("TIMESTAMP") + ` ${t.reason}`);
|
|
12727
13160
|
}
|
|
@@ -12788,12 +13221,12 @@ var exportCmd = new Command("export").description("Air-gap bundle of all evidenc
|
|
|
12788
13221
|
const releases = [];
|
|
12789
13222
|
for (const list of releasesByProduct.values()) releases.push(...list);
|
|
12790
13223
|
const report = computeReadiness({ regime, org, products, releasesByProduct, recentDrill: await hasRecentDrill(rootDir, today()) });
|
|
12791
|
-
const dir =
|
|
12792
|
-
await writeJsonFile(
|
|
12793
|
-
await writeJsonFile(
|
|
12794
|
-
for (const r of releases) await writeJsonFile(
|
|
12795
|
-
await writeJsonFile(
|
|
12796
|
-
console.error(chalk7.green("\u2714") + ` exported evidence state to ${
|
|
13224
|
+
const dir = path39.resolve(rootDir, opts.out);
|
|
13225
|
+
await writeJsonFile(path39.join(dir, "org.json"), org);
|
|
13226
|
+
await writeJsonFile(path39.join(dir, "products.json"), products);
|
|
13227
|
+
for (const r of releases) await writeJsonFile(path39.join(dir, "releases", `${r.productId}@${r.version}.json`.replace(/[^A-Za-z0-9._@-]/g, "_")), r);
|
|
13228
|
+
await writeJsonFile(path39.join(dir, "readiness.json"), report);
|
|
13229
|
+
console.error(chalk7.green("\u2714") + ` exported evidence state to ${path39.relative(rootDir, dir)} (${products.length} products, ${releases.length} frozen releases)`);
|
|
12797
13230
|
});
|
|
12798
13231
|
var pushCmd = new Command("push").description("Push the product registry (and an optional exposure result) to Vibgrate Cloud").option(...cwdOption).option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env / `vg login`)").option("--regime <id>", "Reporting regime").option("--result <file>", "An exposure result.json (or a bundle dir) to include").option("--signed", "Mark the included exposure result as signed").option("--strict", "Fail on push errors").action(async (opts) => {
|
|
12799
13232
|
const rootDir = root(opts);
|
|
@@ -12819,10 +13252,10 @@ var pushCmd = new Command("push").description("Push the product registry (and an
|
|
|
12819
13252
|
}));
|
|
12820
13253
|
let exposure;
|
|
12821
13254
|
if (opts.result) {
|
|
12822
|
-
const raw =
|
|
12823
|
-
const resultPath =
|
|
12824
|
-
if (!
|
|
12825
|
-
exposure = JSON.parse(
|
|
13255
|
+
const raw = path39.resolve(rootDir, opts.result);
|
|
13256
|
+
const resultPath = fs10.existsSync(raw) && fs10.statSync(raw).isDirectory() ? path39.join(raw, "result.json") : raw;
|
|
13257
|
+
if (!fs10.existsSync(resultPath)) throw new CliError(`no result.json at ${opts.result}`, ExitCode.NOT_FOUND);
|
|
13258
|
+
exposure = JSON.parse(fs10.readFileSync(resultPath, "utf8"));
|
|
12826
13259
|
}
|
|
12827
13260
|
const payload = { schemaVersion: "evidence-push-1", regime: regime.id, generatedAt: nowIso(), signed: Boolean(opts.signed), products: productPayload, exposure };
|
|
12828
13261
|
const body = JSON.stringify(payload);
|
|
@@ -12880,6 +13313,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
12880
13313
|
"serve",
|
|
12881
13314
|
"lsp",
|
|
12882
13315
|
"doctor",
|
|
13316
|
+
"module",
|
|
12883
13317
|
"daemon",
|
|
12884
13318
|
"policy",
|
|
12885
13319
|
"llm-host",
|
|
@@ -12887,6 +13321,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
12887
13321
|
"uninstall",
|
|
12888
13322
|
"share",
|
|
12889
13323
|
"watch",
|
|
13324
|
+
"hook",
|
|
12890
13325
|
"help",
|
|
12891
13326
|
// Drift-reporting verbs (merged from the Vibgrate CLI).
|
|
12892
13327
|
"init",
|
|
@@ -12926,6 +13361,8 @@ ${aliasNote}Docs: https://vibgrate.com/help`;
|
|
|
12926
13361
|
registerServe(program);
|
|
12927
13362
|
registerLsp(program);
|
|
12928
13363
|
registerInstall(program);
|
|
13364
|
+
registerModule(program);
|
|
13365
|
+
registerHook(program);
|
|
12929
13366
|
registerShare(program);
|
|
12930
13367
|
registerWatch(program);
|
|
12931
13368
|
registerTests(program);
|
|
@@ -13012,7 +13449,7 @@ function dispatch(argv, cwd) {
|
|
|
13012
13449
|
}
|
|
13013
13450
|
const looksLikeQuestion = /\s/.test(first) || first.endsWith("?");
|
|
13014
13451
|
if (looksLikeQuestion) return ["ask", ...args];
|
|
13015
|
-
if (
|
|
13452
|
+
if (fs10.existsSync(path39.resolve(cwd, first))) {
|
|
13016
13453
|
return [hasBuildOnlyFlag(args) ? "build" : "scan", ...args];
|
|
13017
13454
|
}
|
|
13018
13455
|
const moved = MOVED_COMMANDS[first];
|
|
@@ -13057,9 +13494,9 @@ async function main(argv = process.argv) {
|
|
|
13057
13494
|
}
|
|
13058
13495
|
function readCwd(raw) {
|
|
13059
13496
|
const i = raw.findIndex((a) => a === "-C" || a === "--cwd");
|
|
13060
|
-
if (i >= 0 && raw[i + 1]) return
|
|
13497
|
+
if (i >= 0 && raw[i + 1]) return path39.resolve(raw[i + 1]);
|
|
13061
13498
|
const eq = raw.find((a) => a.startsWith("--cwd="));
|
|
13062
|
-
if (eq) return
|
|
13499
|
+
if (eq) return path39.resolve(eq.slice("--cwd=".length));
|
|
13063
13500
|
return process.cwd();
|
|
13064
13501
|
}
|
|
13065
13502
|
function handleError(err) {
|