@vibgrate/cli 2026.817.1 → 2026.817.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/DOCS.md +61 -0
  2. package/README.md +1 -0
  3. package/dist/agent-UQC7QN77.js +27 -0
  4. package/dist/{agent-CUYQKC6S.js.map → agent-UQC7QN77.js.map} +1 -1
  5. package/dist/{approvals-IPMUDEDV.js → approvals-ASPJQUZK.js} +6 -5
  6. package/dist/{approvals-IPMUDEDV.js.map → approvals-ASPJQUZK.js.map} +1 -1
  7. package/dist/baseline-FIV6PR2M.js +9 -0
  8. package/dist/{baseline-NZFGLMER.js.map → baseline-FIV6PR2M.js.map} +1 -1
  9. package/dist/{chunk-4W5SYK2M.js → chunk-2FEB5KJS.js} +3 -3
  10. package/dist/{chunk-4W5SYK2M.js.map → chunk-2FEB5KJS.js.map} +1 -1
  11. package/dist/{chunk-MIYXVY57.js → chunk-3FMRFECC.js} +4 -4
  12. package/dist/{chunk-MIYXVY57.js.map → chunk-3FMRFECC.js.map} +1 -1
  13. package/dist/{chunk-NQUWC2IA.js → chunk-3H3SVJUY.js} +4 -4
  14. package/dist/{chunk-NQUWC2IA.js.map → chunk-3H3SVJUY.js.map} +1 -1
  15. package/dist/{chunk-UBEVY5UT.js → chunk-3NJUOIZQ.js} +5 -5
  16. package/dist/{chunk-UBEVY5UT.js.map → chunk-3NJUOIZQ.js.map} +1 -1
  17. package/dist/{chunk-3GZSDY35.js → chunk-5OL2TSHK.js} +4 -4
  18. package/dist/{chunk-3GZSDY35.js.map → chunk-5OL2TSHK.js.map} +1 -1
  19. package/dist/{chunk-2BURPMYA.js → chunk-6K3XK7C5.js} +69 -976
  20. package/dist/chunk-6K3XK7C5.js.map +1 -0
  21. package/dist/{chunk-2GSMCOIC.js → chunk-AXZZ5CCG.js} +4 -4
  22. package/dist/{chunk-2GSMCOIC.js.map → chunk-AXZZ5CCG.js.map} +1 -1
  23. package/dist/{chunk-NHB53JTV.js → chunk-C3LMWZKF.js} +3 -3
  24. package/dist/{chunk-NHB53JTV.js.map → chunk-C3LMWZKF.js.map} +1 -1
  25. package/dist/{chunk-FD2HPGX2.js → chunk-DVW3SVCW.js} +9 -8
  26. package/dist/chunk-DVW3SVCW.js.map +1 -0
  27. package/dist/{chunk-RJCRWQNS.js → chunk-E2FAQREZ.js} +164 -221
  28. package/dist/chunk-E2FAQREZ.js.map +1 -0
  29. package/dist/{chunk-45AKSS4A.js → chunk-EXV4QSAD.js} +4 -4
  30. package/dist/{chunk-45AKSS4A.js.map → chunk-EXV4QSAD.js.map} +1 -1
  31. package/dist/{chunk-OWSXI3PJ.js → chunk-GOGMS3ST.js} +5 -5
  32. package/dist/{chunk-OWSXI3PJ.js.map → chunk-GOGMS3ST.js.map} +1 -1
  33. package/dist/{chunk-YL4OIUA5.js → chunk-K6DXETMC.js} +11 -11
  34. package/dist/{chunk-YL4OIUA5.js.map → chunk-K6DXETMC.js.map} +1 -1
  35. package/dist/{chunk-NKRWKCME.js → chunk-KJT2R6WW.js} +4 -4
  36. package/dist/{chunk-NKRWKCME.js.map → chunk-KJT2R6WW.js.map} +1 -1
  37. package/dist/{chunk-FMS6G67U.js → chunk-KNGAZDGF.js} +5 -5
  38. package/dist/{chunk-FMS6G67U.js.map → chunk-KNGAZDGF.js.map} +1 -1
  39. package/dist/{chunk-JHLCKMKF.js → chunk-LHKQGVRF.js} +9 -8
  40. package/dist/chunk-LHKQGVRF.js.map +1 -0
  41. package/dist/{chunk-A7EKFLAN.js → chunk-M7IO3DXH.js} +9 -8
  42. package/dist/chunk-M7IO3DXH.js.map +1 -0
  43. package/dist/{chunk-QPXVTQ7O.js → chunk-MVPRPSXU.js} +7 -4
  44. package/dist/chunk-MVPRPSXU.js.map +1 -0
  45. package/dist/{chunk-DT4KIVRI.js → chunk-NG4KIJW6.js} +34 -2
  46. package/dist/chunk-NG4KIJW6.js.map +1 -0
  47. package/dist/{chunk-K267DHCF.js → chunk-O2DL2Q55.js} +6 -6
  48. package/dist/{chunk-K267DHCF.js.map → chunk-O2DL2Q55.js.map} +1 -1
  49. package/dist/chunk-O3LABX6I.js +43 -0
  50. package/dist/chunk-O3LABX6I.js.map +1 -0
  51. package/dist/chunk-PRG2ULPK.js +6 -0
  52. package/dist/{chunk-PV76EO4G.js.map → chunk-PRG2ULPK.js.map} +1 -1
  53. package/dist/{chunk-ZSECAGSU.js → chunk-Q3M6K3HY.js} +3 -3
  54. package/dist/{chunk-ZSECAGSU.js.map → chunk-Q3M6K3HY.js.map} +1 -1
  55. package/dist/{chunk-2TJ5EY4I.js → chunk-Q4G2BWE3.js} +4 -4
  56. package/dist/{chunk-2TJ5EY4I.js.map → chunk-Q4G2BWE3.js.map} +1 -1
  57. package/dist/chunk-T7RSQ5WD.js +227 -0
  58. package/dist/chunk-T7RSQ5WD.js.map +1 -0
  59. package/dist/{chunk-RQAKBRAV.js → chunk-TVDMNIUT.js} +3 -3
  60. package/dist/{chunk-RQAKBRAV.js.map → chunk-TVDMNIUT.js.map} +1 -1
  61. package/dist/{chunk-ANYL7SUI.js → chunk-WAKP7SK7.js} +4584 -976
  62. package/dist/chunk-WAKP7SK7.js.map +1 -0
  63. package/dist/{chunk-ENFSSUNK.js → chunk-WVMOGIPC.js} +5 -43
  64. package/dist/chunk-WVMOGIPC.js.map +1 -0
  65. package/dist/{chunk-6NOGNOE4.js → chunk-XLXJC7H3.js} +141 -13
  66. package/dist/chunk-XLXJC7H3.js.map +1 -0
  67. package/dist/cli.js +1476 -575
  68. package/dist/cli.js.map +1 -1
  69. package/dist/{daemon-J2HX4GY3.js → daemon-FPTRHO5N.js} +11 -10
  70. package/dist/{daemon-J2HX4GY3.js.map → daemon-FPTRHO5N.js.map} +1 -1
  71. package/dist/embed-worker-main.js +5 -4
  72. package/dist/embed-worker-main.js.map +1 -1
  73. package/dist/ensure-map-342G2IV3.js +33 -0
  74. package/dist/{ensure-map-5C6KG66X.js.map → ensure-map-342G2IV3.js.map} +1 -1
  75. package/dist/{fs-NTQUFCXV.js → fs-TLPOWC2D.js} +3 -3
  76. package/dist/fs-TLPOWC2D.js.map +1 -0
  77. package/dist/graph-backend-RNWQBT3P.js +15 -0
  78. package/dist/{graph-backend-HH5IIQ55.js.map → graph-backend-RNWQBT3P.js.map} +1 -1
  79. package/dist/index.d.ts +22 -3
  80. package/dist/index.js +18 -17
  81. package/dist/index.js.map +1 -1
  82. package/dist/{interactive-FXPAZAUJ.js → interactive-SR7TMF6M.js} +28 -27
  83. package/dist/interactive-SR7TMF6M.js.map +1 -0
  84. package/dist/load-AYFECIQB.js +11 -0
  85. package/dist/{load-OR2XVGAV.js.map → load-AYFECIQB.js.map} +1 -1
  86. package/dist/parse-worker.d.ts +1 -1
  87. package/dist/parse-worker.js +5 -2
  88. package/dist/parse-worker.js.map +1 -1
  89. package/dist/{run-outcome-M4546HLJ.js → run-outcome-YQQJG3LW.js} +7 -6
  90. package/dist/run-outcome-YQQJG3LW.js.map +1 -0
  91. package/dist/runtime-session-CNJ4DAV5.js +22 -0
  92. package/dist/{runtime-session-6KKMFS6Q.js.map → runtime-session-CNJ4DAV5.js.map} +1 -1
  93. package/dist/session-H34HUV2Y.js +17 -0
  94. package/dist/{session-HCFJUSKN.js.map → session-H34HUV2Y.js.map} +1 -1
  95. package/dist/{stream-json-4DL65D6U.js → stream-json-55YDNAFX.js} +15 -14
  96. package/dist/stream-json-55YDNAFX.js.map +1 -0
  97. package/dist/{types-8SYlXhzm.d.ts → types-DHqH0XIE.d.ts} +11 -0
  98. package/dist/version-WSE3QOPK.js +4 -0
  99. package/dist/{version-UPIC3F4P.js.map → version-WSE3QOPK.js.map} +1 -1
  100. package/dist/{vg-mcp-bridge-FZGX6DYO.js → vg-mcp-bridge-2CNLHNEL.js} +13 -12
  101. package/dist/vg-mcp-bridge-2CNLHNEL.js.map +1 -0
  102. package/dist/{vgd-QR26FAZD.js → vgd-URUZQORN.js} +10 -9
  103. package/dist/vgd-URUZQORN.js.map +1 -0
  104. package/package.json +7 -7
  105. package/dist/agent-CUYQKC6S.js +0 -26
  106. package/dist/baseline-NZFGLMER.js +0 -9
  107. package/dist/chunk-2BURPMYA.js.map +0 -1
  108. package/dist/chunk-6NOGNOE4.js.map +0 -1
  109. package/dist/chunk-A7EKFLAN.js.map +0 -1
  110. package/dist/chunk-ANYL7SUI.js.map +0 -1
  111. package/dist/chunk-DT4KIVRI.js.map +0 -1
  112. package/dist/chunk-ENFSSUNK.js.map +0 -1
  113. package/dist/chunk-FD2HPGX2.js.map +0 -1
  114. package/dist/chunk-JHLCKMKF.js.map +0 -1
  115. package/dist/chunk-PV76EO4G.js +0 -6
  116. package/dist/chunk-QPXVTQ7O.js.map +0 -1
  117. package/dist/chunk-RJCRWQNS.js.map +0 -1
  118. package/dist/chunk-RXP66R2E.js +0 -39
  119. package/dist/chunk-RXP66R2E.js.map +0 -1
  120. package/dist/ensure-map-5C6KG66X.js +0 -32
  121. package/dist/fs-NTQUFCXV.js.map +0 -1
  122. package/dist/graph-backend-HH5IIQ55.js +0 -14
  123. package/dist/interactive-FXPAZAUJ.js.map +0 -1
  124. package/dist/load-OR2XVGAV.js +0 -10
  125. package/dist/run-outcome-M4546HLJ.js.map +0 -1
  126. package/dist/runtime-session-6KKMFS6Q.js +0 -21
  127. package/dist/session-HCFJUSKN.js +0 -16
  128. package/dist/stream-json-4DL65D6U.js.map +0 -1
  129. package/dist/version-UPIC3F4P.js +0 -4
  130. package/dist/vg-mcp-bridge-FZGX6DYO.js.map +0 -1
  131. package/dist/vgd-QR26FAZD.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import { fetchCatalog, resolveProviders } from './chunk-NHB53JTV.js';
3
- import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-45AKSS4A.js';
4
- import { loadAdvancedScanHook, baselineCommand } from './chunk-2BURPMYA.js';
5
- import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, ProgressBar, printLogo, runBuild } from './chunk-JHLCKMKF.js';
6
- export { runBuild } from './chunk-JHLCKMKF.js';
7
- import { restartVgdIfRunning, stopVgd, registerDaemon, rootOf, requireGraph } from './chunk-FMS6G67U.js';
2
+ import { fetchCatalog, resolveProviders } from './chunk-C3LMWZKF.js';
3
+ import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-EXV4QSAD.js';
4
+ import { loadAdvancedScanHook, baselineCommand } from './chunk-6K3XK7C5.js';
5
+ import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, ProgressBar, printLogo, runBuild } from './chunk-LHKQGVRF.js';
6
+ export { runBuild } from './chunk-LHKQGVRF.js';
7
+ import { restartVgdIfRunning, stopVgd, registerDaemon, rootOf, requireGraph } from './chunk-KNGAZDGF.js';
8
8
  import { readGlobal, applyGlobalOptions } from './chunk-OZ3IFXRD.js';
9
9
  import { disableColor, info, c, json, out } from './chunk-CT5BS56U.js';
10
- import './chunk-OWSXI3PJ.js';
10
+ import './chunk-GOGMS3ST.js';
11
11
  import './chunk-IS7VTH2Q.js';
12
12
  import { defaultProcessEndpoint, startProcessHostServer, resolveInferenceIsolation } from './chunk-JFE7XCAE.js';
13
13
  import { clearHostSessionPool, acquireHostSession, generateOnSession, listHostSessions, hostSessionPoolSize } from './chunk-3LOWJSAF.js';
14
- import { runCodeSession } from './chunk-UBEVY5UT.js';
15
- import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-YL4OIUA5.js';
14
+ import { runCodeSession } from './chunk-3NJUOIZQ.js';
15
+ import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-K6DXETMC.js';
16
16
  import './chunk-543E6WDJ.js';
17
- import './chunk-ZSECAGSU.js';
18
- import './chunk-RQAKBRAV.js';
17
+ import './chunk-Q3M6K3HY.js';
18
+ import './chunk-TVDMNIUT.js';
19
19
  import './chunk-RBIRXQR6.js';
20
20
  import { buildIdentifierTrieFromGraph } from './chunk-5HYCB55P.js';
21
21
  import { parseReasoningEffort, summarizeDiffs, ScriptedProvider } from './chunk-74UBGF4P.js';
@@ -26,39 +26,40 @@ import { init_resolve_ollama, gatherSystemMemory, resolveOllamaBinary, hasOllama
26
26
  import { readOrchestratorState, writeOrchestratorState, parseMode, setDefaultMode, recommendMode, activePin, resolveMode, providerForBackend, QUALIFIED_PACKS, pinPack, unpinPack, buildOrchestratorStatus } from './chunk-NZELMYT4.js';
27
27
  import { fmt, estimateModelBytes } from './chunk-7N4Y3E47.js';
28
28
  import './chunk-IZDH67DX.js';
29
- import { serveStdio, isRelevantChange, exportGraph, GraphSource, createServer, formatForExt, RefreshScheduler } from './chunk-A7EKFLAN.js';
30
- import { refreshIfStale } from './chunk-K267DHCF.js';
31
- import { driftCount, ASSISTANTS, detectAssistants, isAssistantInstalled, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, TOOLCHAIN_GRAMMARS, resolveToolchainGrammar, buildGraph, writeSnapshot, probeFreshness, discover, assistantById, SMALL_REPO_FILES, isSkippedDirName, SKIP_FILES, SKIP_DIRS } from './chunk-RJCRWQNS.js';
32
- 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-FD2HPGX2.js';
29
+ import { serveStdio, isRelevantChange, exportGraph, GraphSource, createServer, formatForExt, RefreshScheduler } from './chunk-M7IO3DXH.js';
30
+ import { refreshIfStale } from './chunk-O2DL2Q55.js';
31
+ import { driftCount, ASSISTANTS, detectAssistants, isAssistantInstalled, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, buildGraph, writeSnapshot, probeFreshness, discover, assistantById, SMALL_REPO_FILES, isSkippedDirName, SKIP_FILES, SKIP_DIRS } from './chunk-E2FAQREZ.js';
32
+ 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-DVW3SVCW.js';
33
33
  import { resolveIngestHost, dashHostForIngestHost, availableRegionIds, ingestHostForRegionId } from './chunk-HTLDXEHW.js';
34
- 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-ANYL7SUI.js';
34
+ import { writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, refineArchitectureWithGraph, refineArchitectureWithAstRoles, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, perDependencyDrift } from './chunk-WAKP7SK7.js';
35
35
  import { CliError, ExitCode, usageError, engineUnavailable } from './chunk-MS6V5XKB.js';
36
36
  import './chunk-VIWZ4QT7.js';
37
- import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-NQUWC2IA.js';
37
+ import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-3H3SVJUY.js';
38
38
  import { resolveOne } from './chunk-J7LKEB2R.js';
39
- import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-NKRWKCME.js';
40
- import { queryGraphSemantic, queryGraph, indexFor, impactOf } from './chunk-2TJ5EY4I.js';
39
+ import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-KJT2R6WW.js';
40
+ import { queryGraphSemantic, queryGraph, indexFor, impactOf } from './chunk-Q4G2BWE3.js';
41
41
  import { discoverModels } from './chunk-CQDUBAQY.js';
42
42
  import { freeBytesOnVolume, weightStoreDir, listCachedWeights, ensureWeightCached, catalogIntegrityReport } from './chunk-OV4KGXPD.js';
43
- import { loadGraph, loadGraphPreferIndex, SCHEMA_VERSION } from './chunk-MIYXVY57.js';
43
+ import { loadGraph, loadGraphPreferIndex, SCHEMA_VERSION } from './chunk-3FMRFECC.js';
44
44
  import './chunk-5I4VSBWQ.js';
45
- import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-DT4KIVRI.js';
45
+ import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-NG4KIJW6.js';
46
46
  import { ensureDir, pathExists, readJsonFile, writeTextFile, writeJsonFile } from './chunk-CRAMOEBE.js';
47
47
  import { init_cli_invocation, resolveCliInvocation, NPX_INVOCATION } from './chunk-EZ7UC5Y5.js';
48
- import { resolvedGrammarFiles, grammarSetVersion } from './chunk-6NOGNOE4.js';
49
- import './chunk-RXP66R2E.js';
50
- import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, unavailableMessage, countPending, modelCacheInfo, embeddingsPath, clearModelCache, withTimeout } from './chunk-2GSMCOIC.js';
51
- import { cacheDir } from './chunk-QPXVTQ7O.js';
52
- import { resetRelevanceProviderCache, loadRelevanceProvider, relevanceModuleDir, resolveGraphPath, analyzeQuestion, loadTopicTags, renderReport, loadGraphFileWithSnapshot, serializeGraph, stableStringify, writeArtifacts, vibgrateDir, mapFileExists, parseGraph, ensureVibgrateGitignore } from './chunk-ENFSSUNK.js';
53
- import { VERSION } from './chunk-PV76EO4G.js';
48
+ import { resolvedGrammarFiles, TOOLCHAIN_GRAMMARS, resolveToolchainGrammar, grammarSetVersion, fileRolesFromParseCache } from './chunk-XLXJC7H3.js';
49
+ import './chunk-T7RSQ5WD.js';
50
+ import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, unavailableMessage, countPending, modelCacheInfo, embeddingsPath, clearModelCache, withTimeout } from './chunk-AXZZ5CCG.js';
51
+ import { resetRelevanceProviderCache, loadRelevanceProvider, relevanceModuleDir, resolveGraphPath, analyzeQuestion, loadTopicTags, renderReport, loadGraphFileWithSnapshot, writeArtifacts, vibgrateDir, mapFileExists, ensureVibgrateGitignore } from './chunk-WVMOGIPC.js';
52
+ import { cacheDir } from './chunk-MVPRPSXU.js';
53
+ import { serializeGraph, stableStringify, parseGraph } from './chunk-O3LABX6I.js';
54
+ import { VERSION } from './chunk-PRG2ULPK.js';
54
55
  import './chunk-BWPBB45M.js';
55
56
  import { canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
56
57
  import './chunk-BPF3YX2U.js';
57
58
  import { __toCommonJS } from './chunk-Z6ZWNWWR.js';
58
59
  import * as fs14 from 'fs';
59
60
  import fs14__default, { realpathSync } from 'fs';
60
- import * as path43 from 'path';
61
- import path43__default from 'path';
61
+ import * as path46 from 'path';
62
+ import path46__default from 'path';
62
63
  import { fileURLToPath, pathToFileURL } from 'url';
63
64
  import { Command, CommanderError, Option } from 'commander';
64
65
  import { execSync, spawn, spawnSync, execFileSync } from 'child_process';
@@ -71,6 +72,7 @@ import * as crypto3 from 'crypto';
71
72
  import { randomUUID, createHash } from 'crypto';
72
73
  import chalk7 from 'chalk';
73
74
  import * as fs28 from 'fs/promises';
75
+ import { parse, stringify } from 'yaml';
74
76
 
75
77
  init_cli_invocation();
76
78
  function registerStatus(program) {
@@ -80,19 +82,19 @@ function registerStatus(program) {
80
82
  applyGlobalOptions(cmd);
81
83
  }
82
84
  async function runStatus(global) {
83
- const root2 = path43.resolve(global.cwd ?? ".");
84
- const graphPath = resolveGraphPath(root2, global.graph);
85
- const graph = loadGraph(root2, graphPath);
86
- const hasCache = fs14.existsSync(path43.join(cacheDir(root2), "parse-cache.json"));
85
+ const root3 = path46.resolve(global.cwd ?? ".");
86
+ const graphPath = resolveGraphPath(root3, global.graph);
87
+ const graph = loadGraph(root3, graphPath);
88
+ const hasCache = fs14.existsSync(path46.join(cacheDir(root3), "parse-cache.json"));
87
89
  let stale = null;
88
90
  let staleExact = false;
89
91
  if (graph) {
90
- const probe = probeFreshness(root2);
92
+ const probe = probeFreshness(root3);
91
93
  if (probe) {
92
94
  stale = driftCount(probe.drift);
93
95
  staleExact = true;
94
96
  } else {
95
- const files = discover({ root: root2 });
97
+ const files = discover({ root: root3 });
96
98
  let changed = 0;
97
99
  const graphFiles = new Set(graph.nodes.filter((n) => n.kind === "file").map((n) => n.file));
98
100
  const currentFiles = new Set(files.map((f) => f.rel));
@@ -103,8 +105,8 @@ async function runStatus(global) {
103
105
  }
104
106
  if (global.json) {
105
107
  json({
106
- root: path43.relative(process.cwd(), root2) || ".",
107
- graphPath: path43.relative(root2, graphPath),
108
+ root: path46.relative(process.cwd(), root3) || ".",
109
+ graphPath: path46.relative(root3, graphPath),
108
110
  built: graph !== null,
109
111
  generatedAt: graph?.generatedAt ?? null,
110
112
  counts: graph?.meta.counts ?? null,
@@ -119,11 +121,11 @@ async function runStatus(global) {
119
121
  return;
120
122
  }
121
123
  if (!graph) {
122
- info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(path43.relative(root2, graphPath))}`);
124
+ info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(path46.relative(root3, graphPath))}`);
123
125
  info(` run ${c.bold("vg")} to build one`);
124
126
  return;
125
127
  }
126
- info(`${c.cyan("vg")} \xB7 ${path43.relative(root2, graphPath)}`);
128
+ info(`${c.cyan("vg")} \xB7 ${path46.relative(root3, graphPath)}`);
127
129
  info(` generated ${graph.generatedAt}`);
128
130
  info(
129
131
  ` nodes ${c.bold(String(graph.meta.counts.nodes))} edges ${c.bold(
@@ -137,17 +139,17 @@ async function runStatus(global) {
137
139
  } else {
138
140
  info(c.dim(" up to date"));
139
141
  }
140
- if (vibgrateDir(root2)) ;
142
+ if (vibgrateDir(root3)) ;
141
143
  }
142
144
 
143
145
  // src/commands/ask.ts
144
146
  function registerAsk(program) {
145
147
  const cmd = program.command("ask").description("ask the map \u2014 returns a budget-bounded context block").argument("<question...>", 'your question (a bare quoted string also works: vg "\u2026")').option("-b, --budget <n>", "approx token budget for the answer", "2000").option("--no-semantic", "lexical only (skip the local-embedding pass)").option("--no-refresh", "answer from the map as built \u2014 skip the auto-rebuild when files change").action(async function(question, opts) {
146
148
  const global = readGlobal(this);
147
- const root2 = rootOf(global);
149
+ const root3 = rootOf(global);
148
150
  if (opts.refresh !== false && !global.graph) {
149
- const graphPath = resolveGraphPath(root2, global.graph);
150
- const refreshed = await refreshIfStale(root2, { graphPath });
151
+ const graphPath = resolveGraphPath(root3, global.graph);
152
+ const refreshed = await refreshIfStale(root3, { graphPath });
151
153
  if (refreshed.status === "refreshed" && !global.json) {
152
154
  const n = driftCount(refreshed.drift);
153
155
  info(c.dim(` map refreshed \u2014 ${n} file(s) drifted (${(refreshed.ms / 1e3).toFixed(2)}s)`));
@@ -163,9 +165,9 @@ function registerAsk(program) {
163
165
  let mode = "lexical";
164
166
  let note = "";
165
167
  const relevance = await analyzeQuestion(q);
166
- const topicTags = relevance ? await loadTopicTags(graph, root2, resolveGraphPath(root2, global.graph)) : null;
168
+ const topicTags = relevance ? await loadTopicTags(graph, root3, resolveGraphPath(root3, global.graph)) : null;
167
169
  if (wantSemantic) {
168
- const firstRun = !embeddingsCached(root2, resolveEmbedModel());
170
+ const firstRun = !embeddingsCached(root3, resolveEmbedModel());
169
171
  if (!global.json && firstRun) {
170
172
  info(c.dim(" preparing semantic search (first use embeds the map; cached, resumable & offline after)\u2026"));
171
173
  }
@@ -180,7 +182,7 @@ function registerAsk(program) {
180
182
  });
181
183
  if (embedder) {
182
184
  const bar = !global.json && firstRun ? new ProgressBar(c.dim("embedding")) : void 0;
183
- const vectors = await getNodeEmbeddings(graph, embedder, root2, bar ? (d, t) => bar.update(d, t) : void 0);
185
+ const vectors = await getNodeEmbeddings(graph, embedder, root3, bar ? (d, t) => bar.update(d, t) : void 0);
184
186
  bar?.done();
185
187
  result = await queryGraphSemantic(graph, q, { budget, embedder, nodeVectors: vectors, relevance, topicTags });
186
188
  mode = `semantic (${embedder.id})`;
@@ -196,7 +198,7 @@ function registerAsk(program) {
196
198
  if (global.client) {
197
199
  const files = new Set(result.matches.map((m) => m.node.file).filter(Boolean));
198
200
  recordCliCall(
199
- root2,
201
+ root3,
200
202
  {
201
203
  tool: CLI_TOOL_ALIASES.ask,
202
204
  client: global.client,
@@ -237,11 +239,11 @@ function crashedMessage(signal) {
237
239
  return `semantic search is disabled: its native backend (onnxruntime-node, via the vendored fastembed path) crashed with ${signal} \u2014 using fast lexical search instead. This usually means the installed native binaries are broken (an interrupted install, or a platform without prebuilt CPU binaries). Reinstall to repair \u2014 ${envPrefix}npm install -g --allow-scripts=@vibgrate/cli,onnxruntime-node,msgpackr-extract @vibgrate/cli \u2014 or run with --local to stay lexical quietly.`;
238
240
  }
239
241
  function defaultCacheDir() {
240
- const base = process.env.XDG_CACHE_HOME || path43.join(os.homedir(), ".cache");
241
- return path43.join(base, "vibgrate", "models");
242
+ const base = process.env.XDG_CACHE_HOME || path46.join(os.homedir(), ".cache");
243
+ return path46.join(base, "vibgrate", "models");
242
244
  }
243
245
  function verdictPath(dir) {
244
- return path43.join(dir, `embed-health-${process.platform}-${process.arch}.json`);
246
+ return path46.join(dir, `embed-health-${process.platform}-${process.arch}.json`);
245
247
  }
246
248
  function readVerdict(dir) {
247
249
  try {
@@ -261,12 +263,12 @@ function writeVerdict(dir, verdict) {
261
263
  }
262
264
  }
263
265
  function probeModuleUrl(from = import.meta.url) {
264
- const dir = path43.dirname(fileURLToPath(from));
266
+ const dir = path46.dirname(fileURLToPath(from));
265
267
  for (const name of ["embeddings.js", "index.js"]) {
266
- const candidate = path43.join(dir, name);
268
+ const candidate = path46.join(dir, name);
267
269
  if (fs14.existsSync(candidate)) return pathToFileURL(candidate).href;
268
270
  }
269
- return pathToFileURL(path43.join(dir, "index.js")).href;
271
+ return pathToFileURL(path46.join(dir, "index.js")).href;
270
272
  }
271
273
  function defaultRunProbe(timeoutMs) {
272
274
  const moduleUrl = probeModuleUrl();
@@ -285,7 +287,7 @@ function defaultRunProbe(timeoutMs) {
285
287
  })
286
288
  .catch(() => process.stdout.write('{"ok":false}'));
287
289
  `;
288
- return new Promise((resolve21) => {
290
+ return new Promise((resolve23) => {
289
291
  const child = spawn(process.execPath, ["--input-type=module", "-e", script], {
290
292
  stdio: ["ignore", "pipe", "ignore"],
291
293
  // The probe must never inherit a debugger port or loader flags that would
@@ -298,7 +300,7 @@ function defaultRunProbe(timeoutMs) {
298
300
  if (settled) return;
299
301
  settled = true;
300
302
  clearTimeout(timer);
301
- resolve21({ signal, status, stdout });
303
+ resolve23({ signal, status, stdout });
302
304
  };
303
305
  const timer = setTimeout(() => {
304
306
  child.kill("SIGTERM");
@@ -359,7 +361,7 @@ function registerEmbed(program) {
359
361
  if (opts.where) return showWhere(rootOf(global), modelId, global.json);
360
362
  if (opts.clear) return doClear(global.json);
361
363
  const { graph } = requireGraph(global);
362
- const root2 = rootOf(global);
364
+ const root3 = rootOf(global);
363
365
  const bg = opts.bg === true;
364
366
  const speak = !bg && !global.json;
365
367
  if (global.local) {
@@ -367,7 +369,7 @@ function registerEmbed(program) {
367
369
  else if (speak) info(c.dim("vg embed \xB7 semantic is off (--local) \u2014 lexical search only"));
368
370
  return;
369
371
  }
370
- if (countPending(graph, root2, modelId) === 0) {
372
+ if (countPending(graph, root3, modelId) === 0) {
371
373
  if (global.json) json({ embedded: 0, pending: 0, upToDate: true, model: modelId, semantic: true });
372
374
  else if (speak) info(`${c.cyan("vg embed")} \xB7 semantic index already up to date`);
373
375
  return;
@@ -400,19 +402,19 @@ function registerEmbed(program) {
400
402
  }
401
403
  return;
402
404
  }
403
- const pending = countPending(graph, root2, modelId);
405
+ const pending = countPending(graph, root3, modelId);
404
406
  const bar = speak ? new ProgressBar(c.dim("embedding")) : void 0;
405
- await getNodeEmbeddings(graph, embedder, root2, bar ? (d, t) => bar.update(d, t) : void 0);
407
+ await getNodeEmbeddings(graph, embedder, root3, bar ? (d, t) => bar.update(d, t) : void 0);
406
408
  bar?.done();
407
409
  if (global.json) json({ embedded: pending, model: modelId, semantic: true });
408
410
  else if (speak) info(`${c.green("\u2714")} vg embed \xB7 semantic index ready (~${pending} node(s) \xB7 model ${modelId})`);
409
411
  });
410
412
  applyGlobalOptions(cmd);
411
413
  }
412
- function showWhere(root2, modelId, asJson) {
414
+ function showWhere(root3, modelId, asJson) {
413
415
  const m = modelCacheInfo(modelId);
414
- const repoCached = embeddingsCached(root2, modelId);
415
- const repoPath = embeddingsPath(root2);
416
+ const repoCached = embeddingsCached(root3, modelId);
417
+ const repoPath = embeddingsPath(root3);
416
418
  if (asJson) {
417
419
  json({
418
420
  model: modelId,
@@ -1010,8 +1012,8 @@ function isLoopbackOrigin(origin) {
1010
1012
  var PUBLISH_INTERVAL_MS = 2e3;
1011
1013
  var KEEPALIVE_MS = 1e4;
1012
1014
  var STALE_MS = 6e4;
1013
- function liveStatsDir(root2) {
1014
- return path43.join(cacheDir(root2), "serve-live");
1015
+ function liveStatsDir(root3) {
1016
+ return path46.join(cacheDir(root3), "serve-live");
1015
1017
  }
1016
1018
  function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid, signal) => process.kill(pid, signal), selfPid = process.pid) {
1017
1019
  let entries;
@@ -1025,7 +1027,7 @@ function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid
1025
1027
  if (!/^serve-\d+\.json$/.test(name)) continue;
1026
1028
  let parsed;
1027
1029
  try {
1028
- parsed = JSON.parse(fs14.readFileSync(path43.join(dir, name), "utf8"));
1030
+ parsed = JSON.parse(fs14.readFileSync(path46.join(dir, name), "utf8"));
1029
1031
  } catch {
1030
1032
  continue;
1031
1033
  }
@@ -1047,7 +1049,7 @@ var LiveStatsBus = class {
1047
1049
  this.dir = dir;
1048
1050
  this.stats = stats;
1049
1051
  this.pid = pid;
1050
- this.file = path43.join(dir, `serve-${this.pid}.json`);
1052
+ this.file = path46.join(dir, `serve-${this.pid}.json`);
1051
1053
  }
1052
1054
  dir;
1053
1055
  stats;
@@ -1100,7 +1102,7 @@ var LiveStatsBus = class {
1100
1102
  const out2 = [];
1101
1103
  for (const name of entries.sort()) {
1102
1104
  if (!/^serve-\d+\.json$/.test(name)) continue;
1103
- const file = path43.join(this.dir, name);
1105
+ const file = path46.join(this.dir, name);
1104
1106
  let parsed;
1105
1107
  try {
1106
1108
  parsed = JSON.parse(fs14.readFileSync(file, "utf8"));
@@ -1487,8 +1489,8 @@ var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
1487
1489
  function registerServe(program) {
1488
1490
  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) {
1489
1491
  const global = readGlobal(this);
1490
- const root2 = rootOf(global);
1491
- const graphPath = resolveGraphPath(root2, global.graph);
1492
+ const root3 = rootOf(global);
1493
+ const graphPath = resolveGraphPath(root3, global.graph);
1492
1494
  const refresh = opts.refresh !== false && !global.graph;
1493
1495
  const local = global.local === true;
1494
1496
  const optOut = telemetryOptOut();
@@ -1496,7 +1498,7 @@ function registerServe(program) {
1496
1498
  const quiet = global.quiet === true;
1497
1499
  const stats = quiet ? void 0 : new SessionStats();
1498
1500
  if (!quiet) {
1499
- printLogo(root2, { product: "AI Context", tagline: "Local-first MCP for your AI" });
1501
+ printLogo(root3, { product: "AI Context", tagline: "Local-first MCP for your AI" });
1500
1502
  }
1501
1503
  const serveOpts = {
1502
1504
  // A *requested* --share-stats always implies local recording, even when
@@ -1509,7 +1511,7 @@ function registerServe(program) {
1509
1511
  dedup: opts.dedup === true,
1510
1512
  refresh,
1511
1513
  watch: opts.watch !== false,
1512
- root: root2,
1514
+ root: root3,
1513
1515
  stats,
1514
1516
  // Listing surface: flags win over env; unknown values fall back to the
1515
1517
  // full surface inside listedToolNames (fail-open).
@@ -1518,18 +1520,18 @@ function registerServe(program) {
1518
1520
  tools: (opts.tools ?? process.env.VG_MCP_TOOLS)?.split(",").map((s) => s.trim()).filter(Boolean)
1519
1521
  }
1520
1522
  };
1521
- await ensureServableGraph(root2, graphPath, global, refresh);
1523
+ await ensureServableGraph(root3, graphPath, global, refresh);
1522
1524
  if (opts.shareStats === true && local) {
1523
1525
  info(c.dim("vg \xB7 --share-stats ignored under --local (air-gapped): recording locally, not uploading."));
1524
1526
  } else if (opts.shareStats === true && optOut !== null) {
1525
1527
  info(c.dim(`vg \xB7 --share-stats disabled by ${optOut}: recording locally, not uploading.`));
1526
1528
  }
1527
- if (shareStats) startSharing(root2);
1529
+ if (shareStats) startSharing(root3);
1528
1530
  const interactive = process.stderr.isTTY === true;
1529
- const bus = stats ? new LiveStatsBus(liveStatsDir(root2), stats) : void 0;
1531
+ const bus = stats ? new LiveStatsBus(liveStatsDir(root3), stats) : void 0;
1530
1532
  if (bus && !interactive) bus.start();
1531
1533
  const display = stats ? new ServeStatusDisplay(stats, process.stderr, bus && interactive ? () => bus.siblings() : void 0) : void 0;
1532
- if (stats && interactive) new LedgerTail(savingsLedgerPath(root2), stats).start();
1534
+ if (stats && interactive) new LedgerTail(savingsLedgerPath(root3), stats).start();
1533
1535
  const freshness = refresh ? "auto-refresh" : "as built";
1534
1536
  if (opts.http) {
1535
1537
  await serveHttp(graphPath, opts.host ?? "127.0.0.1", Number(opts.port) || 7437, serveOpts, freshness, () => display?.start());
@@ -1541,7 +1543,7 @@ function registerServe(program) {
1541
1543
  });
1542
1544
  applyGlobalOptions(cmd);
1543
1545
  }
1544
- async function ensureServableGraph(root2, graphPath, global, refresh, opts = {}) {
1546
+ async function ensureServableGraph(root3, graphPath, global, refresh, opts = {}) {
1545
1547
  if (refresh) {
1546
1548
  if (!mapFileExists(graphPath)) {
1547
1549
  info(c.dim("vg \xB7 no map found \u2014 building it before serving\u2026"));
@@ -1551,7 +1553,7 @@ async function ensureServableGraph(root2, graphPath, global, refresh, opts = {})
1551
1553
  { ...global, json: false }
1552
1554
  );
1553
1555
  } else {
1554
- const kick = refreshIfStale(root2, { inline: opts.inline, graphPath }).then((refreshed) => {
1556
+ const kick = refreshIfStale(root3, { inline: opts.inline, graphPath }).then((refreshed) => {
1555
1557
  if (refreshed.status === "refreshed") {
1556
1558
  const n = driftCount(refreshed.drift);
1557
1559
  info(
@@ -1576,8 +1578,8 @@ async function ensureServableGraph(root2, graphPath, global, refresh, opts = {})
1576
1578
  );
1577
1579
  }
1578
1580
  }
1579
- function startSharing(root2) {
1580
- const sharer = new StatsSharer(root2);
1581
+ function startSharing(root3) {
1582
+ const sharer = new StatsSharer(root3);
1581
1583
  info(
1582
1584
  c.dim(
1583
1585
  `vg \xB7 sharing counts-only usage stats with Vibgrate (${statsEndpoint()}) to improve the local MCP. No code, paths, or questions are sent. Stop by omitting --share-stats.`
@@ -1632,7 +1634,7 @@ async function serveHttp(graphPath, host, port, opts, freshness, onReady) {
1632
1634
  info(c.red(`vg serve: request error: ${err.message}`));
1633
1635
  }
1634
1636
  });
1635
- await new Promise((resolve21) => httpServer.listen(port, host, resolve21));
1637
+ await new Promise((resolve23) => httpServer.listen(port, host, resolve23));
1636
1638
  info(c.dim(`vg \xB7 MCP server on http://${host}:${port}/mcp (read-only, local, ${freshness})`));
1637
1639
  onReady?.();
1638
1640
  await new Promise(() => {
@@ -1845,10 +1847,10 @@ function endOfLine(text, line) {
1845
1847
  return (lines[line] ?? "").length;
1846
1848
  }
1847
1849
  var CACHE_VERSION = "vg-lsp-scan-cache/2";
1848
- function cachePath(root2) {
1849
- return path43.join(cacheDir(root2), "lsp-scan.json");
1850
+ function cachePath(root3) {
1851
+ return path46.join(cacheDir(root3), "lsp-scan.json");
1850
1852
  }
1851
- function manifestHash(root2) {
1853
+ function manifestHash(root3) {
1852
1854
  const files = [];
1853
1855
  const walk2 = (dir) => {
1854
1856
  let entries;
@@ -1858,7 +1860,7 @@ function manifestHash(root2) {
1858
1860
  return;
1859
1861
  }
1860
1862
  for (const entry of entries) {
1861
- const abs = path43.join(dir, entry.name);
1863
+ const abs = path46.join(dir, entry.name);
1862
1864
  if (entry.isDirectory()) {
1863
1865
  if (isSkippedDirName(entry.name)) continue;
1864
1866
  walk2(abs);
@@ -1868,27 +1870,27 @@ function manifestHash(root2) {
1868
1870
  const base = entry.name.toLowerCase();
1869
1871
  if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
1870
1872
  try {
1871
- const rel = path43.relative(root2, abs).split(path43.sep).join("/");
1873
+ const rel = path46.relative(root3, abs).split(path46.sep).join("/");
1872
1874
  files.push({ rel, hash: hashBytes(fs14.readFileSync(abs)) });
1873
1875
  } catch {
1874
1876
  }
1875
1877
  }
1876
1878
  };
1877
- walk2(root2);
1879
+ walk2(root3);
1878
1880
  files.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
1879
1881
  return hashString(canonicalize(files));
1880
1882
  }
1881
- function isDependencyFile(root2, filePath) {
1882
- const rel = path43.relative(path43.resolve(root2), path43.resolve(filePath));
1883
- if (rel === "" || rel.startsWith("..") || path43.isAbsolute(rel)) return false;
1884
- const parts = rel.split(path43.sep);
1883
+ function isDependencyFile(root3, filePath) {
1884
+ const rel = path46.relative(path46.resolve(root3), path46.resolve(filePath));
1885
+ if (rel === "" || rel.startsWith("..") || path46.isAbsolute(rel)) return false;
1886
+ const parts = rel.split(path46.sep);
1885
1887
  if (parts.slice(0, -1).some((dir) => isSkippedDirName(dir))) return false;
1886
1888
  const base = parts[parts.length - 1].toLowerCase();
1887
1889
  return isManifest(filePath) || SKIP_FILES.has(base);
1888
1890
  }
1889
- function loadScanCache(root2, key2) {
1891
+ function loadScanCache(root3, key2) {
1890
1892
  try {
1891
- const raw = JSON.parse(fs14.readFileSync(cachePath(root2), "utf8"));
1893
+ const raw = JSON.parse(fs14.readFileSync(cachePath(root3), "utf8"));
1892
1894
  if (raw.version === CACHE_VERSION && raw.manifestHash === key2.manifestHash && raw.toolVersion === key2.toolVersion && raw.offline === key2.offline && raw.artifact) {
1893
1895
  return { manifestHash: raw.manifestHash, artifact: raw.artifact };
1894
1896
  }
@@ -1896,11 +1898,11 @@ function loadScanCache(root2, key2) {
1896
1898
  }
1897
1899
  return null;
1898
1900
  }
1899
- function writeScanCache(root2, key2, artifact) {
1901
+ function writeScanCache(root3, key2, artifact) {
1900
1902
  try {
1901
- fs14.mkdirSync(cacheDir(root2), { recursive: true });
1903
+ fs14.mkdirSync(cacheDir(root3), { recursive: true });
1902
1904
  fs14.writeFileSync(
1903
- cachePath(root2),
1905
+ cachePath(root3),
1904
1906
  stableStringify(
1905
1907
  { version: CACHE_VERSION, manifestHash: key2.manifestHash, toolVersion: key2.toolVersion, offline: key2.offline, artifact },
1906
1908
  0
@@ -1911,8 +1913,8 @@ function writeScanCache(root2, key2, artifact) {
1911
1913
  }
1912
1914
  var MAX_ENTRIES = 1e3;
1913
1915
  var KEEP_ENTRIES = 500;
1914
- function historyPath(root2) {
1915
- return path43.join(root2, ".vibgrate", "score-history.jsonl");
1916
+ function historyPath(root3) {
1917
+ return path46.join(root3, ".vibgrate", "score-history.jsonl");
1916
1918
  }
1917
1919
  function parseLine(line) {
1918
1920
  try {
@@ -1924,27 +1926,27 @@ function parseLine(line) {
1924
1926
  }
1925
1927
  return null;
1926
1928
  }
1927
- function readHistory(root2) {
1929
+ function readHistory(root3) {
1928
1930
  try {
1929
- return fs14.readFileSync(historyPath(root2), "utf8").split("\n").filter((l) => l.trim()).map(parseLine).filter((e) => e !== null);
1931
+ return fs14.readFileSync(historyPath(root3), "utf8").split("\n").filter((l) => l.trim()).map(parseLine).filter((e) => e !== null);
1930
1932
  } catch {
1931
1933
  return [];
1932
1934
  }
1933
1935
  }
1934
- function lastEntry(root2) {
1935
- const all = readHistory(root2);
1936
+ function lastEntry(root3) {
1937
+ const all = readHistory(root3);
1936
1938
  return all.length ? all[all.length - 1] : null;
1937
1939
  }
1938
- function recentHistory(root2, days, now = Date.now()) {
1940
+ function recentHistory(root3, days, now = Date.now()) {
1939
1941
  const cutoff = now - days * 24 * 60 * 60 * 1e3;
1940
- return readHistory(root2).filter((e) => {
1942
+ return readHistory(root3).filter((e) => {
1941
1943
  const t = Date.parse(e.ts);
1942
1944
  return Number.isFinite(t) && t >= cutoff;
1943
1945
  });
1944
1946
  }
1945
- function recordScore(root2, entry) {
1947
+ function recordScore(root3, entry) {
1946
1948
  try {
1947
- const prev = lastEntry(root2);
1949
+ const prev = lastEntry(root3);
1948
1950
  if (prev && prev.score === entry.score && prev.band === entry.band && prev.mode === entry.mode && prev.methodology === entry.methodology) {
1949
1951
  const prevT = Date.parse(prev.ts);
1950
1952
  const nextT = Date.parse(entry.ts);
@@ -1952,12 +1954,12 @@ function recordScore(root2, entry) {
1952
1954
  return;
1953
1955
  }
1954
1956
  }
1955
- const file = historyPath(root2);
1956
- fs14.mkdirSync(path43.dirname(file), { recursive: true });
1957
- ensureVibgrateGitignore(root2);
1957
+ const file = historyPath(root3);
1958
+ fs14.mkdirSync(path46.dirname(file), { recursive: true });
1959
+ ensureVibgrateGitignore(root3);
1958
1960
  fs14.appendFileSync(file, `${JSON.stringify(entry)}
1959
1961
  `);
1960
- const all = readHistory(root2);
1962
+ const all = readHistory(root3);
1961
1963
  if (all.length > MAX_ENTRIES) {
1962
1964
  const kept = all.slice(-KEEP_ENTRIES);
1963
1965
  fs14.writeFileSync(file, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
@@ -1970,8 +1972,8 @@ function deltaFrom(prev, current) {
1970
1972
  if (prev.methodology !== current.methodology) return void 0;
1971
1973
  return current.score - prev.score;
1972
1974
  }
1973
- function ignoreFilePath(root2) {
1974
- return path43.join(root2, ".vibgrate", "ignore.json");
1975
+ function ignoreFilePath(root3) {
1976
+ return path46.join(root3, ".vibgrate", "ignore.json");
1975
1977
  }
1976
1978
  function isActive(entry, now = Date.now()) {
1977
1979
  if (!entry.until) return true;
@@ -1979,11 +1981,11 @@ function isActive(entry, now = Date.now()) {
1979
1981
  if (!Number.isFinite(t)) return true;
1980
1982
  return now <= t + 24 * 60 * 60 * 1e3 - 1;
1981
1983
  }
1982
- function readIgnores(root2, now = Date.now()) {
1984
+ function readIgnores(root3, now = Date.now()) {
1983
1985
  const out2 = /* @__PURE__ */ new Map();
1984
1986
  let raw;
1985
1987
  try {
1986
- raw = fs14.readFileSync(ignoreFilePath(root2), "utf8");
1988
+ raw = fs14.readFileSync(ignoreFilePath(root3), "utf8");
1987
1989
  } catch {
1988
1990
  return out2;
1989
1991
  }
@@ -2002,15 +2004,15 @@ function readIgnores(root2, now = Date.now()) {
2002
2004
  }
2003
2005
  return out2;
2004
2006
  }
2005
- function inventoryPath(root2) {
2006
- return path43.join(root2, ".vibgrate", "inline-inventory.json");
2007
+ function inventoryPath(root3) {
2008
+ return path46.join(root3, ".vibgrate", "inline-inventory.json");
2007
2009
  }
2008
2010
  function inventoryKey(projectPath, pkg) {
2009
2011
  return `${projectPath}::${pkg}`;
2010
2012
  }
2011
- function readInventory(root2) {
2013
+ function readInventory(root3) {
2012
2014
  try {
2013
- const parsed = JSON.parse(fs14.readFileSync(inventoryPath(root2), "utf8"));
2015
+ const parsed = JSON.parse(fs14.readFileSync(inventoryPath(root3), "utf8"));
2014
2016
  if (typeof parsed.ts === "string" && typeof parsed.methodology === "string" && Array.isArray(parsed.drifted) && parsed.drifted.every((d) => typeof d === "string")) {
2015
2017
  return parsed;
2016
2018
  }
@@ -2018,10 +2020,10 @@ function readInventory(root2) {
2018
2020
  }
2019
2021
  return null;
2020
2022
  }
2021
- function recordInventory(root2, snap) {
2023
+ function recordInventory(root3, snap) {
2022
2024
  try {
2023
- ensureVibgrateGitignore(root2);
2024
- fs14.writeFileSync(inventoryPath(root2), `${JSON.stringify(snap)}
2025
+ ensureVibgrateGitignore(root3);
2026
+ fs14.writeFileSync(inventoryPath(root3), `${JSON.stringify(snap)}
2025
2027
  `);
2026
2028
  } catch {
2027
2029
  }
@@ -2530,6 +2532,37 @@ async function scanWorkspaceVulns(targets, opts = { offline: false }) {
2530
2532
  }
2531
2533
 
2532
2534
  // src/lsp/server.ts
2535
+ function architectureWire(arch) {
2536
+ const out2 = {
2537
+ archetype: arch.archetype,
2538
+ archetypeConfidence: arch.archetypeConfidence,
2539
+ layers: arch.layers,
2540
+ totalClassified: arch.totalClassified,
2541
+ unclassified: arch.unclassified
2542
+ };
2543
+ if (arch.folders) out2.folders = arch.folders;
2544
+ if (arch.unclassifiedFiles) out2.unclassifiedFiles = arch.unclassifiedFiles;
2545
+ if (arch.classified) out2.classified = arch.classified;
2546
+ if (arch.workspaceShape) out2.workspaceShape = arch.workspaceShape;
2547
+ if (arch.supportLevel !== void 0) out2.supportLevel = arch.supportLevel;
2548
+ if (arch.packIds) out2.packIds = arch.packIds;
2549
+ if (arch.projectKind) out2.projectKind = arch.projectKind;
2550
+ if (arch.artifactKind) out2.artifactKind = arch.artifactKind;
2551
+ if (arch.contractKind) out2.contractKind = arch.contractKind;
2552
+ if (arch.semanticRole) out2.semanticRole = arch.semanticRole;
2553
+ if (arch.evidence) out2.evidence = arch.evidence;
2554
+ if (arch.evidenceCapped) out2.evidenceCapped = true;
2555
+ if (arch.workspaceEdges) out2.workspaceEdges = arch.workspaceEdges;
2556
+ if (arch.workspaceEdgesCapped) out2.workspaceEdgesCapped = true;
2557
+ if (arch.violations) out2.violations = arch.violations;
2558
+ if (arch.violationsCapped) out2.violationsCapped = true;
2559
+ if (arch.boundaryProfile) out2.boundaryProfile = arch.boundaryProfile;
2560
+ if (arch.structure) out2.structure = arch.structure;
2561
+ if (arch.generated) out2.generated = true;
2562
+ if (arch.generatedBy) out2.generatedBy = arch.generatedBy;
2563
+ if (arch.sourceContract) out2.sourceContract = arch.sourceContract;
2564
+ return out2;
2565
+ }
2533
2566
  function bandForDependency(dep) {
2534
2567
  if (dep.drift === "major-behind") {
2535
2568
  return (dep.majorsBehind ?? 1) >= 2 ? "high" : "moderate";
@@ -2580,6 +2613,8 @@ var VibgrateLanguageServer = class {
2580
2613
  debounce = null;
2581
2614
  shuttingDown = false;
2582
2615
  graph = null;
2616
+ /** Role hits from the last graph build or recovered from the parse cache. */
2617
+ fileRoles = [];
2583
2618
  graphBuilding = false;
2584
2619
  /**
2585
2620
  * Keeps the freshness probe / incremental rebuild OFF the query path. The
@@ -2689,6 +2724,7 @@ var VibgrateLanguageServer = class {
2689
2724
  let fromCache = false;
2690
2725
  if (cached2) {
2691
2726
  this.artifact = pruneArtifactProjects(cached2.artifact);
2727
+ if (this.graph) refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
2692
2728
  fromCache = true;
2693
2729
  } else {
2694
2730
  const scanOpts = {
@@ -2705,10 +2741,11 @@ var VibgrateLanguageServer = class {
2705
2741
  };
2706
2742
  const advanced = await loadAdvancedScanHook();
2707
2743
  this.artifact = pruneArtifactProjects(
2708
- await runCoreScanSilently(this.opts.root, scanOpts, advanced)
2744
+ await runCoreScanSilently(this.opts.root, scanOpts, advanced, this.graph, this.fileRoles)
2709
2745
  );
2710
2746
  writeScanCache(this.opts.root, cacheKey, this.artifact);
2711
2747
  }
2748
+ if (this.graph) refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
2712
2749
  this.publishScore();
2713
2750
  this.publishScanArtifact(fromCache);
2714
2751
  for (const uri of this.docs.keys()) this.publishForDoc(uri);
@@ -2923,6 +2960,8 @@ var VibgrateLanguageServer = class {
2923
2960
  const existing = loadGraph(this.opts.root);
2924
2961
  if (existing) {
2925
2962
  this.graph = existing;
2963
+ this.fileRoles = fileRolesFromParseCache(this.opts.root);
2964
+ this.refineAndPublishArchitecture();
2926
2965
  this.conn.notify("vibgrate/graph/status", { state: "ready" });
2927
2966
  return;
2928
2967
  }
@@ -2931,6 +2970,8 @@ var VibgrateLanguageServer = class {
2931
2970
  writeArtifacts(result.graph, { root: this.opts.root, html: false, report: false });
2932
2971
  writeSnapshot(this.opts.root, result.graph.provenance.corpusHash, result.fileStats, {});
2933
2972
  this.graph = result.graph;
2973
+ this.fileRoles = result.fileRoles;
2974
+ this.refineAndPublishArchitecture();
2934
2975
  this.conn.notify("vibgrate/graph/status", { state: "ready" });
2935
2976
  } catch (err) {
2936
2977
  const message = err instanceof Error ? err.message : String(err);
@@ -2962,7 +3003,22 @@ var VibgrateLanguageServer = class {
2962
3003
  if (outcome?.status !== "refreshed" || !outcome.wrote) return;
2963
3004
  const graphPath = resolveGraphPath(this.opts.root);
2964
3005
  const reloaded = loadGraphPreferIndex(this.opts.root, graphPath)?.graph ?? loadGraph(this.opts.root);
2965
- if (reloaded) this.graph = reloaded;
3006
+ if (reloaded) {
3007
+ this.graph = reloaded;
3008
+ this.fileRoles = fileRolesFromParseCache(this.opts.root);
3009
+ this.refineAndPublishArchitecture();
3010
+ }
3011
+ }
3012
+ /**
3013
+ * Graph / AST-role refine can land after the first `publishScore()`. The
3014
+ * Architecture panel only re-pulls on a score or scope change, so a silent
3015
+ * in-memory refine would leave the first session on the path-only snapshot.
3016
+ * Re-push `vibgrate/score` (no-op when the artifact is not ready yet).
3017
+ */
3018
+ refineAndPublishArchitecture() {
3019
+ if (!this.artifact || !this.graph) return;
3020
+ refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
3021
+ this.publishScore();
2966
3022
  }
2967
3023
  async onGraphQuery(params) {
2968
3024
  const p = params;
@@ -3031,7 +3087,7 @@ var VibgrateLanguageServer = class {
3031
3087
  const ecosystem = osvEcosystem(proj.type);
3032
3088
  if (!ecosystem) continue;
3033
3089
  const relManifest = manifestRelativePath(proj);
3034
- const absManifest = path43.resolve(this.opts.root, relManifest);
3090
+ const absManifest = path46.resolve(this.opts.root, relManifest);
3035
3091
  let text = null;
3036
3092
  try {
3037
3093
  if (fs14.existsSync(absManifest)) text = fs14.readFileSync(absManifest, "utf8");
@@ -3114,21 +3170,13 @@ var VibgrateLanguageServer = class {
3114
3170
  const arch = a.extended?.architecture;
3115
3171
  if (!arch) return null;
3116
3172
  return {
3117
- archetype: arch.archetype,
3118
- archetypeConfidence: arch.archetypeConfidence,
3119
- layers: arch.layers,
3120
- totalClassified: arch.totalClassified,
3121
- unclassified: arch.unclassified
3173
+ ...architectureWire(arch)
3122
3174
  };
3123
3175
  }
3124
3176
  const project = projectByPath(a, p.path);
3125
3177
  if (!project?.architecture) return null;
3126
3178
  return {
3127
- archetype: project.architecture.archetype,
3128
- archetypeConfidence: project.architecture.archetypeConfidence,
3129
- layers: project.architecture.layers,
3130
- totalClassified: project.architecture.totalClassified,
3131
- unclassified: project.architecture.unclassified,
3179
+ ...architectureWire(project.architecture),
3132
3180
  manifestPath: manifestRelativePath(project),
3133
3181
  lockfilePath: lockfileRelativePath(this.opts.root, project)
3134
3182
  };
@@ -3141,7 +3189,7 @@ function uriToPath(uri) {
3141
3189
  return uri;
3142
3190
  }
3143
3191
  }
3144
- async function runCoreScanSilently(root2, opts, advanced) {
3192
+ async function runCoreScanSilently(root3, opts, advanced, graph, fileRoles) {
3145
3193
  const realLog = console.log.bind(console);
3146
3194
  console.log = (...args) => {
3147
3195
  if (args.length === 1 && typeof args[0] === "string" && looksLikeScanArtifactJson(args[0])) {
@@ -3150,11 +3198,34 @@ async function runCoreScanSilently(root2, opts, advanced) {
3150
3198
  realLog(...args);
3151
3199
  };
3152
3200
  try {
3153
- return await runCoreScan(root2, opts, advanced);
3201
+ const artifact = await runCoreScan(root3, opts, advanced);
3202
+ if (graph) refineArtifactWithGraph(artifact, graph, fileRoles);
3203
+ return artifact;
3154
3204
  } finally {
3155
3205
  console.log = realLog;
3156
3206
  }
3157
3207
  }
3208
+ function architectureGraphView(graph) {
3209
+ return {
3210
+ nodes: graph.nodes.map((n) => ({ id: n.id, file: n.file, kind: n.kind })),
3211
+ edges: graph.edges.map((e) => ({
3212
+ src: e.src,
3213
+ dst: e.dst,
3214
+ kind: e.kind,
3215
+ confidence: e.confidence,
3216
+ resolution: e.resolution
3217
+ }))
3218
+ };
3219
+ }
3220
+ function refineArtifactWithGraph(artifact, graph, fileRoles) {
3221
+ const hasArch = Boolean(artifact.extended?.architecture) || artifact.projects.some((p) => p.architecture);
3222
+ if (!hasArch) return;
3223
+ const already = artifact.extended?.architecture?.violations !== void 0 || artifact.projects.some((p) => p.architecture?.violations !== void 0);
3224
+ if (!already) refineArchitectureWithGraph(artifact, architectureGraphView(graph));
3225
+ if (fileRoles && fileRoles.length > 0) {
3226
+ refineArchitectureWithAstRoles(artifact, fileRoles);
3227
+ }
3228
+ }
3158
3229
  function looksLikeScanArtifactJson(text) {
3159
3230
  const t = text.trimStart();
3160
3231
  if (!t.startsWith("{")) return false;
@@ -3183,7 +3254,7 @@ function pruneArtifactProjects(a) {
3183
3254
  return { ...a, projects, findings };
3184
3255
  }
3185
3256
  function projectByPath(a, projectPath) {
3186
- const norm = (p) => path43.normalize((p === "" ? "." : p).replace(/\\/g, "/"));
3257
+ const norm = (p) => path46.normalize((p === "" ? "." : p).replace(/\\/g, "/"));
3187
3258
  const want = norm(projectPath);
3188
3259
  for (const proj of a.projects ?? []) {
3189
3260
  if (norm(proj.path) === want) return proj;
@@ -3221,13 +3292,13 @@ function buildProjectRefs(rootDir, projects) {
3221
3292
  refs.sort((a, b) => a.path.localeCompare(b.path));
3222
3293
  return refs.length > 0 ? refs : void 0;
3223
3294
  }
3224
- function projectForFile(root2, a, filePath) {
3225
- const dir = path43.dirname(path43.resolve(filePath));
3295
+ function projectForFile(root3, a, filePath) {
3296
+ const dir = path46.dirname(path46.resolve(filePath));
3226
3297
  let best;
3227
3298
  let bestLen = -1;
3228
3299
  for (const proj of a.projects ?? []) {
3229
- const projDir = path43.resolve(root2, proj.path);
3230
- if ((dir === projDir || dir.startsWith(projDir + path43.sep)) && projDir.length > bestLen) {
3300
+ const projDir = path46.resolve(root3, proj.path);
3301
+ if ((dir === projDir || dir.startsWith(projDir + path46.sep)) && projDir.length > bestLen) {
3231
3302
  best = proj;
3232
3303
  bestLen = projDir.length;
3233
3304
  }
@@ -3235,7 +3306,7 @@ function projectForFile(root2, a, filePath) {
3235
3306
  return best;
3236
3307
  }
3237
3308
  function sameProject(location, projectPath) {
3238
- return path43.normalize(location) === path43.normalize(projectPath);
3309
+ return path46.normalize(location) === path46.normalize(projectPath);
3239
3310
  }
3240
3311
  function hasReleaseDates(a) {
3241
3312
  for (const proj of a.projects ?? []) {
@@ -3305,7 +3376,7 @@ function buildBreakdown(rootDir, projects) {
3305
3376
  const relManifest = manifestRelativePath(p);
3306
3377
  if (isPrunedWorkspacePath(p.path) || isPrunedWorkspacePath(relManifest)) continue;
3307
3378
  const ecosystem = osvEcosystem(p.type);
3308
- const absManifest = path43.resolve(rootDir, relManifest);
3379
+ const absManifest = path46.resolve(rootDir, relManifest);
3309
3380
  let text = null;
3310
3381
  try {
3311
3382
  if (fs14.existsSync(absManifest)) text = fs14.readFileSync(absManifest, "utf8");
@@ -3373,34 +3444,34 @@ function buildBreakdown(rootDir, projects) {
3373
3444
  }
3374
3445
  function manifestRelativePath(proj) {
3375
3446
  const base = proj.path === "." || proj.path === "" ? "" : proj.path;
3376
- const join35 = (name) => base ? path43.join(base, name) : name;
3447
+ const join36 = (name) => base ? path46.join(base, name) : name;
3377
3448
  switch (proj.type) {
3378
3449
  case "node":
3379
3450
  case "typescript":
3380
- return join35("package.json");
3451
+ return join36("package.json");
3381
3452
  case "python":
3382
- return join35("pyproject.toml");
3453
+ return join36("pyproject.toml");
3383
3454
  case "go":
3384
- return join35("go.mod");
3455
+ return join36("go.mod");
3385
3456
  case "rust":
3386
- return join35("Cargo.toml");
3457
+ return join36("Cargo.toml");
3387
3458
  case "ruby":
3388
- return join35("Gemfile");
3459
+ return join36("Gemfile");
3389
3460
  case "php":
3390
- return join35("composer.json");
3461
+ return join36("composer.json");
3391
3462
  case "java":
3392
3463
  case "kotlin":
3393
3464
  case "scala":
3394
3465
  case "groovy":
3395
- return join35("pom.xml");
3466
+ return join36("pom.xml");
3396
3467
  case "dotnet":
3397
- return join35(proj.name ? `${proj.name}.csproj` : "Directory.Build.props");
3468
+ return join36(proj.name ? `${proj.name}.csproj` : "Directory.Build.props");
3398
3469
  case "dart":
3399
- return join35("pubspec.yaml");
3470
+ return join36("pubspec.yaml");
3400
3471
  case "elixir":
3401
- return join35("mix.exs");
3472
+ return join36("mix.exs");
3402
3473
  default:
3403
- return join35("package.json");
3474
+ return join36("package.json");
3404
3475
  }
3405
3476
  }
3406
3477
  function lockfileRelativePath(rootDir, proj) {
@@ -3431,9 +3502,9 @@ function lockfileRelativePath(rootDir, proj) {
3431
3502
  const dirsToTry = base ? [base, ""] : [""];
3432
3503
  for (const dir of dirsToTry) {
3433
3504
  for (const name of candidateNames) {
3434
- const rel = dir ? path43.join(dir, name) : name;
3505
+ const rel = dir ? path46.join(dir, name) : name;
3435
3506
  try {
3436
- if (fs14.existsSync(path43.join(rootDir, rel))) return rel;
3507
+ if (fs14.existsSync(path46.join(rootDir, rel))) return rel;
3437
3508
  } catch {
3438
3509
  }
3439
3510
  }
@@ -3497,14 +3568,14 @@ function lookupImportFiles(map, pkg) {
3497
3568
  const p = pkg.toLowerCase();
3498
3569
  if (map.has(p)) return map.get(p);
3499
3570
  if (!p.startsWith("@")) {
3500
- const root2 = p.split("/")[0];
3501
- if (map.has(root2)) return map.get(root2);
3571
+ const root3 = p.split("/")[0];
3572
+ if (map.has(root3)) return map.get(root3);
3502
3573
  }
3503
3574
  return [];
3504
3575
  }
3505
- function firstPackageMentionLine(root2, relFile, pkg) {
3576
+ function firstPackageMentionLine(root3, relFile, pkg) {
3506
3577
  try {
3507
- const abs = path43.resolve(root2, relFile);
3578
+ const abs = path46.resolve(root3, relFile);
3508
3579
  const stat2 = fs14.statSync(abs);
3509
3580
  if (!stat2.isFile() || stat2.size > 256 * 1024) return null;
3510
3581
  const text = fs14.readFileSync(abs, "utf8");
@@ -3730,9 +3801,9 @@ function registerLsp(program) {
3730
3801
  false
3731
3802
  ).option("--no-graph", "skip the local Vibgrate Graph entirely: no background build, and graph queries report it as turned off").option("--no-semantic", "never use semantic search for graph queries (lexical only; the embedding model is not downloaded)").action(async function(opts) {
3732
3803
  const global = readGlobal(this);
3733
- const root2 = rootOf(global);
3804
+ const root3 = rootOf(global);
3734
3805
  startLanguageServer({
3735
- root: root2,
3806
+ root: root3,
3736
3807
  // `--local` is the established air-gap flag across the CLI; honour it
3737
3808
  // here too, so an offline editor session stays offline.
3738
3809
  offline: global.local === true,
@@ -3751,9 +3822,9 @@ var HOOK_TIMEOUT_S = 10;
3751
3822
  function hookCommand(vgBin) {
3752
3823
  return `${vgBin} hook pre-tool-use`;
3753
3824
  }
3754
- function installClaudeHooks(root2, vgBin = "vg") {
3755
- const file = path43.join(root2, ".claude", "settings.json");
3756
- const rel = path43.join(".claude", "settings.json");
3825
+ function installClaudeHooks(root3, vgBin = "vg") {
3826
+ const file = path46.join(root3, ".claude", "settings.json");
3827
+ const rel = path46.join(".claude", "settings.json");
3757
3828
  let settings = {};
3758
3829
  if (fs14.existsSync(file)) {
3759
3830
  try {
@@ -3793,9 +3864,9 @@ function installClaudeHooks(root2, vgBin = "vg") {
3793
3864
  writeSettings(file, settings);
3794
3865
  return { file: rel, status: "written" };
3795
3866
  }
3796
- function uninstallClaudeHooks(root2) {
3797
- const file = path43.join(root2, ".claude", "settings.json");
3798
- const rel = path43.join(".claude", "settings.json");
3867
+ function uninstallClaudeHooks(root3) {
3868
+ const file = path46.join(root3, ".claude", "settings.json");
3869
+ const rel = path46.join(".claude", "settings.json");
3799
3870
  if (!fs14.existsSync(file)) return { file: rel, status: "unchanged" };
3800
3871
  let settings;
3801
3872
  try {
@@ -3821,7 +3892,7 @@ function uninstallClaudeHooks(root2) {
3821
3892
  return { file: rel, status: "written" };
3822
3893
  }
3823
3894
  function writeSettings(file, settings) {
3824
- fs14.mkdirSync(path43.dirname(file), { recursive: true });
3895
+ fs14.mkdirSync(path46.dirname(file), { recursive: true });
3825
3896
  fs14.writeFileSync(file, `${JSON.stringify(settings, null, 2)}
3826
3897
  `);
3827
3898
  }
@@ -3830,8 +3901,8 @@ function writeSettings(file, settings) {
3830
3901
  function registerInstall(program) {
3831
3902
  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) {
3832
3903
  const global = readGlobal(this);
3833
- const root2 = rootOf(global);
3834
- const detected = opts.detect ? detectAssistants(root2) : [];
3904
+ const root3 = rootOf(global);
3905
+ const detected = opts.detect ? detectAssistants(root3) : [];
3835
3906
  if (opts.list) {
3836
3907
  if (opts.detect) {
3837
3908
  if (global.json) {
@@ -3855,14 +3926,14 @@ function registerInstall(program) {
3855
3926
  mcp: !!a.mcp,
3856
3927
  skill: !!a.skill,
3857
3928
  nudge: !!a.nudge,
3858
- installed: isAssistantInstalled(a, root2)
3929
+ installed: isAssistantInstalled(a, root3)
3859
3930
  }))
3860
3931
  );
3861
3932
  } else {
3862
3933
  info(`${c.cyan("vg install")} \xB7 supported assistants`);
3863
3934
  const pad4 = Math.max(...ASSISTANTS.map((a) => a.id.length)) + 2;
3864
3935
  for (const a of ASSISTANTS) {
3865
- const on = isAssistantInstalled(a, root2) ? c.green("on") : c.dim("off");
3936
+ const on = isAssistantInstalled(a, root3) ? c.green("on") : c.dim("off");
3866
3937
  info(
3867
3938
  ` ${c.bold(a.id.padEnd(pad4))} ${a.label} ${on} ${c.dim(`mcp:${a.mcp ? "\u2713" : "\u2014"} skill:${a.skill ? "\u2713" : "\u2014"} nudge:${a.nudge ? "\u2713" : "\u2014"}`)}`
3868
3939
  );
@@ -3873,20 +3944,20 @@ function registerInstall(program) {
3873
3944
  const targets = opts.all ? ASSISTANTS : opts.detect ? detected.map((d) => d.assistant) : tools.map(resolve4);
3874
3945
  if (targets.length === 0) {
3875
3946
  if (opts.detect) {
3876
- if (global.json) json({ root: root2, detected: [], results: [] });
3947
+ if (global.json) json({ root: root3, detected: [], results: [] });
3877
3948
  else info(`${c.cyan("vg install")} \xB7 no AI assistants detected here \u2014 name one (e.g. \`vg install claude\`) or use --all`);
3878
3949
  return;
3879
3950
  }
3880
3951
  throw usageError("name an assistant (e.g. `vg install claude`) or use --all / --detect / --list");
3881
3952
  }
3882
- const graph = loadGraph(root2, global.graph);
3953
+ const graph = loadGraph(root3, global.graph);
3883
3954
  const fileCount = graph ? graph.nodes.filter((n) => n.kind === "file").length : 0;
3884
3955
  const smallRepo = graph !== null && fileCount > 0 && fileCount < SMALL_REPO_FILES;
3885
3956
  const launch = detectServeLaunch();
3886
- const results = targets.map((a) => ({ id: a.id, ...installAssistant(a, { root: root2, hook: opts.hook, smallRepo, launch }) }));
3887
- const navConfig = writeNavigationConfig(root2);
3957
+ const results = targets.map((a) => ({ id: a.id, ...installAssistant(a, { root: root3, hook: opts.hook, smallRepo, launch }) }));
3958
+ const navConfig = writeNavigationConfig(root3);
3888
3959
  if (global.json) {
3889
- json({ root: root2, smallRepo, navConfig, launch: { command: launch.command, args: launch.args, note: launch.note ?? null }, results });
3960
+ json({ root: root3, smallRepo, navConfig, launch: { command: launch.command, args: launch.args, note: launch.note ?? null }, results });
3890
3961
  return;
3891
3962
  }
3892
3963
  for (const r of results) {
@@ -3894,7 +3965,7 @@ function registerInstall(program) {
3894
3965
  }
3895
3966
  if (opts.hooks && targets.some((a) => a.id === "claude")) {
3896
3967
  const vgBin = launch.command === "npx" ? null : launch.command;
3897
- 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" };
3968
+ const hooksResult = vgBin ? installClaudeHooks(root3, 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" };
3898
3969
  if (hooksResult.status === "written") info(`${c.green("\u2714")} ${c.bold("hooks")} \u2014 wrote PreToolUse enrichment into ${hooksResult.file}`);
3899
3970
  else if (hooksResult.note) info(`${c.yellow("!")} hooks \u2014 ${hooksResult.note}`);
3900
3971
  }
@@ -3906,14 +3977,14 @@ function registerInstall(program) {
3906
3977
  applyGlobalOptions(install);
3907
3978
  const uninstall = program.command("uninstall").description("remove vg from an AI assistant").argument("<tools...>", "assistant ids").option("--purge", "also delete the skill file").action(function(tools, opts) {
3908
3979
  const global = readGlobal(this);
3909
- const root2 = rootOf(global);
3910
- const results = tools.map(resolve4).map((a) => ({ id: a.id, removed: uninstallAssistant(a, root2, !!opts.purge) }));
3980
+ const root3 = rootOf(global);
3981
+ const results = tools.map(resolve4).map((a) => ({ id: a.id, removed: uninstallAssistant(a, root3, !!opts.purge) }));
3911
3982
  if (tools.map(resolve4).some((a) => a.id === "claude")) {
3912
- const hooksResult = uninstallClaudeHooks(root2);
3983
+ const hooksResult = uninstallClaudeHooks(root3);
3913
3984
  if (hooksResult.status === "written") results.find((r) => r.id === "claude")?.removed.push(`${hooksResult.file} (vg hook entries)`);
3914
3985
  }
3915
3986
  if (global.json) {
3916
- json({ root: root2, results });
3987
+ json({ root: root3, results });
3917
3988
  return;
3918
3989
  }
3919
3990
  for (const r of results) {
@@ -3941,11 +4012,11 @@ function registryBase(opts) {
3941
4012
  return (process.env.VIBGRATE_MODULE_REGISTRY || opts.registry || DEFAULT_REGISTRY).replace(/\/+$/, "");
3942
4013
  }
3943
4014
  function modulesBaseDir() {
3944
- const base = process.env.XDG_CACHE_HOME || path43.join(os.homedir(), ".cache");
3945
- return path43.join(base, "vibgrate", "modules");
4015
+ const base = process.env.XDG_CACHE_HOME || path46.join(os.homedir(), ".cache");
4016
+ return path46.join(base, "vibgrate", "modules");
3946
4017
  }
3947
4018
  function stateFile() {
3948
- return path43.join(modulesBaseDir(), "consent.json");
4019
+ return path46.join(modulesBaseDir(), "consent.json");
3949
4020
  }
3950
4021
  function readConsent() {
3951
4022
  try {
@@ -3956,7 +4027,7 @@ function readConsent() {
3956
4027
  }
3957
4028
  function writeConsent(state) {
3958
4029
  try {
3959
- fs14.mkdirSync(path43.dirname(stateFile()), { recursive: true });
4030
+ fs14.mkdirSync(path46.dirname(stateFile()), { recursive: true });
3960
4031
  fs14.writeFileSync(stateFile(), JSON.stringify(state));
3961
4032
  } catch {
3962
4033
  }
@@ -3967,8 +4038,8 @@ function kernelDisabled() {
3967
4038
  }
3968
4039
  function moduleInstalledAt(dir) {
3969
4040
  try {
3970
- const meta = JSON.parse(fs14.readFileSync(path43.join(dir, ".module.json"), "utf8"));
3971
- if (fs14.existsSync(path43.join(dir, "index.js"))) {
4041
+ const meta = JSON.parse(fs14.readFileSync(path46.join(dir, ".module.json"), "utf8"));
4042
+ if (fs14.existsSync(path46.join(dir, "index.js"))) {
3972
4043
  return { installed: true, version: meta.version };
3973
4044
  }
3974
4045
  } catch {
@@ -4016,7 +4087,7 @@ function untar(data) {
4016
4087
  if (type !== "0" && type !== "\0") continue;
4017
4088
  const name = pendingName ?? (prefix ? `${prefix}/${rawName}` : rawName);
4018
4089
  pendingName = null;
4019
- if (name.split("/").includes("..") || path43.isAbsolute(name)) {
4090
+ if (name.split("/").includes("..") || path46.isAbsolute(name)) {
4020
4091
  throw new Error(`unsafe path in tarball: ${name}`);
4021
4092
  }
4022
4093
  out2.push({ name, body: Buffer.from(body) });
@@ -4050,17 +4121,17 @@ async function installModule(mod, opts = {}) {
4050
4121
  if (e.name.startsWith("package/dist/")) rel = e.name.slice("package/dist/".length);
4051
4122
  else if (e.name === "package/LICENSE" || e.name === "package/README.md") rel = e.name.slice("package/".length);
4052
4123
  if (!rel) continue;
4053
- const target = path43.join(dir, rel);
4054
- if (!target.startsWith(dir + path43.sep) && target !== dir) throw new Error(`unsafe extract path: ${rel}`);
4055
- fs14.mkdirSync(path43.dirname(target), { recursive: true });
4124
+ const target = path46.join(dir, rel);
4125
+ if (!target.startsWith(dir + path46.sep) && target !== dir) throw new Error(`unsafe extract path: ${rel}`);
4126
+ fs14.mkdirSync(path46.dirname(target), { recursive: true });
4056
4127
  fs14.writeFileSync(target, e.body);
4057
4128
  wrote += 1;
4058
4129
  }
4059
- if (!wrote || !fs14.existsSync(path43.join(dir, "index.js"))) {
4130
+ if (!wrote || !fs14.existsSync(path46.join(dir, "index.js"))) {
4060
4131
  fs14.rmSync(dir, { recursive: true, force: true });
4061
4132
  return { status: "unavailable", detail: "package layout not recognized (no dist/index.js)" };
4062
4133
  }
4063
- fs14.writeFileSync(path43.join(dir, ".module.json"), JSON.stringify({ name: mod.npmName, version }));
4134
+ fs14.writeFileSync(path46.join(dir, ".module.json"), JSON.stringify({ name: mod.npmName, version }));
4064
4135
  mod.onChanged?.();
4065
4136
  return { status: "installed", version };
4066
4137
  } catch (e) {
@@ -4106,19 +4177,19 @@ function disabled() {
4106
4177
  return v === "1" || v === "true";
4107
4178
  }
4108
4179
  function hcsModuleDir() {
4109
- return path43.join(modulesBaseDir(), "hcs");
4180
+ return path46.join(modulesBaseDir(), "hcs");
4110
4181
  }
4111
4182
  function candidatePaths() {
4112
4183
  const custom = process.env.VIBGRATE_HCS_PATH;
4113
4184
  if (custom) {
4114
- const p = path43.resolve(custom);
4185
+ const p = path46.resolve(custom);
4115
4186
  try {
4116
- if (fs14.statSync(p).isDirectory()) return [path43.join(p, "index.js")];
4187
+ if (fs14.statSync(p).isDirectory()) return [path46.join(p, "index.js")];
4117
4188
  } catch {
4118
4189
  }
4119
4190
  return [p];
4120
4191
  }
4121
- return [path43.join(hcsModuleDir(), "index.js")];
4192
+ return [path46.join(hcsModuleDir(), "index.js")];
4122
4193
  }
4123
4194
  var cached;
4124
4195
  function resetHcsEngineCache() {
@@ -4268,11 +4339,11 @@ function requireModule(name) {
4268
4339
  return mod;
4269
4340
  }
4270
4341
  function confirm(question) {
4271
- return new Promise((resolve21) => {
4342
+ return new Promise((resolve23) => {
4272
4343
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
4273
4344
  rl.question(question, (answer) => {
4274
4345
  rl.close();
4275
- resolve21(answer.trim() === "" || /^y(es)?$/i.test(answer.trim()));
4346
+ resolve23(answer.trim() === "" || /^y(es)?$/i.test(answer.trim()));
4276
4347
  });
4277
4348
  });
4278
4349
  }
@@ -4281,7 +4352,7 @@ var MIN_PATTERN_CHARS = 3;
4281
4352
  function patternToQuery(pattern) {
4282
4353
  return pattern.replace(/\\[dwsbDWSB]/g, " ").replace(/[\\^$.*+?()[\]{}|]/g, " ").replace(/\s+/g, " ").trim();
4283
4354
  }
4284
- function hookContext(payloadText, root2, graphOverride) {
4355
+ function hookContext(payloadText, root3, graphOverride) {
4285
4356
  let payload;
4286
4357
  try {
4287
4358
  payload = JSON.parse(payloadText);
@@ -4292,10 +4363,10 @@ function hookContext(payloadText, root2, graphOverride) {
4292
4363
  const raw = payload.tool_input?.pattern ?? payload.tool_input?.query ?? "";
4293
4364
  const query = patternToQuery(String(raw));
4294
4365
  if (query.length < MIN_PATTERN_CHARS) return null;
4295
- const graphPath = resolveGraphPath(root2, graphOverride);
4366
+ const graphPath = resolveGraphPath(root3, graphOverride);
4296
4367
  let graph;
4297
4368
  try {
4298
- const preferred = loadGraphPreferIndex(root2, graphPath);
4369
+ const preferred = loadGraphPreferIndex(root3, graphPath);
4299
4370
  graph = preferred ? preferred.graph : parseGraph(fs14.readFileSync(graphPath, "utf8"));
4300
4371
  } catch {
4301
4372
  return null;
@@ -4316,10 +4387,10 @@ function registerHook(program) {
4316
4387
  const cmd = program.command("hook", { hidden: true }).description("assistant-host hook endpoints (wired by `vg install --hooks`)");
4317
4388
  const pre = cmd.command("pre-tool-use").description("PreToolUse enrichment: annotate Grep/Glob calls with graph context (stdin: host hook payload)").action(async function() {
4318
4389
  const global = readGlobal(this);
4319
- const root2 = rootOf(global);
4390
+ const root3 = rootOf(global);
4320
4391
  try {
4321
4392
  const stdin = fs14.readFileSync(0, "utf8");
4322
- const context = hookContext(stdin, root2, global.graph);
4393
+ const context = hookContext(stdin, root3, global.graph);
4323
4394
  if (context) {
4324
4395
  process.stdout.write(
4325
4396
  `${JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } })}
@@ -4337,20 +4408,20 @@ var HOOK_END = "# vg:end";
4337
4408
  function registerShare(program) {
4338
4409
  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) {
4339
4410
  const global = readGlobal(this);
4340
- const root2 = rootOf(global);
4341
- const gitDir = path43.join(root2, ".git");
4411
+ const root3 = rootOf(global);
4412
+ const gitDir = path46.join(root3, ".git");
4342
4413
  if (!fs14.existsSync(gitDir)) {
4343
4414
  throw new CliError("not a git repository \u2014 run `vg share` inside a git repo", ExitCode.USAGE_ERROR);
4344
4415
  }
4345
4416
  if (opts.undo) {
4346
- undo(root2);
4417
+ undo(root3);
4347
4418
  info(`${c.cyan("vg share")} ${c.dim("--undo")} \xB7 removed hooks, merge driver, and attributes`);
4348
4419
  return;
4349
4420
  }
4350
- writeVibgrateGitignore(root2, !!opts.reports);
4351
- writeGitAttributes(root2);
4352
- configureMergeDriver(root2);
4353
- installPreCommitHook(root2);
4421
+ writeVibgrateGitignore(root3, !!opts.reports);
4422
+ writeGitAttributes(root3);
4423
+ configureMergeDriver(root3);
4424
+ installPreCommitHook(root3);
4354
4425
  info(`${c.cyan("vg share")} \xB7 the map is now committable + auto-updating`);
4355
4426
  info(` ${c.green("\u2714")} .vibgrate/.gitignore (cache + volatile artifacts ignored, graph.json committed)`);
4356
4427
  info(` ${c.green("\u2714")} pre-commit hook (rebuilds + stages graph.json only when it changed)`);
@@ -4359,16 +4430,16 @@ function registerShare(program) {
4359
4430
  });
4360
4431
  applyGlobalOptions(cmd);
4361
4432
  }
4362
- function writeVibgrateGitignore(root2, reports) {
4363
- const dir = path43.join(root2, ".vibgrate");
4433
+ function writeVibgrateGitignore(root3, reports) {
4434
+ const dir = path46.join(root3, ".vibgrate");
4364
4435
  fs14.mkdirSync(dir, { recursive: true });
4365
4436
  const lines = ["# Managed by `vg share` \u2014 keep graph.json committed, ignore the rest", "cache/", "facts.jsonl", "graph.snap"];
4366
4437
  if (!reports) lines.push("graph.html", "GRAPH_REPORT.md");
4367
- fs14.writeFileSync(path43.join(dir, ".gitignore"), `${lines.join("\n")}
4438
+ fs14.writeFileSync(path46.join(dir, ".gitignore"), `${lines.join("\n")}
4368
4439
  `);
4369
4440
  }
4370
- function writeGitAttributes(root2) {
4371
- const file = path43.join(root2, ".gitattributes");
4441
+ function writeGitAttributes(root3) {
4442
+ const file = path46.join(root3, ".gitattributes");
4372
4443
  const line = ".vibgrate/graph.json merge=vg";
4373
4444
  let content = fs14.existsSync(file) ? fs14.readFileSync(file, "utf8") : "";
4374
4445
  if (!content.split("\n").some((l) => l.trim() === line)) {
@@ -4379,14 +4450,14 @@ ${line}
4379
4450
  fs14.writeFileSync(file, content);
4380
4451
  }
4381
4452
  }
4382
- function configureMergeDriver(root2) {
4383
- git(root2, ["config", "merge.vg.name", "vg deterministic graph rebuild"]);
4384
- git(root2, ["config", "merge.vg.driver", "vg build >/dev/null 2>&1; cp .vibgrate/graph.json %A"]);
4453
+ function configureMergeDriver(root3) {
4454
+ git(root3, ["config", "merge.vg.name", "vg deterministic graph rebuild"]);
4455
+ git(root3, ["config", "merge.vg.driver", "vg build >/dev/null 2>&1; cp .vibgrate/graph.json %A"]);
4385
4456
  }
4386
- function installPreCommitHook(root2) {
4387
- const hooksDir = hookDir(root2);
4457
+ function installPreCommitHook(root3) {
4458
+ const hooksDir = hookDir(root3);
4388
4459
  fs14.mkdirSync(hooksDir, { recursive: true });
4389
- const file = path43.join(hooksDir, "pre-commit");
4460
+ const file = path46.join(hooksDir, "pre-commit");
4390
4461
  const block = [
4391
4462
  HOOK_BEGIN,
4392
4463
  "if command -v vg >/dev/null 2>&1; then",
@@ -4407,34 +4478,34 @@ ${content}`;
4407
4478
  fs14.writeFileSync(file, content);
4408
4479
  fs14.chmodSync(file, 493);
4409
4480
  }
4410
- function undo(root2) {
4411
- const hookFile = path43.join(hookDir(root2), "pre-commit");
4481
+ function undo(root3) {
4482
+ const hookFile = path46.join(hookDir(root3), "pre-commit");
4412
4483
  if (fs14.existsSync(hookFile)) {
4413
4484
  const re = new RegExp(`\\n*${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}\\n*`);
4414
4485
  const next2 = fs14.readFileSync(hookFile, "utf8").replace(re, "\n");
4415
4486
  if (next2.trim() === "#!/bin/sh") fs14.rmSync(hookFile);
4416
4487
  else fs14.writeFileSync(hookFile, next2);
4417
4488
  }
4418
- const attr = path43.join(root2, ".gitattributes");
4489
+ const attr = path46.join(root3, ".gitattributes");
4419
4490
  if (fs14.existsSync(attr)) {
4420
4491
  const next2 = fs14.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
4421
4492
  fs14.writeFileSync(attr, next2);
4422
4493
  }
4423
4494
  try {
4424
- git(root2, ["config", "--remove-section", "merge.vg"]);
4495
+ git(root3, ["config", "--remove-section", "merge.vg"]);
4425
4496
  } catch {
4426
4497
  }
4427
4498
  }
4428
- function hookDir(root2) {
4499
+ function hookDir(root3) {
4429
4500
  try {
4430
- const configured = git(root2, ["config", "--get", "core.hooksPath"]).trim();
4431
- if (configured) return path43.resolve(root2, configured);
4501
+ const configured = git(root3, ["config", "--get", "core.hooksPath"]).trim();
4502
+ if (configured) return path46.resolve(root3, configured);
4432
4503
  } catch {
4433
4504
  }
4434
- return path43.join(root2, ".git", "hooks");
4505
+ return path46.join(root3, ".git", "hooks");
4435
4506
  }
4436
- function git(root2, args) {
4437
- return execFileSync("git", args, { cwd: root2, encoding: "utf8" });
4507
+ function git(root3, args) {
4508
+ return execFileSync("git", args, { cwd: root3, encoding: "utf8" });
4438
4509
  }
4439
4510
  function escapeRe(s) {
4440
4511
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -4443,9 +4514,9 @@ var DEFAULT_DEBOUNCE_MS = 400;
4443
4514
  function registerWatch(program) {
4444
4515
  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) {
4445
4516
  const global = readGlobal(this);
4446
- const root2 = rootOf(global);
4517
+ const root3 = rootOf(global);
4447
4518
  const debounceMs = Math.max(50, Number(opts.debounce) || DEFAULT_DEBOUNCE_MS);
4448
- const watchRoots = (paths.length ? paths : ["."]).map((p) => path43.resolve(root2, p));
4519
+ const watchRoots = (paths.length ? paths : ["."]).map((p) => path46.resolve(root3, p));
4449
4520
  for (const w of watchRoots) {
4450
4521
  if (!fs14.existsSync(w)) {
4451
4522
  throw new CliError(`watch path not found: ${w}`, ExitCode.USAGE_ERROR);
@@ -4463,18 +4534,18 @@ function registerWatch(program) {
4463
4534
  try {
4464
4535
  info(c.dim(`vg watch \xB7 rebuild (${reason})`));
4465
4536
  const result = await buildGraph({
4466
- root: root2,
4537
+ root: root3,
4467
4538
  paths: paths.length ? paths : void 0,
4468
4539
  fast: opts.fast === true,
4469
4540
  noGround: false
4470
4541
  });
4471
4542
  writeArtifacts(result.graph, {
4472
- root: root2,
4543
+ root: root3,
4473
4544
  html: opts.html !== false,
4474
4545
  report: opts.report !== false,
4475
4546
  graphPath: global.graph
4476
4547
  });
4477
- writeSnapshot(root2, result.graph.provenance.corpusHash, result.fileStats);
4548
+ writeSnapshot(root3, result.graph.provenance.corpusHash, result.fileStats);
4478
4549
  const { counts } = result.graph.meta;
4479
4550
  info(
4480
4551
  `${c.cyan("vg watch")} \xB7 ${counts.nodes} nodes \xB7 ${counts.edges} edges \xB7 ${(result.timing.totalMs / 1e3).toFixed(2)}s` + (result.statHits ? c.dim(` \xB7 ${result.statHits} mtime hits`) : "")
@@ -4513,12 +4584,12 @@ function registerWatch(program) {
4513
4584
  throw new CliError(`cannot watch ${w}: ${err.message}`, ExitCode.ERROR);
4514
4585
  }
4515
4586
  }
4516
- info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) => path43.relative(root2, p) || ".").join(", ")} (debounce ${debounceMs}ms) \xB7 Ctrl+C to stop`));
4517
- await new Promise((resolve21) => {
4587
+ info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) => path46.relative(root3, p) || ".").join(", ")} (debounce ${debounceMs}ms) \xB7 Ctrl+C to stop`));
4588
+ await new Promise((resolve23) => {
4518
4589
  const stop = () => {
4519
4590
  for (const w of watchers) w.close();
4520
4591
  if (timer) clearTimeout(timer);
4521
- resolve21();
4592
+ resolve23();
4522
4593
  };
4523
4594
  process.once("SIGINT", stop);
4524
4595
  process.once("SIGTERM", stop);
@@ -4529,7 +4600,7 @@ function registerWatch(program) {
4529
4600
  function registerTests(program) {
4530
4601
  const cmd = program.command("tests").description("which tests cover a node (call/coverage linkage)").argument("<name>", "node to inspect").option("--missing", "show untested nodes nearby instead").option("--run", "print the command to run exactly these tests").option("--exec", "run that command").option("--pick <n>", "pick the nth candidate when ambiguous").action(function(name, opts) {
4531
4602
  const global = readGlobal(this);
4532
- const { root: root2, graph } = requireGraph(global);
4603
+ const { root: root3, graph } = requireGraph(global);
4533
4604
  const { node: node2, candidates } = resolveOne(graph, name, opts.pick ? Number(opts.pick) : void 0);
4534
4605
  if (!node2) throw ambiguityError(`"${name}" ${candidates.length ? "is ambiguous" : "not found"}`, candidates);
4535
4606
  if (opts.missing) {
@@ -4549,7 +4620,7 @@ function registerTests(program) {
4549
4620
  const command = runner.command(testFiles);
4550
4621
  if (opts.exec) {
4551
4622
  info(c.dim(`$ ${command}`));
4552
- execSync(command, { cwd: root2, stdio: "inherit" });
4623
+ execSync(command, { cwd: root3, stdio: "inherit" });
4553
4624
  return;
4554
4625
  }
4555
4626
  if (global.json) json({ runner: runner.name, command, testFiles });
@@ -4665,16 +4736,16 @@ function registerGuide(program) {
4665
4736
  function registerWhy(program) {
4666
4737
  const cmd = program.command("why").description("who introduced a dependency (and any open vulnerabilities), from git history").argument("<package>", "package name to explain").action(async function(pkg) {
4667
4738
  const global = readGlobal(this);
4668
- const root2 = rootOf(global);
4669
- if (!await gitHistoryAvailable(root2)) {
4739
+ const root3 = rootOf(global);
4740
+ if (!await gitHistoryAvailable(root3)) {
4670
4741
  throw new CliError(
4671
4742
  "git history is required for `vg why` (not a git repository, or git is unavailable)",
4672
4743
  ExitCode.USAGE_ERROR
4673
4744
  );
4674
4745
  }
4675
- const timelines = await buildVersionTimelines(root2);
4746
+ const timelines = await buildVersionTimelines(root3);
4676
4747
  const pt = timelines ? findPackageAnyEcosystem(timelines, pkg) ?? null : null;
4677
- const artifact = readScanArtifact(root2);
4748
+ const artifact = readScanArtifact(root3);
4678
4749
  const vulnPkg = artifact?.extended?.vulnerabilities?.packages.find((p) => p.package === pkg) ?? null;
4679
4750
  if (global.json) {
4680
4751
  json({ package: pkg, history: pt?.changes ?? [], vulnerabilities: vulnPkg?.advisories ?? [] });
@@ -4743,8 +4814,8 @@ function registerBisect(program) {
4743
4814
  const cmd = program.command("bisect").description("pinpoint the commit where a dependency crossed a version line, from git history").argument("<package>", "package name to bisect").argument("<constraint>", 'target version or semver range (a bare version means ">=" that version)').option("--assert", "exit non-zero when the current version does not satisfy the constraint (CI gate)").action(async function(pkg, constraintArg) {
4744
4815
  const global = readGlobal(this);
4745
4816
  const opts = this.opts();
4746
- const root2 = rootOf(global);
4747
- if (!await gitHistoryAvailable(root2)) {
4817
+ const root3 = rootOf(global);
4818
+ if (!await gitHistoryAvailable(root3)) {
4748
4819
  throw new CliError(
4749
4820
  "git history is required for `vg bisect` (not a git repository, or git is unavailable)",
4750
4821
  ExitCode.USAGE_ERROR
@@ -4757,7 +4828,7 @@ function registerBisect(program) {
4757
4828
  ExitCode.USAGE_ERROR
4758
4829
  );
4759
4830
  }
4760
- const timelines = await buildVersionTimelines(root2);
4831
+ const timelines = await buildVersionTimelines(root3);
4761
4832
  const pt = timelines ? findPackageAnyEcosystem(timelines, pkg) ?? null : null;
4762
4833
  if (!pt || pt.changes.length === 0) {
4763
4834
  if (global.json) {
@@ -4844,9 +4915,9 @@ function normalizeStandards(raw) {
4844
4915
  }
4845
4916
  return { banned };
4846
4917
  }
4847
- function loadStandards(root2) {
4918
+ function loadStandards(root3) {
4848
4919
  for (const rel of STANDARDS_FILES) {
4849
- const p = path43__default.join(root2, rel);
4920
+ const p = path46__default.join(root3, rel);
4850
4921
  let text;
4851
4922
  try {
4852
4923
  text = fs14__default.readFileSync(p, "utf8");
@@ -4874,18 +4945,18 @@ function checkStandards(policy, records) {
4874
4945
  function registerDrift(program) {
4875
4946
  const cmd = program.command("drift").description("what is outdated across dependencies (offline; --online for currency)").option("--online", "query the npm registry for latest versions (opt-in network)").option("--fail-on <level>", "exit 2 on violations \u2014 major|minor (version drift) or standards (enterprise policy)").action(async function(opts) {
4876
4947
  const global = readGlobal(this);
4877
- const root2 = rootOf(global);
4948
+ const root3 = rootOf(global);
4878
4949
  if (opts.online && global.local) {
4879
4950
  throw new CliError("--online conflicts with --local (no network in local mode)", ExitCode.USAGE_ERROR);
4880
4951
  }
4881
4952
  if (opts.failOn && !["major", "minor", "standards"].includes(opts.failOn)) {
4882
4953
  throw new CliError(`--fail-on must be one of: major, minor, standards (got "${opts.failOn}")`, ExitCode.USAGE_ERROR);
4883
4954
  }
4884
- const inv = inventory(root2);
4955
+ const inv = inventory(root3);
4885
4956
  if (opts.online && !global.local) await enrichOnline(inv.records);
4886
4957
  let standards = null;
4887
4958
  if (opts.failOn === "standards") {
4888
- const loaded = loadStandards(root2);
4959
+ const loaded = loadStandards(root3);
4889
4960
  if (loaded.path && !loaded.policy) {
4890
4961
  throw new CliError(`standards policy at ${loaded.path} is not valid JSON`, ExitCode.USAGE_ERROR);
4891
4962
  }
@@ -5567,8 +5638,8 @@ async function runHostBench(options = {}) {
5567
5638
  }
5568
5639
  async function runArmSimulate(input) {
5569
5640
  const { arm, lib, modelPath, turns, trie, env, live } = input;
5570
- const path45 = `${modelPath}#${arm.id}`;
5571
- const session = await acquireHostSession(lib, path45);
5641
+ const path48 = `${modelPath}#${arm.id}`;
5642
+ const session = await acquireHostSession(lib, path48);
5572
5643
  const system = "You are a coding agent with a Task Capsule.";
5573
5644
  const capsule = "function readConfig(): Config { return load(); }";
5574
5645
  let last = null;
@@ -6426,8 +6497,8 @@ function registerModels(program) {
6426
6497
  printRawFleet(report);
6427
6498
  return;
6428
6499
  }
6429
- const root2 = rootOf(global);
6430
- const orch = buildFullStatus(root2, models, sys);
6500
+ const root3 = rootOf(global);
6501
+ const orch = buildFullStatus(root3, models, sys);
6431
6502
  const backends = probeInferenceBackends({ hasBinary });
6432
6503
  if (global.json) {
6433
6504
  json({ ...orch, fleet: report, backends });
@@ -6511,9 +6582,9 @@ function registerModels(program) {
6511
6582
  );
6512
6583
  });
6513
6584
  applyGlobalOptions(modeCmd);
6514
- const resolve21 = cmd.command("resolve").description("resolve a Code Mode to pack + underlying model + fit (no download)").argument("[mode]", "spark | flow | forge (default: auto-fit or configured default)").action(function(modeArg) {
6585
+ const resolve23 = cmd.command("resolve").description("resolve a Code Mode to pack + underlying model + fit (no download)").argument("[mode]", "spark | flow | forge (default: auto-fit or configured default)").action(function(modeArg) {
6515
6586
  const global = readGlobal(this);
6516
- const root2 = rootOf(global);
6587
+ const root3 = rootOf(global);
6517
6588
  const sys = gatherSystemMemory();
6518
6589
  const state = readOrchestratorState();
6519
6590
  const mode = parseMode(modeArg) ?? state.defaultMode;
@@ -6522,7 +6593,7 @@ function registerModels(program) {
6522
6593
  mode: mode ?? void 0,
6523
6594
  autoFit: !mode,
6524
6595
  system: sys,
6525
- repo: repoSignals(root2),
6596
+ repo: repoSignals(root3),
6526
6597
  pin: pin2,
6527
6598
  defaultMode: state.defaultMode
6528
6599
  });
@@ -6543,10 +6614,10 @@ function registerModels(program) {
6543
6614
  info(c.yellow(" will not fit \u2014 choose a lower mode or free memory before loading"));
6544
6615
  }
6545
6616
  });
6546
- applyGlobalOptions(resolve21);
6617
+ applyGlobalOptions(resolve23);
6547
6618
  const installCmd = cmd.command("install").description("resolve the best Code Mode for this machine and install the pack").argument("[mode]", "spark | flow | forge (default: auto-fit)").option("--dry-run", "print the install plan only \u2014 do not download").option("--yes", "skip prompts (install already runs by default)").action(async function(modeArg, opts) {
6548
6619
  const global = readGlobal(this);
6549
- const root2 = rootOf(global);
6620
+ const root3 = rootOf(global);
6550
6621
  const models = discoverModels();
6551
6622
  const sys = gatherSystemMemory();
6552
6623
  const state = readOrchestratorState();
@@ -6556,7 +6627,7 @@ function registerModels(program) {
6556
6627
  mode: mode ?? void 0,
6557
6628
  autoFit: !mode,
6558
6629
  system: sys,
6559
- repo: repoSignals(root2),
6630
+ repo: repoSignals(root3),
6560
6631
  pin: pin2,
6561
6632
  installedNames: models.map((m) => m.name),
6562
6633
  defaultMode: state.defaultMode
@@ -6932,8 +7003,8 @@ function registerModels(program) {
6932
7003
  }
6933
7004
  });
6934
7005
  if (opts.out) {
6935
- const outPath = path43.resolve(opts.out);
6936
- fs14.mkdirSync(path43.dirname(outPath), { recursive: true });
7006
+ const outPath = path46.resolve(opts.out);
7007
+ fs14.mkdirSync(path46.dirname(outPath), { recursive: true });
6937
7008
  fs14.writeFileSync(outPath, `${JSON.stringify(report, null, 2)}
6938
7009
  `);
6939
7010
  if (!global.json) info(c.dim(` wrote ${outPath}`));
@@ -6978,9 +7049,9 @@ function hasBinary(bin) {
6978
7049
  const probe = spawnSync(process.platform === "win32" ? "where" : "which", [bin], { stdio: "ignore" });
6979
7050
  return probe.status === 0;
6980
7051
  }
6981
- function repoSignals(root2) {
7052
+ function repoSignals(root3) {
6982
7053
  try {
6983
- const g = loadGraph(root2);
7054
+ const g = loadGraph(root3);
6984
7055
  if (!g) return {};
6985
7056
  const fileCount = g.nodes.filter((n) => n.kind === "file").length;
6986
7057
  return { fileCount, graphNodeCount: g.nodes.length };
@@ -6988,12 +7059,12 @@ function repoSignals(root2) {
6988
7059
  return {};
6989
7060
  }
6990
7061
  }
6991
- function buildFullStatus(root2, models, sys) {
7062
+ function buildFullStatus(root3, models, sys) {
6992
7063
  const state = readOrchestratorState();
6993
7064
  const freeDiskBytes = freeBytesOnVolume(weightStoreDir());
6994
7065
  return buildOrchestratorStatus({
6995
7066
  system: sys,
6996
- repo: repoSignals(root2),
7067
+ repo: repoSignals(root3),
6997
7068
  installedNames: models.map((m) => m.name),
6998
7069
  freeDiskBytes,
6999
7070
  pins: state.pins,
@@ -7048,11 +7119,11 @@ function fitColor(label) {
7048
7119
  return label;
7049
7120
  }
7050
7121
  function confirmYesNo(question) {
7051
- return new Promise((resolve21) => {
7122
+ return new Promise((resolve23) => {
7052
7123
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
7053
7124
  rl.question(`${question} [y/N] `, (answer) => {
7054
7125
  rl.close();
7055
- resolve21(/^y(es)?$/i.test(answer.trim()));
7126
+ resolve23(/^y(es)?$/i.test(answer.trim()));
7056
7127
  });
7057
7128
  });
7058
7129
  }
@@ -7083,14 +7154,14 @@ function resolveUninstallTarget(name, runtimeOpt) {
7083
7154
  }
7084
7155
  return exact[0];
7085
7156
  }
7086
- const base = path43.basename(wantLc);
7157
+ const base = path46.basename(wantLc);
7087
7158
  const soft = fleet.filter((m) => {
7088
7159
  const n = m.name.toLowerCase();
7089
- return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) || path43.basename(n) === base;
7160
+ return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) || path46.basename(n) === base;
7090
7161
  });
7091
7162
  if (soft.length === 1) return soft[0];
7092
7163
  if (soft.length > 1) {
7093
- const baseHits = soft.filter((m) => path43.basename(m.name).toLowerCase() === base);
7164
+ const baseHits = soft.filter((m) => path46.basename(m.name).toLowerCase() === base);
7094
7165
  const pool = baseHits.length ? baseHits : soft;
7095
7166
  const order = ["ollama", "gguf", "lm-studio"];
7096
7167
  for (const rt of order) {
@@ -7127,12 +7198,12 @@ function printBackends(backends) {
7127
7198
  info(c.dim(` [${mark}] ${b.id} \xB7 ${b.detail ?? b.label}`));
7128
7199
  }
7129
7200
  }
7130
- function codeConfigPath(root2) {
7131
- return path43.join(root2, ".vibgrate", "code.json");
7201
+ function codeConfigPath(root3) {
7202
+ return path46.join(root3, ".vibgrate", "code.json");
7132
7203
  }
7133
- function loadCodeConfig(root2) {
7204
+ function loadCodeConfig(root3) {
7134
7205
  try {
7135
- const raw = JSON.parse(fs14.readFileSync(codeConfigPath(root2), "utf8"));
7206
+ const raw = JSON.parse(fs14.readFileSync(codeConfigPath(root3), "utf8"));
7136
7207
  return sanitize(raw);
7137
7208
  } catch {
7138
7209
  return {};
@@ -7206,11 +7277,11 @@ function readMcpServersFile(file) {
7206
7277
  return {};
7207
7278
  }
7208
7279
  }
7209
- function discoverMcpServers(root2, ownServers) {
7280
+ function discoverMcpServers(root3, ownServers) {
7210
7281
  const servers = {};
7211
7282
  const sources = [];
7212
7283
  for (const rel of MCP_CONFIG_FILES) {
7213
- const found = readMcpServersFile(path43.join(root2, rel));
7284
+ const found = readMcpServersFile(path46.join(root3, rel));
7214
7285
  const names = Object.keys(found);
7215
7286
  if (names.length) {
7216
7287
  sources.push(rel);
@@ -7245,11 +7316,11 @@ function registerCode(program) {
7245
7316
  if (!files.length) {
7246
7317
  throw usageError("--restore-checkpoint needs the files to restore, e.g. -f src/a.ts -f src/b.ts");
7247
7318
  }
7248
- const root3 = rootOf(global);
7319
+ const root4 = rootOf(global);
7249
7320
  const child = await import('child_process');
7250
7321
  const gitRunner = (gitArgs, gitEnv) => {
7251
7322
  const res = child.spawnSync("git", gitArgs, {
7252
- cwd: root3,
7323
+ cwd: root4,
7253
7324
  encoding: "utf8",
7254
7325
  env: { ...process.env, ...gitEnv ?? {} },
7255
7326
  timeout: 3e4,
@@ -7277,10 +7348,10 @@ function registerCode(program) {
7277
7348
  const child = await import('child_process');
7278
7349
  const os7 = await import('os');
7279
7350
  const pathMod = await import('path');
7280
- const root3 = rootOf(global);
7351
+ const root4 = rootOf(global);
7281
7352
  const gitRunner = (gitArgs, gitEnv) => {
7282
7353
  const res = child.spawnSync("git", gitArgs, {
7283
- cwd: root3,
7354
+ cwd: root4,
7284
7355
  encoding: "utf8",
7285
7356
  env: { ...process.env, ...gitEnv ?? {} },
7286
7357
  timeout: 6e4,
@@ -7292,7 +7363,7 @@ function registerCode(program) {
7292
7363
  if (opts.streamJson) process.stdout.write(JSON.stringify(line) + "\n");
7293
7364
  else if (global.json) json(line);
7294
7365
  };
7295
- const findWt = (id) => wt.listSessionWorktrees(gitRunner, root3).find((w) => w.id === id);
7366
+ const findWt = (id) => wt.listSessionWorktrees(gitRunner, root4).find((w) => w.id === id);
7296
7367
  if (opts.worktreeDiff) {
7297
7368
  const target = findWt(opts.worktreeDiff);
7298
7369
  if (!target) throw new CliError(`no worktree ${opts.worktreeDiff} under .vibgrate/worktrees`, ExitCode.NOT_FOUND);
@@ -7328,7 +7399,7 @@ function registerCode(program) {
7328
7399
  "delta.patch"
7329
7400
  );
7330
7401
  fs14.writeFileSync(patchFile, diff2.patch);
7331
- const outcome = wt.applyWorktreePatch(gitRunner, root3, patchFile, diff2.files);
7402
+ const outcome = wt.applyWorktreePatch(gitRunner, root4, patchFile, diff2.files);
7332
7403
  emitLine({ event: "worktree-applied", id: target.id, ...outcome });
7333
7404
  if (!opts.streamJson && !global.json) {
7334
7405
  out(
@@ -7339,7 +7410,7 @@ function registerCode(program) {
7339
7410
  return;
7340
7411
  }
7341
7412
  if (opts.worktreeRemove) {
7342
- const outcome = wt.removeSessionWorktree(gitRunner, root3, opts.worktreeRemove);
7413
+ const outcome = wt.removeSessionWorktree(gitRunner, root4, opts.worktreeRemove);
7343
7414
  emitLine({ event: "worktree-removed", id: opts.worktreeRemove, ...outcome });
7344
7415
  if (!opts.streamJson && !global.json) {
7345
7416
  out(outcome.removed ? `removed worktree ${opts.worktreeRemove}` : `remove failed: ${outcome.error}`);
@@ -7393,7 +7464,7 @@ function registerCode(program) {
7393
7464
  try {
7394
7465
  const { parseMode: parseMode2, resolveMode: resolveMode2, readOrchestratorState: readOrchestratorState2, activePin: activePin2 } = await import('./model-orchestrator-LHYPJBFH.js');
7395
7466
  const { gatherSystemMemory: gatherSystemMemory2 } = await import('./local-runtime-6SNWKSA7.js');
7396
- const { loadGraph: loadGraph2 } = await import('./load-OR2XVGAV.js');
7467
+ const { loadGraph: loadGraph2 } = await import('./load-AYFECIQB.js');
7397
7468
  const state = readOrchestratorState2();
7398
7469
  const mode = parseMode2(modeFlag) ?? state.defaultMode ?? void 0;
7399
7470
  const pin = mode ? activePin2(mode, state) : null;
@@ -7478,7 +7549,7 @@ function registerCode(program) {
7478
7549
  if (!(process.stdin.isTTY && process.stdout.isTTY)) {
7479
7550
  throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"` (guided mode needs an interactive terminal)');
7480
7551
  }
7481
- const { runInteractive } = await import('./interactive-FXPAZAUJ.js');
7552
+ const { runInteractive } = await import('./interactive-SR7TMF6M.js');
7482
7553
  await runInteractive(
7483
7554
  rootOf(global),
7484
7555
  global,
@@ -7508,7 +7579,7 @@ function registerCode(program) {
7508
7579
  }
7509
7580
  if (!instruction && !stdinFirstTurn) throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"`');
7510
7581
  {
7511
- const { ensureCodeMap } = await import('./ensure-map-5C6KG66X.js');
7582
+ const { ensureCodeMap } = await import('./ensure-map-342G2IV3.js');
7512
7583
  const mapRoot = rootOf(global);
7513
7584
  try {
7514
7585
  await ensureCodeMap(mapRoot, global, {
@@ -7541,7 +7612,7 @@ function registerCode(program) {
7541
7612
  throw e;
7542
7613
  }
7543
7614
  }
7544
- const { root: root2, graph } = requireGraph(global);
7615
+ const { root: root3, graph } = requireGraph(global);
7545
7616
  let mockReply;
7546
7617
  if (opts.mock) {
7547
7618
  try {
@@ -7593,21 +7664,21 @@ function registerCode(program) {
7593
7664
  process.stdout.write(JSON.stringify(line) + "\n");
7594
7665
  };
7595
7666
  try {
7596
- const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-M4546HLJ.js');
7667
+ const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-YQQJG3LW.js');
7597
7668
  const primarySlug = route.providers[0]?.model.includes("/") ? route.providers[0].model.split("/")[0] : route.providers[0]?.id;
7598
- const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-4DL65D6U.js');
7599
- const { nodeCodeFs } = await import('./session-HCFJUSKN.js');
7669
+ const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-55YDNAFX.js');
7670
+ const { nodeCodeFs } = await import('./session-H34HUV2Y.js');
7600
7671
  const readline6 = await import('readline');
7601
7672
  const child = await import('child_process');
7602
7673
  const { loadOrDiscoverFederation } = await import('./federation-MDQPO3PW.js');
7603
7674
  const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
7604
7675
  const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
7605
- const { startCodeRuntimeSession } = await import('./runtime-session-6KKMFS6Q.js');
7606
- const { resolveGraphBackend } = await import('./graph-backend-HH5IIQ55.js');
7676
+ const { startCodeRuntimeSession } = await import('./runtime-session-CNJ4DAV5.js');
7677
+ const { resolveGraphBackend } = await import('./graph-backend-RNWQBT3P.js');
7607
7678
  const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
7608
7679
  const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
7609
- const { vgdRequest } = await import('./vgd-QR26FAZD.js');
7610
- const federation = loadOrDiscoverFederation(root2);
7680
+ const { vgdRequest } = await import('./vgd-URUZQORN.js');
7681
+ const federation = loadOrDiscoverFederation(root3);
7611
7682
  const modelProfile = mergeModelExecutionProfile(
7612
7683
  resolveModelExecutionProfile({
7613
7684
  providerId: primarySlug,
@@ -7618,13 +7689,13 @@ function registerCode(program) {
7618
7689
  config.modelProfile
7619
7690
  );
7620
7691
  const tier = securityTier ?? modelProfile.securityTier ?? (auto ? "L1" : "L0");
7621
- const writableRoots = federation?.members.map((m) => m.root) ?? [root2];
7692
+ const writableRoots = federation?.members.map((m) => m.root) ?? [root3];
7622
7693
  const executionEnv = resolveExecutionEnv(tier, { writableRoots });
7623
- const runtime = await startCodeRuntimeSession({ root: root2 });
7624
- const git2 = detectGitRef(root2);
7625
- const repositoryId = repositoryIdFromRoot(root2);
7694
+ const runtime = await startCodeRuntimeSession({ root: root3 });
7695
+ const git2 = detectGitRef(root3);
7696
+ const repositoryId = repositoryIdFromRoot(root3);
7626
7697
  const runOutcome = new RunOutcomeRecorder(
7627
- root2,
7698
+ root3,
7628
7699
  repositoryId,
7629
7700
  git2.ref || null,
7630
7701
  { mode: codeModeActive ? modeFlag : void 0, provider: primarySlug, model: route.providers[0]?.model },
@@ -7652,7 +7723,7 @@ function registerCode(program) {
7652
7723
  });
7653
7724
  const run = (command) => {
7654
7725
  const res = child.spawnSync(command, {
7655
- cwd: root2,
7726
+ cwd: root3,
7656
7727
  shell: true,
7657
7728
  encoding: "utf8",
7658
7729
  timeout: 12e4,
@@ -7664,10 +7735,10 @@ ${res.stderr}` : ""),
7664
7735
  exitCode: res.status ?? 1
7665
7736
  };
7666
7737
  };
7667
- const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-FZGX6DYO.js');
7738
+ const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-2CNLHNEL.js');
7668
7739
  const vgBuiltin = createVgBuiltinMcpTools({
7669
7740
  getGraph: () => graph,
7670
- root: root2,
7741
+ root: root3,
7671
7742
  local: global.local === true
7672
7743
  });
7673
7744
  let disposeMcpStream;
@@ -7693,7 +7764,7 @@ ${res.stderr}` : ""),
7693
7764
  );
7694
7765
  const gitRunner = (gitArgs, gitEnv) => {
7695
7766
  const res = child.spawnSync("git", gitArgs, {
7696
- cwd: root2,
7767
+ cwd: root3,
7697
7768
  encoding: "utf8",
7698
7769
  env: { ...process.env, ...gitEnv ?? {} },
7699
7770
  timeout: 3e4,
@@ -7709,9 +7780,9 @@ ${res.stderr}` : ""),
7709
7780
  files,
7710
7781
  indexFile: checkpointIndex
7711
7782
  });
7712
- const { loadApprovalRules, addApprovalRule, ruleForAction, ruleLabel } = await import('./approvals-IPMUDEDV.js');
7783
+ const { loadApprovalRules, addApprovalRule, ruleForAction, ruleLabel } = await import('./approvals-ASPJQUZK.js');
7713
7784
  const { loadProjectRules, nodeRulesReader } = await import('./rules-ASYEUNB5.js');
7714
- const projectRules = loadProjectRules(nodeRulesReader(root2));
7785
+ const projectRules = loadProjectRules(nodeRulesReader(root3));
7715
7786
  if (projectRules.sources.length && !global.quiet) {
7716
7787
  emitStream({
7717
7788
  event: "event",
@@ -7721,9 +7792,9 @@ ${res.stderr}` : ""),
7721
7792
  }
7722
7793
  const baseAgentOptions = {
7723
7794
  graph,
7724
- root: root2,
7795
+ root: root3,
7725
7796
  providers: route.providers,
7726
- fsImpl: nodeCodeFs(root2),
7797
+ fsImpl: nodeCodeFs(root3),
7727
7798
  run,
7728
7799
  // Live command-output events for the VG Code panel (async shell).
7729
7800
  // Sandboxed executionEnv still uses its own runner when set.
@@ -7767,8 +7838,8 @@ ${res.stderr}` : ""),
7767
7838
  };
7768
7839
  try {
7769
7840
  if (opts.session) {
7770
- const { runCodeStreamJsonSession, STREAM_JSON_PROTOCOL_VERSION } = await import('./stream-json-4DL65D6U.js');
7771
- const { runAgent: runAgent2 } = await import('./agent-CUYQKC6S.js');
7841
+ const { runCodeStreamJsonSession, STREAM_JSON_PROTOCOL_VERSION } = await import('./stream-json-55YDNAFX.js');
7842
+ const { runAgent: runAgent2 } = await import('./agent-UQC7QN77.js');
7772
7843
  const {
7773
7844
  condenseSession,
7774
7845
  loadLatestSession,
@@ -7779,7 +7850,7 @@ ${res.stderr}` : ""),
7779
7850
  sessionsDir,
7780
7851
  summarizeSession
7781
7852
  } = await import('./session-store-AQUORKWB.js');
7782
- const { VERSION: VERSION2 } = await import('./version-UPIC3F4P.js');
7853
+ const { VERSION: VERSION2 } = await import('./version-WSE3QOPK.js');
7783
7854
  const resumedRecord = typeof opts.continue === "string" ? loadSession(mainRoot, opts.continue) : opts.continue ? loadLatestSession(mainRoot) : void 0;
7784
7855
  if (typeof opts.continue === "string" && !resumedRecord) {
7785
7856
  emitStream({
@@ -7811,8 +7882,8 @@ ${res.stderr}` : ""),
7811
7882
  if (ended) return Promise.resolve(null);
7812
7883
  const queued = pendingTurns.shift();
7813
7884
  if (queued !== void 0) return Promise.resolve(queued);
7814
- return new Promise((resolve21) => {
7815
- wakeTurn = resolve21;
7885
+ return new Promise((resolve23) => {
7886
+ wakeTurn = resolve23;
7816
7887
  });
7817
7888
  };
7818
7889
  let turnIndex = 0;
@@ -7991,8 +8062,8 @@ ${res.stderr}` : ""),
7991
8062
  if (!autonomous && !tty) {
7992
8063
  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).");
7993
8064
  }
7994
- const { agentTask } = await import('./interactive-FXPAZAUJ.js');
7995
- const { nodeCodeFs } = await import('./session-HCFJUSKN.js');
8065
+ const { agentTask } = await import('./interactive-SR7TMF6M.js');
8066
+ const { nodeCodeFs } = await import('./session-H34HUV2Y.js');
7996
8067
  const { TtyPrompter } = await import('./ui-K7D3BCHD.js');
7997
8068
  let priorSummary;
7998
8069
  let priorInstruction;
@@ -8007,10 +8078,10 @@ ${res.stderr}` : ""),
8007
8078
  }
8008
8079
  }
8009
8080
  const prompter = tty ? new TtyPrompter() : void 0;
8010
- const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-FZGX6DYO.js');
8081
+ const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-2CNLHNEL.js');
8011
8082
  const vgBuiltin = createVgBuiltinMcpTools({
8012
8083
  getGraph: () => graph,
8013
- root: root2,
8084
+ root: root3,
8014
8085
  local: global.local === true
8015
8086
  });
8016
8087
  let projectMcp;
@@ -8031,12 +8102,12 @@ ${res.stderr}` : ""),
8031
8102
  const { loadFederation } = await import('./federation-MDQPO3PW.js');
8032
8103
  const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
8033
8104
  const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
8034
- const { startCodeRuntimeSession } = await import('./runtime-session-6KKMFS6Q.js');
8035
- const { resolveGraphBackend } = await import('./graph-backend-HH5IIQ55.js');
8105
+ const { startCodeRuntimeSession } = await import('./runtime-session-CNJ4DAV5.js');
8106
+ const { resolveGraphBackend } = await import('./graph-backend-RNWQBT3P.js');
8036
8107
  const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
8037
8108
  const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
8038
- const { vgdRequest } = await import('./vgd-QR26FAZD.js');
8039
- const federation = loadFederation(root2);
8109
+ const { vgdRequest } = await import('./vgd-URUZQORN.js');
8110
+ const federation = loadFederation(root3);
8040
8111
  const modelProfile = mergeModelExecutionProfile(
8041
8112
  resolveModelExecutionProfile({
8042
8113
  providerId: primarySlug,
@@ -8048,11 +8119,11 @@ ${res.stderr}` : ""),
8048
8119
  );
8049
8120
  const tier = securityTier ?? modelProfile.securityTier ?? (autonomous ? "L1" : "L0");
8050
8121
  const executionEnv = resolveExecutionEnv(tier, {
8051
- writableRoots: federation?.members.map((m) => m.root) ?? [root2]
8122
+ writableRoots: federation?.members.map((m) => m.root) ?? [root3]
8052
8123
  });
8053
- const runtime = await startCodeRuntimeSession({ root: root2 });
8054
- const git2 = detectGitRef(root2);
8055
- const repositoryId = repositoryIdFromRoot(root2);
8124
+ const runtime = await startCodeRuntimeSession({ root: root3 });
8125
+ const git2 = detectGitRef(root3);
8126
+ const repositoryId = repositoryIdFromRoot(root3);
8056
8127
  if (runtime.socketPath && git2.ref) {
8057
8128
  try {
8058
8129
  await vgdRequest(
@@ -8070,11 +8141,11 @@ ${res.stderr}` : ""),
8070
8141
  });
8071
8142
  try {
8072
8143
  const agentResult = await agentTask({
8073
- root: root2,
8144
+ root: root3,
8074
8145
  graph,
8075
8146
  instruction,
8076
8147
  providers: route.providers,
8077
- fsImpl: nodeCodeFs(root2),
8148
+ fsImpl: nodeCodeFs(root3),
8078
8149
  attribution: { client: "vg-code", provider: primarySlug, model: route.providers[0]?.model },
8079
8150
  auto: autonomous,
8080
8151
  maxSteps,
@@ -8107,7 +8178,7 @@ ${res.stderr}` : ""),
8107
8178
  }
8108
8179
  let consent = !!opts.yes;
8109
8180
  if (opts.apply && !consent && process.stdin.isTTY && process.stdout.isTTY) {
8110
- consent = await confirm2(`Apply the proposed change to ${root2}?`);
8181
+ consent = await confirm2(`Apply the proposed change to ${root3}?`);
8111
8182
  }
8112
8183
  const onPhase = (phase, detail) => {
8113
8184
  if (!global.json && !global.quiet) info(c.dim(` ${phase}: ${detail}`));
@@ -8116,7 +8187,7 @@ ${res.stderr}` : ""),
8116
8187
  const providerSlug = primary?.model.includes("/") ? primary.model.split("/")[0] : primary?.id;
8117
8188
  const result = await runCodeSession({
8118
8189
  graph,
8119
- root: root2,
8190
+ root: root3,
8120
8191
  instruction,
8121
8192
  providers: route.providers,
8122
8193
  apply: !!opts.apply,
@@ -8181,11 +8252,11 @@ function resolveSecurityTierFlag(flag, fromConfig, auto) {
8181
8252
  return auto ? "L1" : "L0";
8182
8253
  }
8183
8254
  function confirm2(question) {
8184
- return new Promise((resolve21) => {
8255
+ return new Promise((resolve23) => {
8185
8256
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
8186
8257
  rl.question(`${question} [y/N] `, (answer) => {
8187
8258
  rl.close();
8188
- resolve21(/^y(es)?$/i.test(answer.trim()));
8259
+ resolve23(/^y(es)?$/i.test(answer.trim()));
8189
8260
  });
8190
8261
  });
8191
8262
  }
@@ -8195,9 +8266,9 @@ function registerSavings(program) {
8195
8266
  const cmd = program.command("savings").description("local, privacy-safe report of tokens/$ saved vs a grep baseline (estimates)").option("--days <n>", "window in days", "30").option("--clear", "delete the recorded usage data for this repo").action(function() {
8196
8267
  const global = readGlobal(this);
8197
8268
  const days = Number(this.opts().days) || 30;
8198
- const root2 = rootOf(global);
8269
+ const root3 = rootOf(global);
8199
8270
  if (this.opts().clear) {
8200
- const existed = clearSavings(root2);
8271
+ const existed = clearSavings(root3);
8201
8272
  if (global.json) {
8202
8273
  json({ ok: true, cleared: existed });
8203
8274
  return;
@@ -8208,9 +8279,9 @@ function registerSavings(program) {
8208
8279
  return;
8209
8280
  }
8210
8281
  const now = Date.now();
8211
- const report = readSavings(root2, days, now);
8212
- const usage = readUsage(root2, days, now);
8213
- const models = readModelSavings(root2, days, now);
8282
+ const report = readSavings(root3, days, now);
8283
+ const usage = readUsage(root3, days, now);
8284
+ const models = readModelSavings(root3, days, now);
8214
8285
  if (global.json) {
8215
8286
  json({ ...report, usage, models });
8216
8287
  return;
@@ -8292,16 +8363,16 @@ function printSplit(usage) {
8292
8363
  function registerLib(program) {
8293
8364
  const cmd = program.command("lib").description("library currency: version-correct, drift-annotated usage docs").argument("[args...]", "a library name, or: add <source> | publish <name> | resolve <name> | refresh").option("--name <name>", "library name for `add`").option("--version <v>", "pin the doc version for `add`/`publish`").option("--online", "(deprecated; network is on by default) allow network for add/refresh URL sources").option("-b, --budget <n>", "trim docs to ~N tokens").option("--readme <path>", "README path for `publish` (default ./README.md)").option("--dts <path>", "TypeScript declaration (.d.ts) path for `publish`").option("--language <lang>", "primary language for `publish`").option("--region <region>", "data-residency region for the hosted catalog (same as scans; default us)").option("--ingest <url>", "hosted catalog/ingest URL override (host extracted; wins over --region)").action(async function(args, opts) {
8294
8365
  const global = readGlobal(this);
8295
- const root2 = rootOf(global);
8366
+ const root3 = rootOf(global);
8296
8367
  const [verb, ...rest] = args;
8297
8368
  const network = !global.local;
8298
- if (!verb) return listCatalog(root2, global.json);
8369
+ if (!verb) return listCatalog(root3, global.json);
8299
8370
  const online = (Boolean(opts.online) || network) && !global.local;
8300
- if (verb === "add") return addCmd(root2, rest, { ...opts, online }, global.json);
8301
- if (verb === "publish") return publishCmd(root2, rest, opts, global, global.json);
8302
- if (verb === "resolve") return resolveCmd(root2, rest, global.json);
8303
- if (verb === "refresh") return refreshCmd(root2, { online }, global.json);
8304
- return showCmd(root2, args.join(" "), opts.budget ? Number(opts.budget) : void 0, global.json, {
8371
+ if (verb === "add") return addCmd(root3, rest, { ...opts, online }, global.json);
8372
+ if (verb === "publish") return publishCmd(root3, rest, opts, global, global.json);
8373
+ if (verb === "resolve") return resolveCmd(root3, rest, global.json);
8374
+ if (verb === "refresh") return refreshCmd(root3, { online }, global.json);
8375
+ return showCmd(root3, args.join(" "), opts.budget ? Number(opts.budget) : void 0, global.json, {
8305
8376
  online: network,
8306
8377
  // hosted-on-miss allowed unless --local
8307
8378
  region: opts.region,
@@ -8310,8 +8381,8 @@ function registerLib(program) {
8310
8381
  });
8311
8382
  applyGlobalOptions(cmd);
8312
8383
  }
8313
- function listCatalog(root2, asJson) {
8314
- const catalog = loadCatalog(root2);
8384
+ function listCatalog(root3, asJson) {
8385
+ const catalog = loadCatalog(root3);
8315
8386
  const libs = Object.values(catalog.libraries).sort((a, b) => a.name.localeCompare(b.name));
8316
8387
  if (asJson) {
8317
8388
  json(libs.map((l) => ({ id: l.id, name: l.name, version: l.version, source: l.source.type })));
@@ -8319,18 +8390,18 @@ function listCatalog(root2, asJson) {
8319
8390
  }
8320
8391
  info(`${c.cyan("vg lib")} \xB7 ${libs.length} library(ies) in the catalog`);
8321
8392
  for (const l of libs) {
8322
- const d = driftFor(root2, l);
8393
+ const d = driftFor(root3, l);
8323
8394
  const tag = d.drift === "behind" ? c.yellow(" (docs behind your version)") : d.drift === "ahead" ? c.dim(" (docs ahead)") : "";
8324
8395
  info(` ${c.bold(l.name)} ${c.dim(`@${l.version}`)} ${c.dim(`[${l.source.type}]`)}${tag}`);
8325
8396
  }
8326
8397
  if (!libs.length) info(c.dim(" empty \u2014 add docs with `vg lib add <path|url>`"));
8327
8398
  }
8328
- async function addCmd(root2, rest, opts, asJson) {
8399
+ async function addCmd(root3, rest, opts, asJson) {
8329
8400
  const source = rest[0];
8330
8401
  if (!source) throw usageError("usage: vg lib add <path|url|git> [--name <name>] [--version <v>] [--online]");
8331
8402
  let entry;
8332
8403
  try {
8333
- entry = await addLibrary(source, { root: root2, name: opts.name, version: opts.version, allowNetwork: opts.online });
8404
+ entry = await addLibrary(source, { root: root3, name: opts.name, version: opts.version, allowNetwork: opts.online });
8334
8405
  } catch (err) {
8335
8406
  throw new CliError(err.message, ExitCode.ERROR);
8336
8407
  }
@@ -8340,9 +8411,9 @@ async function addCmd(root2, rest, opts, asJson) {
8340
8411
  }
8341
8412
  info(`${c.green("\u2714")} added ${c.bold(entry.name)} ${c.dim(`@${entry.version}`)} (${entry.bytes} bytes) \u2192 ${entry.docFile}`);
8342
8413
  }
8343
- function readPackageVersion(root2) {
8414
+ function readPackageVersion(root3) {
8344
8415
  try {
8345
- const pkg = JSON.parse(fs14__default.readFileSync(path43__default.join(root2, "package.json"), "utf8"));
8416
+ const pkg = JSON.parse(fs14__default.readFileSync(path46__default.join(root3, "package.json"), "utf8"));
8346
8417
  return typeof pkg.version === "string" ? pkg.version : void 0;
8347
8418
  } catch {
8348
8419
  return void 0;
@@ -8356,17 +8427,17 @@ function readOptionalFile(p) {
8356
8427
  return void 0;
8357
8428
  }
8358
8429
  }
8359
- async function publishCmd(root2, rest, opts, global, asJson) {
8430
+ async function publishCmd(root3, rest, opts, global, asJson) {
8360
8431
  if (global.local) throw usageError("vg lib publish uploads to the hosted catalog \u2014 it needs network (remove --local)");
8361
8432
  const name = (rest.join(" ") || opts.name || "").trim();
8362
8433
  if (!name) throw usageError("usage: vg lib publish <name> [--version <v>] [--readme <path>] [--dts <path>] [--language <lang>]");
8363
8434
  const dsn = resolveDsn();
8364
8435
  const parsed = dsn ? parseDsn$1(dsn) : null;
8365
8436
  if (!parsed) throw new CliError("publishing a private library requires a DSN \u2014 run `vibgrate login` or set VIBGRATE_DSN", ExitCode.USAGE_ERROR);
8366
- const version = (opts.version || readPackageVersion(root2) || "").trim();
8437
+ const version = (opts.version || readPackageVersion(root3) || "").trim();
8367
8438
  if (!version) throw usageError("a --version is required (or run from a package whose package.json has a version)");
8368
- const readme = readOptionalFile(opts.readme ? path43__default.resolve(opts.readme) : path43__default.join(root2, "README.md"));
8369
- const dts = opts.dts ? readOptionalFile(path43__default.resolve(opts.dts)) : void 0;
8439
+ const readme = readOptionalFile(opts.readme ? path46__default.resolve(opts.readme) : path46__default.join(root3, "README.md"));
8440
+ const dts = opts.dts ? readOptionalFile(path46__default.resolve(opts.dts)) : void 0;
8370
8441
  if (!readme && !dts) throw usageError("nothing to publish \u2014 no README.md found and no --dts given");
8371
8442
  const result = await publishPrivateLibrary(
8372
8443
  { name, version, readme, dts, language: opts.language },
@@ -8384,20 +8455,20 @@ async function publishCmd(root2, rest, opts, global, asJson) {
8384
8455
  info(`${c.green("\u2714")} published ${c.bold(name)} ${c.dim(`@${version}`)} \xB7 ${result.entities} entities \xB7 ${result.indexedTokens} tokens indexed (MCP-token meter)`);
8385
8456
  info(c.dim(` private to your workspace \xB7 targetId ${result.targetId}`));
8386
8457
  }
8387
- function resolveCmd(root2, rest, asJson) {
8458
+ function resolveCmd(root3, rest, asJson) {
8388
8459
  const name = rest.join(" ");
8389
8460
  if (!name) throw usageError("usage: vg lib resolve <name>");
8390
- const entry = resolveLib(loadCatalog(root2), name);
8461
+ const entry = resolveLib(loadCatalog(root3), name);
8391
8462
  if (!entry) throw new CliError(`no library matches "${name}" (id ${libId(name)})`, ExitCode.NOT_FOUND);
8392
8463
  if (asJson) json({ id: entry.id, name: entry.name, version: entry.version });
8393
8464
  else info(`${entry.id} ${c.dim(`(${entry.name}@${entry.version})`)}`);
8394
8465
  }
8395
- async function refreshCmd(root2, opts, asJson) {
8396
- const catalog = loadCatalog(root2);
8466
+ async function refreshCmd(root3, opts, asJson) {
8467
+ const catalog = loadCatalog(root3);
8397
8468
  const results = [];
8398
8469
  for (const entry of Object.values(catalog.libraries)) {
8399
8470
  try {
8400
- await addLibrary(entry.source.location, { root: root2, name: entry.name, version: entry.version, allowNetwork: opts.online });
8471
+ await addLibrary(entry.source.location, { root: root3, name: entry.name, version: entry.version, allowNetwork: opts.online });
8401
8472
  results.push({ name: entry.name, ok: true });
8402
8473
  } catch {
8403
8474
  results.push({ name: entry.name, ok: false });
@@ -8406,36 +8477,36 @@ async function refreshCmd(root2, opts, asJson) {
8406
8477
  if (asJson) json({ refreshed: results });
8407
8478
  else info(`${c.cyan("vg lib refresh")} \xB7 ${results.filter((r) => r.ok).length}/${results.length} re-ingested`);
8408
8479
  }
8409
- async function showCmd(root2, name, budget, asJson, opts = {}) {
8480
+ async function showCmd(root3, name, budget, asJson, opts = {}) {
8410
8481
  const online = opts.online;
8411
- const catalog = loadCatalog(root2);
8482
+ const catalog = loadCatalog(root3);
8412
8483
  const entry = resolveLib(catalog, name);
8413
- const ver = resolveVersion(root2, name);
8484
+ const ver = resolveVersion(root3, name);
8414
8485
  const displayName = entry?.name ?? name;
8415
8486
  let localDoc;
8416
8487
  let docVersion = ver.served ?? "*";
8417
8488
  let docSource;
8418
8489
  let drift;
8419
8490
  if (entry) {
8420
- localDoc = readDoc(root2, entry);
8491
+ localDoc = readDoc(root3, entry);
8421
8492
  docVersion = entry.version;
8422
- drift = driftFor(root2, entry);
8493
+ drift = driftFor(root3, entry);
8423
8494
  } else {
8424
- const local = localPackageDocs(root2, name);
8495
+ const local = localPackageDocs(root3, name);
8425
8496
  if (local) {
8426
8497
  localDoc = local.docs;
8427
8498
  docVersion = local.version ?? ver.served ?? "*";
8428
8499
  docSource = local.source;
8429
8500
  }
8430
8501
  }
8431
- const apiSurface = localApiSurface(root2, displayName);
8502
+ const apiSurface = localApiSurface(root3, displayName);
8432
8503
  const quality = localDoc !== void 0 ? assessDocQuality([localDoc, apiSurface].filter(Boolean).join("\n\n"), { name: displayName, symbols: symbolsFromApi(apiSurface) }) : { sufficient: false, score: 0, reasons: ["no local docs"] };
8433
8504
  let hosted = null;
8434
8505
  if (online && !quality.sufficient) {
8435
8506
  const dsn = resolveDsn();
8436
8507
  const parsed = dsn ? parseDsn$1(dsn) : null;
8437
8508
  hosted = await fetchHostedDocsCached(
8438
- root2,
8509
+ root3,
8439
8510
  { name: displayName, query: name, maxTokens: budget },
8440
8511
  { region: opts.region, ingest: opts.ingest, auth: parsed ? { keyId: parsed.keyId, secret: parsed.secret } : void 0 }
8441
8512
  );
@@ -8482,13 +8553,13 @@ function registerExport(program) {
8482
8553
  const cmd = program.command("export").description("export the map (format inferred: json|ndjson|graphml|dot|cypher|sql|md|html|cdx.json|spdx.json)").argument("[file]", "target file (or - for stdout)", "map.json").option("--compact", "compact JSON (no pretty-print; auto for large maps)").option("--slim", "drop heavy fields (area member lists, grounding) for smaller exports").action(function(file, opts) {
8483
8554
  const global = readGlobal(this);
8484
8555
  const { graph } = requireGraph(global);
8485
- const root2 = rootOf(global);
8556
+ const root3 = rootOf(global);
8486
8557
  const format = detectFormat(file);
8487
8558
  if (!format) throw usageError(`unknown export format for "${file}"`);
8488
8559
  const needsDeps = format === "cyclonedx" || format === "spdx";
8489
8560
  const content = exportGraph(format, {
8490
8561
  graph,
8491
- deps: needsDeps ? inventory(root2).records : void 0,
8562
+ deps: needsDeps ? inventory(root3).records : void 0,
8492
8563
  models: needsDeps && !global.local ? discoverModels() : void 0,
8493
8564
  generatedAt: graph.generatedAt,
8494
8565
  compact: opts.compact === true ? true : opts.compact === false ? false : void 0,
@@ -8498,7 +8569,7 @@ function registerExport(program) {
8498
8569
  out(content.trimEnd());
8499
8570
  return;
8500
8571
  }
8501
- fs14.mkdirSync(path43.dirname(path43.resolve(file)), { recursive: true });
8572
+ fs14.mkdirSync(path46.dirname(path46.resolve(file)), { recursive: true });
8502
8573
  fs14.writeFileSync(file, content);
8503
8574
  info(`${c.green("\u2714")} wrote ${file} ${c.dim(`(${format})`)}`);
8504
8575
  });
@@ -8509,49 +8580,49 @@ function detectFormat(file) {
8509
8580
  if (lower.endsWith(".cdx.json")) return "cyclonedx";
8510
8581
  if (lower.endsWith(".spdx.json")) return "spdx";
8511
8582
  if (file === "-") return "json";
8512
- return formatForExt(path43.extname(file));
8583
+ return formatForExt(path46.extname(file));
8513
8584
  }
8514
8585
  function registerBundle(program) {
8515
8586
  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) {
8516
8587
  const global = readGlobal(this);
8517
- const root2 = rootOf(global);
8518
- const outDir = path43.resolve(root2, opts.out ?? "vg-bundle");
8588
+ const root3 = rootOf(global);
8589
+ const outDir = path46.resolve(root3, opts.out ?? "vg-bundle");
8519
8590
  let grammarFiles;
8520
8591
  try {
8521
8592
  grammarFiles = resolvedGrammarFiles();
8522
8593
  } catch {
8523
8594
  throw new CliError("no grammars found to bundle (run a build first)", ExitCode.ERROR);
8524
8595
  }
8525
- fs14.mkdirSync(path43.join(outDir, "grammars"), { recursive: true });
8596
+ fs14.mkdirSync(path46.join(outDir, "grammars"), { recursive: true });
8526
8597
  let grammarCount = 0;
8527
8598
  for (const { fileName, absPath } of grammarFiles) {
8528
- fs14.copyFileSync(absPath, path43.join(outDir, "grammars", fileName));
8599
+ fs14.copyFileSync(absPath, path46.join(outDir, "grammars", fileName));
8529
8600
  grammarCount++;
8530
8601
  }
8531
8602
  for (const id of Object.keys(TOOLCHAIN_GRAMMARS).sort()) {
8532
8603
  const absPath = resolveToolchainGrammar(id);
8533
8604
  if (!absPath) continue;
8534
- fs14.copyFileSync(absPath, path43.join(outDir, "grammars", path43.basename(absPath)));
8605
+ fs14.copyFileSync(absPath, path46.join(outDir, "grammars", path46.basename(absPath)));
8535
8606
  grammarCount++;
8536
8607
  }
8537
8608
  const included = [`grammars/ (${grammarCount} .wasm)`];
8538
- const graphSrc = resolveGraphPath(root2, global.graph);
8609
+ const graphSrc = resolveGraphPath(root3, global.graph);
8539
8610
  if (fs14.existsSync(graphSrc)) {
8540
- fs14.copyFileSync(graphSrc, path43.join(outDir, "graph.json"));
8611
+ fs14.copyFileSync(graphSrc, path46.join(outDir, "graph.json"));
8541
8612
  included.push("graph.json");
8542
8613
  } else {
8543
8614
  const graph = loadGraphFileWithSnapshot(graphSrc);
8544
8615
  if (graph) {
8545
- fs14.writeFileSync(path43.join(outDir, "graph.json"), serializeGraph(graph));
8616
+ fs14.writeFileSync(path46.join(outDir, "graph.json"), serializeGraph(graph));
8546
8617
  included.push("graph.json");
8547
8618
  }
8548
8619
  }
8549
- const cat = catalogPath(root2);
8620
+ const cat = catalogPath(root3);
8550
8621
  if (fs14.existsSync(cat)) {
8551
- fs14.copyFileSync(cat, path43.join(outDir, "vibgrate.lib.json"));
8622
+ fs14.copyFileSync(cat, path46.join(outDir, "vibgrate.lib.json"));
8552
8623
  included.push("vibgrate.lib.json");
8553
- if (fs14.existsSync(libDir(root2))) {
8554
- fs14.cpSync(libDir(root2), path43.join(outDir, "lib"), { recursive: true });
8624
+ if (fs14.existsSync(libDir(root3))) {
8625
+ fs14.cpSync(libDir(root3), path46.join(outDir, "lib"), { recursive: true });
8555
8626
  included.push("lib/");
8556
8627
  }
8557
8628
  }
@@ -8563,15 +8634,15 @@ function registerBundle(program) {
8563
8634
  included,
8564
8635
  usage: "Run `vg build --grammars <this dir>/grammars` to build fully offline."
8565
8636
  };
8566
- fs14.writeFileSync(path43.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
8637
+ fs14.writeFileSync(path46.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
8567
8638
  `);
8568
8639
  if (global.json) {
8569
- json({ out: path43.relative(root2, outDir), ...manifest });
8640
+ json({ out: path46.relative(root3, outDir), ...manifest });
8570
8641
  return;
8571
8642
  }
8572
- info(`${c.cyan("vg bundle")} \xB7 wrote ${path43.relative(root2, outDir)}/`);
8643
+ info(`${c.cyan("vg bundle")} \xB7 wrote ${path46.relative(root3, outDir)}/`);
8573
8644
  for (const i of included) info(` ${c.green("\u2714")} ${i}`);
8574
- info(c.dim(` offline: vg build --grammars "${path43.relative(root2, outDir)}/grammars"`));
8645
+ info(c.dim(` offline: vg build --grammars "${path46.relative(root3, outDir)}/grammars"`));
8575
8646
  });
8576
8647
  applyGlobalOptions(cmd);
8577
8648
  }
@@ -8586,19 +8657,19 @@ function registerDoctor(program) {
8586
8657
  var REACH_TIMEOUT_MS = 3e3;
8587
8658
  var CONFIG_BASENAMES = ["vibgrate.config.ts", "vibgrate.config.js", "vibgrate.config.json"];
8588
8659
  async function runDoctor(global) {
8589
- const root2 = rootOf(global);
8660
+ const root3 = rootOf(global);
8590
8661
  const local = global.local === true;
8591
- const graphPath = resolveGraphPath(root2, global.graph);
8592
- const configFile = CONFIG_BASENAMES.find((f) => fs14.existsSync(path43.join(root2, f))) ?? null;
8593
- const graph = loadGraph(root2, graphPath);
8662
+ const graphPath = resolveGraphPath(root3, global.graph);
8663
+ const configFile = CONFIG_BASENAMES.find((f) => fs14.existsSync(path46.join(root3, f))) ?? null;
8664
+ const graph = loadGraph(root3, graphPath);
8594
8665
  let staleFiles = null;
8595
8666
  if (graph) {
8596
- const probe = probeFreshness(root2);
8667
+ const probe = probeFreshness(root3);
8597
8668
  if (probe) staleFiles = driftCount(probe.drift);
8598
8669
  }
8599
- const catalog = loadCatalog(root2);
8670
+ const catalog = loadCatalog(root3);
8600
8671
  const libCount = Object.keys(catalog.libraries).length;
8601
- const creds = diagnoseCredentials(root2);
8672
+ const creds = diagnoseCredentials(root3);
8602
8673
  const base = hostedBase();
8603
8674
  const reachable = local ? null : await probeReachable(base);
8604
8675
  const launch = detectServeLaunch();
@@ -8612,17 +8683,17 @@ async function runDoctor(global) {
8612
8683
  version: VERSION,
8613
8684
  node: process.version,
8614
8685
  platform: `${process.platform}/${process.arch}`,
8615
- root: root2,
8686
+ root: root3,
8616
8687
  configFile,
8617
8688
  map: {
8618
- path: path43.relative(root2, graphPath),
8689
+ path: path46.relative(root3, graphPath),
8619
8690
  built: graph !== null,
8620
8691
  generatedAt: graph?.generatedAt ?? null,
8621
8692
  staleFiles
8622
8693
  },
8623
8694
  libCatalog: {
8624
- path: path43.relative(root2, catalogPath(root2)),
8625
- present: fs14.existsSync(catalogPath(root2)),
8695
+ path: path46.relative(root3, catalogPath(root3)),
8696
+ present: fs14.existsSync(catalogPath(root3)),
8626
8697
  libraries: libCount
8627
8698
  },
8628
8699
  credentials: creds,
@@ -8679,7 +8750,7 @@ async function runDoctor(global) {
8679
8750
  );
8680
8751
  info(
8681
8752
  ` host pool ${li.hostPool.size} warm session(s)` + (li.hostPool.sessions[0] ? c.dim(
8682
- ` \xB7 last ${path43.basename(li.hostPool.sessions[0].modelPath)} kv=${li.hostPool.sessions[0].kvBlocks}`
8753
+ ` \xB7 last ${path46.basename(li.hostPool.sessions[0].modelPath)} kv=${li.hostPool.sessions[0].kvBlocks}`
8683
8754
  ) : c.dim(" \xB7 none (load via vg code / vgd host-load)"))
8684
8755
  );
8685
8756
  if (li.dynamicOnToken || li.dynamicCustomSampler) {
@@ -8692,16 +8763,16 @@ async function runDoctor(global) {
8692
8763
  }
8693
8764
  info(c.dim(" tip `vg models mode --apply-recommend` pins the recommended Code Mode"));
8694
8765
  }
8695
- function diagnoseCredentials(root2) {
8766
+ function diagnoseCredentials(root3) {
8696
8767
  const none = { source: "none", path: null, host: null, workspaceId: null };
8697
8768
  if (process.env.VIBGRATE_DSN) {
8698
8769
  const parsed2 = parseDsn$1(process.env.VIBGRATE_DSN);
8699
8770
  return { source: "env", path: null, host: parsed2?.host ?? null, workspaceId: parsed2?.workspaceId ?? null };
8700
8771
  }
8701
- const stored = readStoredCredentials({ cwd: root2 });
8772
+ const stored = readStoredCredentials({ cwd: root3 });
8702
8773
  if (!stored) return none;
8703
- const file = credentialsPath({ cwd: root2 });
8704
- const source = process.env.VIBGRATE_CREDENTIALS ? "env" : file === projectCredentialsPath(root2) ? "project" : "home";
8774
+ const file = credentialsPath({ cwd: root3 });
8775
+ const source = process.env.VIBGRATE_CREDENTIALS ? "env" : file === projectCredentialsPath(root3) ? "project" : "home";
8705
8776
  const parsed = parseDsn$1(stored.dsn);
8706
8777
  return {
8707
8778
  source,
@@ -8986,7 +9057,7 @@ async function readStdin() {
8986
9057
  return Buffer.concat(chunks).toString("utf-8");
8987
9058
  }
8988
9059
  function readFileOrThrow(file, label) {
8989
- const p = path43.resolve(file);
9060
+ const p = path46.resolve(file);
8990
9061
  if (!fs14.existsSync(p)) throw usageError(`${label} file not found: ${p}`);
8991
9062
  return fs14.readFileSync(p, "utf-8");
8992
9063
  }
@@ -8998,8 +9069,8 @@ function writeOutput(outFile, content) {
8998
9069
  process.stdout.write(content.endsWith("\n") ? content : content + "\n");
8999
9070
  return;
9000
9071
  }
9001
- const outPath = path43.resolve(outFile);
9002
- const dir = path43.dirname(outPath);
9072
+ const outPath = path46.resolve(outFile);
9073
+ const dir = path46.dirname(outPath);
9003
9074
  if (!fs14.existsSync(dir)) throw usageError(`output directory does not exist: ${dir}`);
9004
9075
  fs14.writeFileSync(outPath, content.endsWith("\n") ? content : content + "\n", "utf-8");
9005
9076
  info(c.dim(`wrote ${outPath}`));
@@ -9034,9 +9105,9 @@ function registerHcs(program) {
9034
9105
  }
9035
9106
  const statePath = opts.previous ?? opts.out;
9036
9107
  const useIncremental = canIncremental && statePath !== void 0;
9037
- const root2 = path43.resolve(dir);
9038
- if (!fs14.existsSync(root2) || !fs14.statSync(root2).isDirectory()) {
9039
- throw usageError(`not a directory: ${root2}`);
9108
+ const root3 = path46.resolve(dir);
9109
+ if (!fs14.existsSync(root3) || !fs14.statSync(root3).isDirectory()) {
9110
+ throw usageError(`not a directory: ${root3}`);
9040
9111
  }
9041
9112
  const supported = new Set(
9042
9113
  engine.supportedLanguages().split(",").map((l) => l.trim()).filter(Boolean)
@@ -9059,7 +9130,7 @@ function registerHcs(program) {
9059
9130
  }
9060
9131
  for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : 1)) {
9061
9132
  if (entry.name.startsWith(".")) continue;
9062
- const absChild = path43.join(abs, entry.name);
9133
+ const absChild = path46.join(abs, entry.name);
9063
9134
  const relChild = rel ? `${rel}/${entry.name}` : entry.name;
9064
9135
  if (entry.isDirectory()) {
9065
9136
  if (!SKIP_DIRS2.has(entry.name)) walk2(absChild, relChild);
@@ -9067,7 +9138,7 @@ function registerHcs(program) {
9067
9138
  }
9068
9139
  if (!entry.isFile()) continue;
9069
9140
  if (!opts.includeTests && TEST_PATTERNS.some((p) => p.test(relChild))) continue;
9070
- const lang = extToLang.get(path43.extname(entry.name).toLowerCase());
9141
+ const lang = extToLang.get(path46.extname(entry.name).toLowerCase());
9071
9142
  if (!lang) continue;
9072
9143
  try {
9073
9144
  if (fs14.statSync(absChild).size > MAX_EXTRACT_FILE_BYTES) continue;
@@ -9079,9 +9150,9 @@ function registerHcs(program) {
9079
9150
  }
9080
9151
  }
9081
9152
  };
9082
- walk2(root2, "");
9153
+ walk2(root3, "");
9083
9154
  if (byLanguage.size === 0 && !useIncremental) {
9084
- info(`no extractable sources found under ${root2} (languages: ${[...wanted].sort().join(", ")})`);
9155
+ info(`no extractable sources found under ${root3} (languages: ${[...wanted].sort().join(", ")})`);
9085
9156
  writeOutput(opts.out, "");
9086
9157
  return;
9087
9158
  }
@@ -9101,7 +9172,7 @@ function registerHcs(program) {
9101
9172
  writeOutput(opts.out, parts2.join("\n"));
9102
9173
  return;
9103
9174
  }
9104
- const previousPath = path43.resolve(statePath);
9175
+ const previousPath = path46.resolve(statePath);
9105
9176
  const previousNdjson = !opts.full && fs14.existsSync(previousPath) ? fs14.readFileSync(previousPath, "utf-8") : "";
9106
9177
  if (opts.full) info(c.dim("running a full re-extraction (--full)"));
9107
9178
  else if (!previousNdjson) info(c.dim(`previous stream not found at ${previousPath} \u2014 running a full extraction`));
@@ -9206,11 +9277,11 @@ function registerHcs(program) {
9206
9277
  applyGlobalOptions(cmd);
9207
9278
  }
9208
9279
  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) => {
9209
- const rootDir = path43.resolve(targetPath);
9210
- const vibgrateDir2 = path43.join(rootDir, ".vibgrate");
9280
+ const rootDir = path46.resolve(targetPath);
9281
+ const vibgrateDir2 = path46.join(rootDir, ".vibgrate");
9211
9282
  await ensureDir(vibgrateDir2);
9212
9283
  console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold(".vibgrate/")} directory`);
9213
- const configPath = path43.join(rootDir, "vibgrate.config.ts");
9284
+ const configPath = path46.join(rootDir, "vibgrate.config.ts");
9214
9285
  if (await pathExists(configPath)) {
9215
9286
  console.log(chalk7.dim(" vibgrate.config.ts already exists, skipping"));
9216
9287
  } else {
@@ -9218,7 +9289,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
9218
9289
  console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold("vibgrate.config.ts")}`);
9219
9290
  }
9220
9291
  if (opts.baseline) {
9221
- const { runBaseline } = await import('./baseline-NZFGLMER.js');
9292
+ const { runBaseline } = await import('./baseline-FIV6PR2M.js');
9222
9293
  await runBaseline(rootDir);
9223
9294
  }
9224
9295
  console.log("");
@@ -9321,7 +9392,7 @@ function collectPackageImports(graph, ecosystem, pkg) {
9321
9392
  };
9322
9393
  }
9323
9394
  async function firstReferenceLine(rootDir, relFile, identifier, readFile3) {
9324
- const content = await readFile3(path43.join(rootDir, relFile));
9395
+ const content = await readFile3(path46.join(rootDir, relFile));
9325
9396
  if (!content) return null;
9326
9397
  const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9327
9398
  const re = new RegExp(`\\b${escaped}\\b`);
@@ -9557,7 +9628,7 @@ var AI_ASSISTANT_DETECTORS = [
9557
9628
  async function detectAiAssistant(rootDir) {
9558
9629
  for (const detector of AI_ASSISTANT_DETECTORS) {
9559
9630
  for (const file of detector.files) {
9560
- if (await pathExists$1(path43.join(rootDir, file))) {
9631
+ if (await pathExists$1(path46.join(rootDir, file))) {
9561
9632
  return detector.name;
9562
9633
  }
9563
9634
  }
@@ -9589,6 +9660,18 @@ init_cli_invocation();
9589
9660
  function shouldBuildCodeMap(opts) {
9590
9661
  return opts.graph !== false && !opts.maxPrivacy && !opts.noLocalArtifacts;
9591
9662
  }
9663
+ function architectureGraphView2(graph) {
9664
+ return {
9665
+ nodes: graph.nodes.map((n) => ({ id: n.id, file: n.file, kind: n.kind })),
9666
+ edges: graph.edges.map((e) => ({
9667
+ src: e.src,
9668
+ dst: e.dst,
9669
+ kind: e.kind,
9670
+ confidence: e.confidence,
9671
+ resolution: e.resolution
9672
+ }))
9673
+ };
9674
+ }
9592
9675
  async function attachReachability(artifact, rootDir, opts, graph) {
9593
9676
  try {
9594
9677
  const dsn = resolveDsn(opts.dsn);
@@ -9750,8 +9833,8 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
9750
9833
  'Exclude paths matching a glob pattern. Repeatable, and a single value may list several patterns separated by commas or semicolons (e.g. --exclude "legacy/**,vendor/**"). Merged with excludes from the config file.',
9751
9834
  collectExcludes,
9752
9835
  []
9753
- ).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) => {
9754
- const rootDir = path43.resolve(targetPath);
9836
+ ).option("--concurrency <n>", "Max concurrent registry lookups", "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) => {
9837
+ const rootDir = path46.resolve(targetPath);
9755
9838
  if (!await pathExists$1(rootDir)) {
9756
9839
  console.error(chalk7.red(`Path does not exist: ${rootDir}`));
9757
9840
  process.exit(1);
@@ -9759,7 +9842,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
9759
9842
  const hasDsn = !!resolveDsn(opts.dsn);
9760
9843
  let willPush = !opts.offline && (opts.push || hasDsn);
9761
9844
  const noLocalArtifacts = opts.localArtifacts === false;
9762
- const isFirstRun = !await pathExists$1(path43.join(rootDir, ".vibgrate", "scan_result.json"));
9845
+ const isFirstRun = !await pathExists$1(path46.join(rootDir, ".vibgrate", "scan_result.json"));
9763
9846
  let pinnedRegion;
9764
9847
  let planTier;
9765
9848
  let upgradeUrl;
@@ -9891,7 +9974,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
9891
9974
  const wantGraph = shouldBuildCodeMap({ graph: opts.graph, maxPrivacy: opts.maxPrivacy, noLocalArtifacts });
9892
9975
  let builtGraph = null;
9893
9976
  if (wantGraph) {
9894
- scanOpts.postScan = async (report) => {
9977
+ scanOpts.postScan = async (report, ctx) => {
9895
9978
  const result = await buildGraph({
9896
9979
  root: rootDir,
9897
9980
  exclude: opts.exclude,
@@ -9902,6 +9985,14 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
9902
9985
  writeSnapshot(rootDir, result.graph.provenance.corpusHash, result.fileStats, {
9903
9986
  exclude: opts.exclude
9904
9987
  });
9988
+ refineArchitectureWithGraph(
9989
+ { projects: ctx.projects, solutions: ctx.solutions, extended: ctx.extended },
9990
+ architectureGraphView2(result.graph)
9991
+ );
9992
+ refineArchitectureWithAstRoles(
9993
+ { projects: ctx.projects, solutions: ctx.solutions, extended: ctx.extended },
9994
+ result.fileRoles
9995
+ );
9905
9996
  const { counts } = result.graph.meta;
9906
9997
  return `${counts.nodes.toLocaleString()} nodes \xB7 ${counts.edges.toLocaleString()} edges`;
9907
9998
  };
@@ -10048,11 +10139,11 @@ function scanStaleness(rootDir, artifactPath) {
10048
10139
  scanned++;
10049
10140
  if (scanned > MAX_ENTRIES2) break;
10050
10141
  if (e.isDirectory()) {
10051
- if (!SKIP_DIRS3.has(e.name) && !e.name.startsWith(".")) walk2(path43.join(dir, e.name), depth + 1);
10142
+ if (!SKIP_DIRS3.has(e.name) && !e.name.startsWith(".")) walk2(path46.join(dir, e.name), depth + 1);
10052
10143
  continue;
10053
10144
  }
10054
10145
  if (!WATCHED_FILES.has(e.name) && !WATCHED_EXT.test(e.name)) continue;
10055
- const abs = path43.join(dir, e.name);
10146
+ const abs = path46.join(dir, e.name);
10056
10147
  let mtimeMs;
10057
10148
  try {
10058
10149
  mtimeMs = fs14.statSync(abs).mtimeMs;
@@ -10061,7 +10152,7 @@ function scanStaleness(rootDir, artifactPath) {
10061
10152
  }
10062
10153
  if (mtimeMs > newest) {
10063
10154
  newest = mtimeMs;
10064
- newestChanged = path43.relative(rootDir, abs) || e.name;
10155
+ newestChanged = path46.relative(rootDir, abs) || e.name;
10065
10156
  }
10066
10157
  }
10067
10158
  };
@@ -10169,7 +10260,7 @@ function importPattern(ecosystem, pkg) {
10169
10260
  }
10170
10261
  return new RegExp(`^\\s*(?:import\\s+${e}(?:[.\\s]|$)|from\\s+${e}(?:[.\\s]))`, "gm");
10171
10262
  }
10172
- function analyzeTree(root2, packages) {
10263
+ function analyzeTree(root3, packages) {
10173
10264
  const result = /* @__PURE__ */ new Map();
10174
10265
  const tracked = [];
10175
10266
  for (const p of packages) {
@@ -10201,13 +10292,13 @@ function analyzeTree(root2, packages) {
10201
10292
  if (filesSeen >= MAX_FILES) return;
10202
10293
  if (entry.isDirectory()) {
10203
10294
  if (SKIP_DIRS4.has(entry.name) || entry.name.startsWith(".")) continue;
10204
- walk2(path43.join(dir, entry.name), depth + 1);
10295
+ walk2(path46.join(dir, entry.name), depth + 1);
10205
10296
  continue;
10206
10297
  }
10207
- const fileEco = EXT_ECOSYSTEM[path43.extname(entry.name)];
10298
+ const fileEco = EXT_ECOSYSTEM[path46.extname(entry.name)];
10208
10299
  if (!fileEco || fileEco === "unknown") continue;
10209
10300
  filesSeen++;
10210
- const full = path43.join(dir, entry.name);
10301
+ const full = path46.join(dir, entry.name);
10211
10302
  let content;
10212
10303
  try {
10213
10304
  if (fs14.statSync(full).size > MAX_FILE_BYTES2) continue;
@@ -10229,7 +10320,7 @@ function analyzeTree(root2, packages) {
10229
10320
  }
10230
10321
  }
10231
10322
  };
10232
- walk2(root2, 0);
10323
+ walk2(root3, 0);
10233
10324
  for (const t of tracked) {
10234
10325
  result.set(t.name, {
10235
10326
  importSites: t.importSites,
@@ -10522,7 +10613,7 @@ function applyPlan(rootDir, upgrades, opts = {}) {
10522
10613
  }
10523
10614
  const outcomes = effective.map((t) => {
10524
10615
  const c2 = pmCommandFor(u.ecosystem, u.package, u.to, opts.packageManager, { workspaceRoot: t.isWorkspaceRoot });
10525
- return { t, res: run(c2, path43.join(rootDir, t.dir)) };
10616
+ return { t, res: run(c2, path46.join(rootDir, t.dir)) };
10526
10617
  });
10527
10618
  const failures = outcomes.filter((o) => !o.res.ok);
10528
10619
  if (failures.length === 0) {
@@ -10551,8 +10642,8 @@ function applyPlan(rootDir, upgrades, opts = {}) {
10551
10642
  return results;
10552
10643
  }
10553
10644
  var REGISTRY_URL = "https://registry.npmjs.org/@vibgrate%2fcli/latest";
10554
- var CACHE_DIR = path43.join(os.homedir(), ".vibgrate");
10555
- var CACHE_FILE = path43.join(CACHE_DIR, "update-check.json");
10645
+ var CACHE_DIR = path46.join(os.homedir(), ".vibgrate");
10646
+ var CACHE_FILE = path46.join(CACHE_DIR, "update-check.json");
10556
10647
  async function fetchLatestVersion() {
10557
10648
  try {
10558
10649
  const controller = new AbortController();
@@ -10615,8 +10706,8 @@ function buildDeferredUpdateScript(opts) {
10615
10706
  }
10616
10707
  function scheduleDeferredUpdate(command, pid = process.pid, dir = os.tmpdir()) {
10617
10708
  const stamp = `${pid}-${process.hrtime.bigint().toString(36)}`;
10618
- const scriptPath = path43.join(dir, `vg-update-${stamp}.cmd`);
10619
- const logPath = path43.join(dir, `vg-update-${stamp}.log`);
10709
+ const scriptPath = path46.join(dir, `vg-update-${stamp}.cmd`);
10710
+ const logPath = path46.join(dir, `vg-update-${stamp}.log`);
10620
10711
  try {
10621
10712
  fs14.writeFileSync(scriptPath, buildDeferredUpdateScript({ pid, command, logPath }), "utf8");
10622
10713
  const child = spawn(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", scriptPath], {
@@ -10672,14 +10763,14 @@ async function findGlobalInstall(pkg = PACKAGE_NAME, run = defaultRunner) {
10672
10763
  pm: "yarn",
10673
10764
  root: () => {
10674
10765
  const dir = tryRun("yarn global dir");
10675
- return dir ? path43.join(dir, "node_modules") : null;
10766
+ return dir ? path46.join(dir, "node_modules") : null;
10676
10767
  }
10677
10768
  },
10678
- { pm: "bun", root: () => path43.join(os.homedir(), ".bun", "install", "global", "node_modules") }
10769
+ { pm: "bun", root: () => path46.join(os.homedir(), ".bun", "install", "global", "node_modules") }
10679
10770
  ];
10680
10771
  for (const probe of probes) {
10681
- const root2 = probe.root();
10682
- if (root2 && await pathExists(path43.join(root2, pkg))) return probe.pm;
10772
+ const root3 = probe.root();
10773
+ if (root3 && await pathExists(path46.join(root3, pkg))) return probe.pm;
10683
10774
  }
10684
10775
  return null;
10685
10776
  }
@@ -10698,15 +10789,15 @@ function getGlobalUpdateCommand(pm, pkg, version) {
10698
10789
  }
10699
10790
  }
10700
10791
  async function detectPackageManager(cwd) {
10701
- if (await pathExists(path43.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10702
- if (await pathExists(path43.join(cwd, "bun.lockb"))) return "bun";
10703
- if (await pathExists(path43.join(cwd, "yarn.lock"))) return "yarn";
10792
+ if (await pathExists(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10793
+ if (await pathExists(path46.join(cwd, "bun.lockb"))) return "bun";
10794
+ if (await pathExists(path46.join(cwd, "yarn.lock"))) return "yarn";
10704
10795
  return "npm";
10705
10796
  }
10706
10797
  async function detectWorkspaceRoot(cwd) {
10707
- if (await pathExists(path43.join(cwd, "pnpm-workspace.yaml"))) return true;
10798
+ if (await pathExists(path46.join(cwd, "pnpm-workspace.yaml"))) return true;
10708
10799
  try {
10709
- const pkgPath = path43.join(cwd, "package.json");
10800
+ const pkgPath = path46.join(cwd, "package.json");
10710
10801
  const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
10711
10802
  const pkg = JSON.parse(raw);
10712
10803
  return pkg.workspaces != null;
@@ -10733,7 +10824,7 @@ function getInstallCommand(pm, pkg, version, isDev, opts = {}) {
10733
10824
  }
10734
10825
  async function getLocalDependencyState(cwd, pkg = PACKAGE_NAME) {
10735
10826
  try {
10736
- const pkgPath = path43.join(cwd, "package.json");
10827
+ const pkgPath = path46.join(cwd, "package.json");
10737
10828
  const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
10738
10829
  const parsed = JSON.parse(raw);
10739
10830
  if (parsed.devDependencies?.[pkg]) return "dev";
@@ -10748,18 +10839,18 @@ async function confirmWorkspaceRoot(pm) {
10748
10839
  async function confirm3(question) {
10749
10840
  if (!(process.stdin.isTTY && process.stdout.isTTY)) return false;
10750
10841
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
10751
- return new Promise((resolve21) => {
10842
+ return new Promise((resolve23) => {
10752
10843
  rl.question(chalk7.yellow(question), (ans) => {
10753
10844
  rl.close();
10754
10845
  const t = ans.trim().toLowerCase();
10755
- resolve21(t === "y" || t === "yes");
10846
+ resolve23(t === "y" || t === "yes");
10756
10847
  });
10757
10848
  });
10758
10849
  }
10759
10850
  var CAPTURED_OUTPUT_LIMIT = 64e3;
10760
10851
  async function runInstall(cmd, cwd) {
10761
10852
  const { spawn: spawn4 } = await import('child_process');
10762
- return new Promise((resolve21) => {
10853
+ return new Promise((resolve23) => {
10763
10854
  const child = spawn4(cmd, {
10764
10855
  cwd,
10765
10856
  shell: true,
@@ -10778,15 +10869,15 @@ async function runInstall(cmd, cwd) {
10778
10869
  };
10779
10870
  tee(child.stdout, process.stdout);
10780
10871
  tee(child.stderr, process.stderr);
10781
- child.on("error", (err) => resolve21({ ok: false, output: `${output}
10872
+ child.on("error", (err) => resolve23({ ok: false, output: `${output}
10782
10873
  ${String(err)}` }));
10783
- child.on("close", (code) => resolve21({ ok: code === 0, output }));
10874
+ child.on("close", (code) => resolve23({ ok: code === 0, output }));
10784
10875
  });
10785
10876
  }
10786
10877
  async function releaseWindowsFileLocks(cwd, keepVersion) {
10787
10878
  reapAndReport(cwd, keepVersion);
10788
10879
  try {
10789
- const { vgdSocketPath } = await import('./vgd-QR26FAZD.js');
10880
+ const { vgdSocketPath } = await import('./vgd-URUZQORN.js');
10790
10881
  const result = await stopVgd(vgdSocketPath());
10791
10882
  if (result === "stopped") {
10792
10883
  console.log(chalk7.dim("Stopped the vgd daemon so its files can be replaced \u2014 it restarts on the next command."));
@@ -10911,7 +11002,7 @@ var updateCommand = new Command("update").description("Update vibgrate to the la
10911
11002
  );
10912
11003
  async function rewarmVgdAndReport(cwd) {
10913
11004
  try {
10914
- const { vgdIsRunning, vgdRequest } = await import('./vgd-QR26FAZD.js');
11005
+ const { vgdIsRunning, vgdRequest } = await import('./vgd-URUZQORN.js');
10915
11006
  if (!await vgdIsRunning()) return;
10916
11007
  const loaded = await vgdRequest({ op: "load-graph", root: cwd });
10917
11008
  if (!loaded.ok || !("repositoryId" in loaded)) return;
@@ -11000,13 +11091,13 @@ function collectCandidates(artifact) {
11000
11091
  return out2;
11001
11092
  }
11002
11093
  var defaultFixScanner = async (rootDir) => {
11003
- const scratch = path43.join(os.tmpdir(), `vg-fix-scan-${process.pid}.json`);
11094
+ const scratch = path46.join(os.tmpdir(), `vg-fix-scan-${process.pid}.json`);
11004
11095
  const scanOpts = { vibgrateVersion: VERSION, format: "json", out: scratch, quiet: true, concurrency: 8 };
11005
11096
  const advanced = await loadAdvancedScanHook();
11006
11097
  return runCoreScan(rootDir, scanOpts, advanced);
11007
11098
  };
11008
11099
  async function loadArtifact(rootDir, inFile, scan = defaultFixScanner) {
11009
- const artifactPath = path43.isAbsolute(inFile) ? inFile : path43.join(rootDir, inFile);
11100
+ const artifactPath = path46.isAbsolute(inFile) ? inFile : path46.join(rootDir, inFile);
11010
11101
  if (await pathExists(artifactPath)) {
11011
11102
  const staleness = scanStaleness(rootDir, artifactPath);
11012
11103
  if (!staleness.stale) {
@@ -11033,7 +11124,7 @@ async function repositoryIdentity(rootDir, override) {
11033
11124
  }
11034
11125
  }
11035
11126
  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", "Preview the recommended plan (or --plan <tier>) without applying; never prompts").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) => {
11036
- const rootDir = path43.resolve(targetPath);
11127
+ const rootDir = path46.resolve(targetPath);
11037
11128
  if (!await pathExists(rootDir)) {
11038
11129
  console.error(chalk7.red(`Path does not exist: ${rootDir}`));
11039
11130
  process.exit(1);
@@ -11194,7 +11285,7 @@ function emit(report, format) {
11194
11285
  }
11195
11286
  }
11196
11287
  function relProjectDir(rootDir, projectPath) {
11197
- const rel = path43.isAbsolute(projectPath) ? path43.relative(rootDir, projectPath) : projectPath;
11288
+ const rel = path46.isAbsolute(projectPath) ? path46.relative(rootDir, projectPath) : projectPath;
11198
11289
  return rel === "" ? "." : rel;
11199
11290
  }
11200
11291
  async function buildTargetResolver(rootDir, artifact) {
@@ -11235,14 +11326,14 @@ function promptPlanSelection(response) {
11235
11326
  const defaultIdx = plans.findIndex((p) => p.tier === response.recommended);
11236
11327
  const def = defaultIdx >= 0 ? defaultIdx + 1 : 1;
11237
11328
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11238
- return new Promise((resolve21) => {
11329
+ return new Promise((resolve23) => {
11239
11330
  rl.question(`Plan [1-${plans.length}, default ${def}, q to cancel]: `, (ans) => {
11240
11331
  rl.close();
11241
11332
  const t = ans.trim().toLowerCase();
11242
- if (t === "q" || t === "quit") return resolve21(null);
11333
+ if (t === "q" || t === "quit") return resolve23(null);
11243
11334
  const n = t === "" ? def : Number.parseInt(t, 10);
11244
- if (!Number.isInteger(n) || n < 1 || n > plans.length) return resolve21(null);
11245
- resolve21(plans[n - 1].tier);
11335
+ if (!Number.isInteger(n) || n < 1 || n > plans.length) return resolve23(null);
11336
+ resolve23(plans[n - 1].tier);
11246
11337
  });
11247
11338
  });
11248
11339
  }
@@ -11664,6 +11755,19 @@ function formatArchitectureDiagram(arch) {
11664
11755
  lines.push("");
11665
11756
  lines.push(chalk7.bold(" Archetype: ") + `${arch.archetype}` + chalk7.dim(` (${Math.round(arch.archetypeConfidence * 100)}% confidence)`));
11666
11757
  lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ? chalk7.dim(` (${arch.unclassified} unclassified)`) : ""));
11758
+ if (arch.folders && arch.folders.length > 0) {
11759
+ lines.push(` Folders classified: ${arch.folders.length}`);
11760
+ for (const folder of arch.folders.slice(0, 12)) {
11761
+ lines.push(` ${folder.path} ${folder.layer} ${Math.round(folder.confidence * 100)}% ${folder.fileCount} file${folder.fileCount !== 1 ? "s" : ""}`);
11762
+ }
11763
+ if (arch.folders.length > 12) {
11764
+ lines.push(chalk7.dim(` + ${arch.folders.length - 12} more`));
11765
+ }
11766
+ }
11767
+ if (arch.unclassifiedFiles && arch.unclassifiedFiles.length > 0) {
11768
+ const extra = arch.unclassified > arch.unclassifiedFiles.length ? ` of ${arch.unclassified}` : "";
11769
+ lines.push(` Unclassified source (sample): ${arch.unclassifiedFiles.length}${extra}`);
11770
+ }
11667
11771
  lines.push("");
11668
11772
  if (arch.layers.length > 0) {
11669
11773
  for (const layer of arch.layers) {
@@ -11672,6 +11776,17 @@ function formatArchitectureDiagram(arch) {
11672
11776
  }
11673
11777
  lines.push("");
11674
11778
  }
11779
+ if (arch.violations && arch.violations.length > 0) {
11780
+ const extra = arch.violationsCapped ? "+" : "";
11781
+ lines.push(` Boundary violations (${arch.violations.length}${extra})`);
11782
+ for (const v of arch.violations.slice(0, 8)) {
11783
+ lines.push(` ${v.fromFile} \u2192 ${v.toFile} ${v.rule}`);
11784
+ }
11785
+ if (arch.violations.length > 8) {
11786
+ lines.push(chalk7.dim(` + ${arch.violations.length - 8} more`));
11787
+ }
11788
+ lines.push("");
11789
+ }
11675
11790
  return lines;
11676
11791
  }
11677
11792
  function generatePriorityActions(artifact) {
@@ -12037,7 +12152,7 @@ function formatMarkdown(artifact) {
12037
12152
 
12038
12153
  // src/reporting/commands/report.ts
12039
12154
  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) => {
12040
- const artifactPath = path43.resolve(opts.in);
12155
+ const artifactPath = path46.resolve(opts.in);
12041
12156
  if (!await pathExists(artifactPath)) {
12042
12157
  console.error(chalk7.red(`Artifact not found: ${artifactPath}`));
12043
12158
  console.error(chalk7.dim('Run "vibgrate scan" first to generate a scan artifact.'));
@@ -12084,10 +12199,10 @@ function openUrl(url) {
12084
12199
  }
12085
12200
 
12086
12201
  // src/reporting/commands/login.ts
12087
- var delay = (ms) => new Promise((resolve21) => setTimeout(resolve21, ms));
12202
+ var delay = (ms) => new Promise((resolve23) => setTimeout(resolve23, ms));
12088
12203
  function isInside(dir, file) {
12089
- const rel = path43.relative(dir, file);
12090
- return !!rel && !rel.startsWith("..") && !path43.isAbsolute(rel);
12204
+ const rel = path46.relative(dir, file);
12205
+ return !!rel && !rel.startsWith("..") && !path46.isAbsolute(rel);
12091
12206
  }
12092
12207
  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(
12093
12208
  "--local",
@@ -12191,9 +12306,9 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
12191
12306
  }
12192
12307
  console.log(chalk7.dim(` Credentials saved to ${credsFile}`));
12193
12308
  try {
12194
- const root2 = findGitRoot();
12195
- if (root2 && isInside(root2, credsFile)) {
12196
- const res = ensureGitignored(gitignoreEntryForCredentials(root2, credsFile), root2);
12309
+ const root3 = findGitRoot();
12310
+ if (root3 && isInside(root3, credsFile)) {
12311
+ const res = ensureGitignored(gitignoreEntryForCredentials(root3, credsFile), root3);
12197
12312
  if (res.status === "created") {
12198
12313
  console.log(chalk7.dim(` Created .gitignore and ignored ${res.entry}`));
12199
12314
  } else if (res.status === "added") {
@@ -12253,6 +12368,789 @@ var logoutCommand = new Command("logout").description("Clear stored Vibgrate log
12253
12368
  );
12254
12369
  }
12255
12370
  });
12371
+
12372
+ // src/reporting/commands/locale/client.ts
12373
+ function buildUrl(scheme, host, path48, query) {
12374
+ const url = new URL(`${scheme}://${host}/v1/locale${path48}`);
12375
+ for (const [key2, value] of Object.entries(query ?? {})) {
12376
+ if (value !== void 0 && value !== "") url.searchParams.set(key2, String(value));
12377
+ }
12378
+ return url.toString();
12379
+ }
12380
+ async function toEnvelope(response, url) {
12381
+ let envelope = null;
12382
+ const text = await response.text();
12383
+ try {
12384
+ envelope = text ? JSON.parse(text) : null;
12385
+ } catch {
12386
+ envelope = null;
12387
+ }
12388
+ if (response.ok && envelope) return envelope;
12389
+ const serverMessage = envelope?.errors?.map((e) => e.message).join(" ") ?? "";
12390
+ const requestId = envelope?.meta?.requestId;
12391
+ const suffix = requestId ? ` (request ${requestId})` : "";
12392
+ if (response.status === 401) {
12393
+ throw new CliError(
12394
+ `${serverMessage || "Authentication failed."}${suffix}
12395
+ Run "vg login", set VIBGRATE_DSN, or pass --dsn.`,
12396
+ ExitCode.ERROR
12397
+ );
12398
+ }
12399
+ if (response.status === 402) {
12400
+ throw new CliError(`${serverMessage || "This feature requires a paid plan."}${suffix}`, ExitCode.ERROR);
12401
+ }
12402
+ if (response.status === 403) {
12403
+ throw new CliError(`${serverMessage || "Not permitted."}${suffix}`, ExitCode.ERROR);
12404
+ }
12405
+ if (response.status === 404) {
12406
+ throw new CliError(`${serverMessage || "Not found."}${suffix}`, ExitCode.ERROR);
12407
+ }
12408
+ throw new CliError(
12409
+ `Vibgrate Cloud returned ${response.status} for ${new URL(url).pathname}.${suffix}` + (serverMessage ? `
12410
+ ${serverMessage}` : ""),
12411
+ ExitCode.ERROR
12412
+ );
12413
+ }
12414
+ function createLocaleClient(dsn) {
12415
+ const parsed = parseDsn$1(dsn);
12416
+ if (!parsed) {
12417
+ throw new CliError(
12418
+ "Invalid DSN format.\nExpected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>",
12419
+ ExitCode.ERROR
12420
+ );
12421
+ }
12422
+ const request = async (method, path48, opts = {}) => {
12423
+ const url = buildUrl(parsed.scheme, parsed.host, path48, opts.query);
12424
+ const body = opts.body === void 0 ? void 0 : JSON.stringify(opts.body);
12425
+ const response = await fetch(url, {
12426
+ method,
12427
+ headers: {
12428
+ // Same presentation as the ingest path — see the module header.
12429
+ Authorization: `VibgrateDSN ${parsed.keyId}:${parsed.secret}`,
12430
+ "X-Vibgrate-Timestamp": String(Date.now()),
12431
+ ...body ? { "Content-Type": "application/json" } : {}
12432
+ },
12433
+ body
12434
+ });
12435
+ return toEnvelope(response, url);
12436
+ };
12437
+ return {
12438
+ workspaceId: parsed.workspaceId,
12439
+ get: (path48, query) => request("GET", path48, { query }),
12440
+ post: (path48, body) => request("POST", path48, { body }),
12441
+ patch: (path48, body) => request("PATCH", path48, { body }),
12442
+ del: (path48, body) => request("DELETE", path48, { body })
12443
+ };
12444
+ }
12445
+ var LOCALE_CONFIG_FILENAMES = ["vibgrate.locale.yaml", ".vibgrate/locale.yaml"];
12446
+ function findLocaleConfig(cwd) {
12447
+ for (const name of LOCALE_CONFIG_FILENAMES) {
12448
+ const candidate = path46.join(cwd, name);
12449
+ if (fs14.existsSync(candidate)) return candidate;
12450
+ }
12451
+ return null;
12452
+ }
12453
+ function parseLocaleConfig(text) {
12454
+ const raw = parse(text);
12455
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
12456
+ const obj = raw;
12457
+ const stringArray = (value) => {
12458
+ if (!Array.isArray(value)) return void 0;
12459
+ const items = value.filter((v) => typeof v === "string" && v.trim().length > 0);
12460
+ return items.length ? items : void 0;
12461
+ };
12462
+ const str = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
12463
+ return {
12464
+ project: str(obj.project),
12465
+ sourceLanguage: str(obj.sourceLanguage),
12466
+ targetLanguages: stringArray(obj.targetLanguages),
12467
+ format: str(obj.format),
12468
+ path: str(obj.path),
12469
+ namespaces: stringArray(obj.namespaces),
12470
+ version: str(obj.version)
12471
+ };
12472
+ }
12473
+ function loadLocaleConfig(cwd) {
12474
+ const file = findLocaleConfig(cwd);
12475
+ if (!file) return { config: {}, file: null };
12476
+ try {
12477
+ return { config: parseLocaleConfig(fs14.readFileSync(file, "utf8")), file };
12478
+ } catch (error) {
12479
+ throw new Error(
12480
+ `Could not parse ${path46.relative(cwd, file)}: ${error instanceof Error ? error.message : String(error)}`
12481
+ );
12482
+ }
12483
+ }
12484
+ function serializeLocaleConfig(config) {
12485
+ const out2 = {};
12486
+ for (const [key2, value] of Object.entries(config)) {
12487
+ if (value === void 0 || value === null) continue;
12488
+ if (Array.isArray(value) && value.length === 0) continue;
12489
+ if (typeof value === "string" && !value.trim()) continue;
12490
+ out2[key2] = value;
12491
+ }
12492
+ return stringify(out2);
12493
+ }
12494
+ function writeLocaleConfig(cwd, config, filename) {
12495
+ const target = filename ?? path46.join(cwd, LOCALE_CONFIG_FILENAMES[0]);
12496
+ fs14.mkdirSync(path46.dirname(target), { recursive: true });
12497
+ fs14.writeFileSync(target, serializeLocaleConfig(config), "utf8");
12498
+ return target;
12499
+ }
12500
+ function resolveLocaleSettings(config, flags) {
12501
+ return {
12502
+ project: flags.project ?? config.project,
12503
+ sourceLanguage: flags.sourceLanguage ?? config.sourceLanguage ?? "en-US",
12504
+ targetLanguages: flags.targetLanguages ?? config.targetLanguages ?? [],
12505
+ format: flags.format ?? config.format ?? "nested",
12506
+ path: flags.path ?? config.path ?? "src/locales/{language}/{namespace}.json",
12507
+ namespaces: flags.namespaces ?? config.namespaces ?? ["common"],
12508
+ version: flags.version ?? config.version ?? "latest"
12509
+ };
12510
+ }
12511
+ var SUPPORTED_FORMATS = ["json", "nested", "flat", "yaml", "yml"];
12512
+ var PLANNED_FORMATS = [
12513
+ "po",
12514
+ "xliff",
12515
+ "xliff2",
12516
+ "android",
12517
+ "strings",
12518
+ "resx",
12519
+ "csv",
12520
+ "arb",
12521
+ "properties"
12522
+ ];
12523
+ var LocaleNestConflictError = class extends Error {
12524
+ constructor(leafKey, branchKey) {
12525
+ super(
12526
+ `Cannot write nested output: "${leafKey}" is a value and "${branchKey}" needs it to be a group. Rename one of them, or switch this project to the "flat" format, which stores keys verbatim.`
12527
+ );
12528
+ this.leafKey = leafKey;
12529
+ this.branchKey = branchKey;
12530
+ this.name = "LocaleNestConflictError";
12531
+ }
12532
+ leafKey;
12533
+ branchKey;
12534
+ };
12535
+ function nest(catalog) {
12536
+ const out2 = {};
12537
+ for (const [key2, value] of Object.entries(catalog)) {
12538
+ const parts = key2.split(".");
12539
+ let cursor = out2;
12540
+ for (let i = 0; i < parts.length - 1; i += 1) {
12541
+ const part = parts[i];
12542
+ const existing2 = cursor[part];
12543
+ if (existing2 === void 0) {
12544
+ cursor[part] = {};
12545
+ } else if (typeof existing2 !== "object" || existing2 === null || Array.isArray(existing2)) {
12546
+ throw new LocaleNestConflictError(parts.slice(0, i + 1).join("."), key2);
12547
+ }
12548
+ cursor = cursor[part];
12549
+ }
12550
+ const leaf = parts[parts.length - 1];
12551
+ const existing = cursor[leaf];
12552
+ if (typeof existing === "object" && existing !== null && !Array.isArray(existing)) {
12553
+ throw new LocaleNestConflictError(key2, `${key2}.\u2026`);
12554
+ }
12555
+ cursor[leaf] = value;
12556
+ }
12557
+ return out2;
12558
+ }
12559
+ function flatten(input, prefix = "") {
12560
+ const out2 = {};
12561
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
12562
+ if (prefix) out2[prefix] = input === null || input === void 0 ? "" : String(input);
12563
+ return out2;
12564
+ }
12565
+ for (const [key2, value] of Object.entries(input)) {
12566
+ const next2 = prefix ? `${prefix}.${key2}` : key2;
12567
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
12568
+ Object.assign(out2, flatten(value, next2));
12569
+ } else if (Array.isArray(value)) {
12570
+ out2[next2] = JSON.stringify(value);
12571
+ } else {
12572
+ out2[next2] = value === null || value === void 0 ? "" : String(value);
12573
+ }
12574
+ }
12575
+ return out2;
12576
+ }
12577
+ var nestedJson = {
12578
+ id: "nested",
12579
+ extension: ".json",
12580
+ encode: (catalog) => `${JSON.stringify(nest(sortKeys(catalog)), null, 2)}
12581
+ `,
12582
+ decode: (text) => text.trim() ? flatten(JSON.parse(text)) : {}
12583
+ };
12584
+ var flatJson = {
12585
+ id: "flat",
12586
+ extension: ".json",
12587
+ encode: (catalog) => `${JSON.stringify(sortKeys(catalog), null, 2)}
12588
+ `,
12589
+ decode: (text) => {
12590
+ if (!text.trim()) return {};
12591
+ const parsed = JSON.parse(text);
12592
+ const out2 = {};
12593
+ for (const [key2, value] of Object.entries(parsed)) {
12594
+ out2[key2] = value === null || value === void 0 ? "" : String(value);
12595
+ }
12596
+ return out2;
12597
+ }
12598
+ };
12599
+ var yamlCodec = {
12600
+ id: "yaml",
12601
+ extension: ".yaml",
12602
+ encode: (catalog) => stringify(nest(sortKeys(catalog))),
12603
+ decode: (text) => text.trim() ? flatten(parse(text)) : {}
12604
+ };
12605
+ function sortKeys(catalog) {
12606
+ return Object.fromEntries(Object.entries(catalog).sort(([a], [b]) => a.localeCompare(b)));
12607
+ }
12608
+ var CODECS = {
12609
+ json: nestedJson,
12610
+ nested: nestedJson,
12611
+ flat: flatJson,
12612
+ yaml: yamlCodec,
12613
+ yml: { ...yamlCodec, id: "yml", extension: ".yml" }
12614
+ };
12615
+ function isSupportedFormat(format) {
12616
+ return format in CODECS;
12617
+ }
12618
+ function isPlannedFormat(format) {
12619
+ return PLANNED_FORMATS.includes(format);
12620
+ }
12621
+ function codecFor(format) {
12622
+ const codec = CODECS[format];
12623
+ if (codec) return codec;
12624
+ if (isPlannedFormat(format)) {
12625
+ throw new Error(
12626
+ `The "${format}" format is not available in this release yet. Supported now: ${SUPPORTED_FORMATS.join(", ")}.`
12627
+ );
12628
+ }
12629
+ throw new Error(
12630
+ `Unknown format "${format}". Supported: ${SUPPORTED_FORMATS.join(", ")}. Planned: ${PLANNED_FORMATS.join(", ")}.`
12631
+ );
12632
+ }
12633
+ function validatePathTemplate(template) {
12634
+ if (!template.trim()) return "path template is required.";
12635
+ if (!template.includes("{language}")) {
12636
+ return 'path template must contain {language} (e.g. "src/locales/{language}/{namespace}.json").';
12637
+ }
12638
+ if (path46.isAbsolute(template)) {
12639
+ return "path template must be relative to the project directory.";
12640
+ }
12641
+ if (template.split(/[\\/]/).includes("..")) {
12642
+ return 'path template must not contain "..".';
12643
+ }
12644
+ return null;
12645
+ }
12646
+ function renderPath(template, language, namespace) {
12647
+ return template.replaceAll("{language}", language).replaceAll("{namespace}", namespace);
12648
+ }
12649
+ function resolveLocaleFiles(cwd, template, languages, namespaces) {
12650
+ const out2 = [];
12651
+ for (const language of languages) {
12652
+ for (const namespace of namespaces) {
12653
+ const relative13 = renderPath(template, language, namespace);
12654
+ const file = path46.resolve(cwd, relative13);
12655
+ out2.push({ language, namespace, file, relative: relative13, exists: fs14.existsSync(file) });
12656
+ }
12657
+ }
12658
+ return out2;
12659
+ }
12660
+ function readCatalog(file, format) {
12661
+ if (!fs14.existsSync(file)) return {};
12662
+ const text = fs14.readFileSync(file, "utf8");
12663
+ try {
12664
+ return codecFor(format).decode(text);
12665
+ } catch (error) {
12666
+ throw new Error(
12667
+ `Could not parse ${file}: ${error instanceof Error ? error.message : String(error)}`
12668
+ );
12669
+ }
12670
+ }
12671
+ function writeCatalog(file, format, catalog, opts = {}) {
12672
+ const encoded = codecFor(format).encode(sortKeys(catalog));
12673
+ const existing = fs14.existsSync(file) ? fs14.readFileSync(file, "utf8") : null;
12674
+ const changed = existing !== encoded;
12675
+ if (changed && !opts.dryRun) {
12676
+ fs14.mkdirSync(path46.dirname(file), { recursive: true });
12677
+ fs14.writeFileSync(file, encoded, "utf8");
12678
+ }
12679
+ return { file, changed, bytes: Buffer.byteLength(encoded, "utf8") };
12680
+ }
12681
+ function diffCatalogs(before, after) {
12682
+ const diff2 = { added: [], changed: [], removed: [], unchanged: [] };
12683
+ for (const [key2, value] of Object.entries(after)) {
12684
+ if (!(key2 in before)) diff2.added.push(key2);
12685
+ else if (before[key2] !== value) diff2.changed.push(key2);
12686
+ else diff2.unchanged.push(key2);
12687
+ }
12688
+ for (const key2 of Object.keys(before)) {
12689
+ if (!(key2 in after)) diff2.removed.push(key2);
12690
+ }
12691
+ return diff2;
12692
+ }
12693
+
12694
+ // src/reporting/commands/locale/index.ts
12695
+ var cwdOption = ["-C, --cwd <dir>", "Run against this directory (default: current)"];
12696
+ function root(opts) {
12697
+ return path46.resolve(opts.cwd ?? process.cwd());
12698
+ }
12699
+ function withCommonOptions(cmd) {
12700
+ return cmd.option(...cwdOption).option("--dsn <dsn>", 'DSN token (or use VIBGRATE_DSN, or run "vg login")').option("--project <slug>", "Locale project slug or id").option("--format <format>", `Locale file format (${SUPPORTED_FORMATS.join(", ")})`).option("--path <template>", "Path template, e.g. src/locales/{language}/{namespace}.json").option("--namespace <name>", "Restrict to one namespace").option("--language <code>", "Restrict to one BCP-47 language").option("--ver <version>", "Target version (default: latest)").option("--json", "Machine-readable output").option("-q, --quiet", "Suppress non-essential output");
12701
+ }
12702
+ function settingsFor(opts) {
12703
+ const dir = root(opts);
12704
+ const { config } = loadLocaleConfig(dir);
12705
+ const merged = resolveLocaleSettings(config, {
12706
+ project: opts.project,
12707
+ format: opts.format,
12708
+ path: opts.path,
12709
+ version: opts.ver,
12710
+ namespaces: opts.namespace ? [opts.namespace] : void 0,
12711
+ targetLanguages: opts.language ? [opts.language] : void 0
12712
+ });
12713
+ if (!merged.project) {
12714
+ throw new CliError(
12715
+ 'No locale project configured.\nRun "vg locale config --project <slug>", or pass --project <slug>.',
12716
+ ExitCode.ERROR
12717
+ );
12718
+ }
12719
+ if (!isSupportedFormat(merged.format)) {
12720
+ codecFor(merged.format);
12721
+ }
12722
+ const pathError = validatePathTemplate(merged.path);
12723
+ if (pathError) throw new CliError(pathError, ExitCode.ERROR);
12724
+ return {
12725
+ dir,
12726
+ settings: merged
12727
+ };
12728
+ }
12729
+ function clientFor(opts) {
12730
+ const dsn = resolveDsn(opts.dsn);
12731
+ if (!dsn) {
12732
+ throw new CliError(
12733
+ 'No DSN available.\nRun "vg login", set VIBGRATE_DSN, or pass --dsn.',
12734
+ ExitCode.ERROR
12735
+ );
12736
+ }
12737
+ return createLocaleClient(dsn);
12738
+ }
12739
+ function emit2(opts, payload, human) {
12740
+ if (opts.json) {
12741
+ console.log(JSON.stringify(payload, null, 2));
12742
+ return;
12743
+ }
12744
+ if (!opts.quiet) human();
12745
+ }
12746
+ var configCmd = withCommonOptions(
12747
+ new Command("config").description("Create or update vibgrate.locale.yaml (no credentials are stored)")
12748
+ ).option("--source-language <code>", "Source language (default: en-US)").option("--target-languages <codes>", "Comma-separated target languages").option("--namespaces <names>", "Comma-separated namespaces").action(
12749
+ async (opts) => {
12750
+ const dir = root(opts);
12751
+ const { config, file } = loadLocaleConfig(dir);
12752
+ const csv2 = (v) => v ? v.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
12753
+ const next2 = {
12754
+ project: opts.project ?? config.project,
12755
+ sourceLanguage: opts.sourceLanguage ?? config.sourceLanguage ?? "en-US",
12756
+ targetLanguages: csv2(opts.targetLanguages) ?? config.targetLanguages,
12757
+ format: opts.format ?? config.format ?? "nested",
12758
+ path: opts.path ?? config.path ?? "src/locales/{language}/{namespace}.json",
12759
+ namespaces: csv2(opts.namespaces) ?? config.namespaces ?? ["common"],
12760
+ version: opts.ver ?? config.version ?? "latest"
12761
+ };
12762
+ const pathError = validatePathTemplate(next2.path);
12763
+ if (pathError) throw new CliError(pathError, ExitCode.ERROR);
12764
+ if (!isSupportedFormat(next2.format)) codecFor(next2.format);
12765
+ const written = writeLocaleConfig(dir, next2, file ?? void 0);
12766
+ emit2(opts, { file: written, config: next2 }, () => {
12767
+ console.log(chalk7.green(`Wrote ${path46.relative(dir, written) || path46.basename(written)}`));
12768
+ console.log(
12769
+ chalk7.dim('Credentials are not stored here \u2014 run "vg login" or set VIBGRATE_DSN.')
12770
+ );
12771
+ });
12772
+ }
12773
+ );
12774
+ var statusCmd = withCommonOptions(
12775
+ new Command("status").description("Show translation coverage and health for the locale project")
12776
+ ).action(async (opts) => {
12777
+ const { settings } = settingsFor(opts);
12778
+ const client = clientFor(opts);
12779
+ const res = await client.get(
12780
+ `/projects/${encodeURIComponent(settings.project)}/status`
12781
+ );
12782
+ const data = res.data;
12783
+ if (!data) throw new CliError("Vibgrate Cloud returned no status data.", ExitCode.ERROR);
12784
+ emit2(opts, data, () => {
12785
+ console.log(chalk7.bold(`${data.project.name} (${data.project.slug})`));
12786
+ console.log(chalk7.dim(`Source: ${data.project.sourceLanguage} \xB7 ${data.coverage.totalKeys} keys`));
12787
+ console.log("");
12788
+ for (const lang of data.coverage.languages) {
12789
+ const pct = lang.coveragePercent === null ? "\u2014" : `${lang.coveragePercent}%`;
12790
+ const parts = [`${lang.translated} translated`];
12791
+ if (lang.machine) parts.push(chalk7.yellow(`${lang.machine} machine`));
12792
+ if (lang.needsReview) parts.push(chalk7.yellow(`${lang.needsReview} to review`));
12793
+ if (lang.missing) parts.push(chalk7.dim(`${lang.missing} missing`));
12794
+ console.log(` ${lang.language.padEnd(10)} ${pct.padStart(6)} ${parts.join(" \xB7 ")}`);
12795
+ }
12796
+ if (data.namespaces.length) {
12797
+ console.log("");
12798
+ console.log(chalk7.dim(`Namespaces: ${data.namespaces.map((n) => `${n.name} (${n.keys})`).join(", ")}`));
12799
+ }
12800
+ console.log(
12801
+ chalk7.dim(
12802
+ data.lastTranslationAt ? `Last translation: ${data.lastTranslationAt}` : "No translations recorded yet."
12803
+ )
12804
+ );
12805
+ });
12806
+ });
12807
+ function collectLocalKeys(dir, settings, languages) {
12808
+ const namespaces = settings.namespaces ?? ["common"];
12809
+ const sourceLanguage = settings.sourceLanguage ?? "en-US";
12810
+ const format = settings.format;
12811
+ const template = settings.path;
12812
+ const byKey = /* @__PURE__ */ new Map();
12813
+ const filesRead = [];
12814
+ const allLanguages = [sourceLanguage, ...languages.filter((l) => l !== sourceLanguage)];
12815
+ for (const file of resolveLocaleFiles(dir, template, allLanguages, namespaces)) {
12816
+ if (!file.exists) continue;
12817
+ filesRead.push(file.relative);
12818
+ const catalog = readCatalog(file.file, format);
12819
+ for (const [key2, value] of Object.entries(catalog)) {
12820
+ const id = `${file.namespace}\0${key2}`;
12821
+ const entry = byKey.get(id) ?? {
12822
+ namespace: file.namespace,
12823
+ key: key2,
12824
+ sourceValue: null,
12825
+ translations: []
12826
+ };
12827
+ if (file.language === sourceLanguage) entry.sourceValue = value;
12828
+ else entry.translations.push({ language: file.language, value });
12829
+ byKey.set(id, entry);
12830
+ }
12831
+ }
12832
+ return { keys: [...byKey.values()], filesRead };
12833
+ }
12834
+ var PUSH_BATCH_SIZE = 400;
12835
+ function makePushCommand(name, description) {
12836
+ return withCommonOptions(new Command(name).description(description)).option("--dry-run", "Report what would be pushed without sending anything").option(
12837
+ "--source-only",
12838
+ "Push only source-language keys, leaving existing translations untouched"
12839
+ ).action(async (opts) => {
12840
+ const { dir, settings } = settingsFor(opts);
12841
+ const languages = opts.sourceOnly ? [] : settings.targetLanguages ?? [];
12842
+ const { keys, filesRead } = collectLocalKeys(dir, settings, languages);
12843
+ if (!keys.length) {
12844
+ throw new CliError(
12845
+ `No locale files found for template "${settings.path}".
12846
+ Check the template with "vg locale config --path <template>", or create the source files first.`,
12847
+ ExitCode.ERROR
12848
+ );
12849
+ }
12850
+ if (opts.dryRun) {
12851
+ const summary = {
12852
+ dryRun: true,
12853
+ filesRead,
12854
+ keys: keys.length,
12855
+ translations: keys.reduce((n, k) => n + k.translations.length, 0),
12856
+ version: settings.version
12857
+ };
12858
+ emit2(opts, summary, () => {
12859
+ console.log(chalk7.bold("Dry run \u2014 nothing was sent."));
12860
+ console.log(` ${summary.keys} keys from ${filesRead.length} file(s)`);
12861
+ console.log(` ${summary.translations} translations`);
12862
+ console.log(chalk7.dim(` version: ${summary.version}`));
12863
+ });
12864
+ return;
12865
+ }
12866
+ const client = clientFor(opts);
12867
+ let created = 0;
12868
+ let updated = 0;
12869
+ let translationsWritten = 0;
12870
+ for (let i = 0; i < keys.length; i += PUSH_BATCH_SIZE) {
12871
+ const batch = keys.slice(i, i + PUSH_BATCH_SIZE);
12872
+ const res = await client.post(
12873
+ `/projects/${encodeURIComponent(settings.project)}/keys`,
12874
+ {
12875
+ version: settings.version,
12876
+ keys: batch.map((k) => ({
12877
+ namespace: k.namespace,
12878
+ key: k.key,
12879
+ sourceValue: k.sourceValue,
12880
+ translations: k.translations.map((t) => ({
12881
+ language: t.language,
12882
+ value: t.value,
12883
+ origin: "import"
12884
+ }))
12885
+ }))
12886
+ }
12887
+ );
12888
+ created += res.data?.created ?? 0;
12889
+ updated += res.data?.updated ?? 0;
12890
+ translationsWritten += res.data?.translationsWritten ?? 0;
12891
+ }
12892
+ emit2(opts, { created, updated, translationsWritten, version: settings.version }, () => {
12893
+ console.log(
12894
+ chalk7.green(
12895
+ `Pushed ${keys.length} keys \u2014 ${created} created, ${updated} updated, ${translationsWritten} translations.`
12896
+ )
12897
+ );
12898
+ });
12899
+ });
12900
+ }
12901
+ var PULL_PAGE_SIZE = 200;
12902
+ async function fetchAllKeys(client, project, language, namespace) {
12903
+ const out2 = [];
12904
+ let offset = 0;
12905
+ for (; ; ) {
12906
+ const res = await client.get(`/projects/${encodeURIComponent(project)}/keys`, {
12907
+ language,
12908
+ namespace,
12909
+ limit: PULL_PAGE_SIZE,
12910
+ offset
12911
+ });
12912
+ const page = res.data?.keys ?? [];
12913
+ out2.push(...page);
12914
+ const total = res.meta?.pagination?.total ?? out2.length;
12915
+ offset += page.length;
12916
+ if (!page.length || out2.length >= total) break;
12917
+ }
12918
+ return out2;
12919
+ }
12920
+ function makePullCommand(name, description) {
12921
+ return withCommonOptions(new Command(name).description(description)).option("--dry-run", "Report what would change without writing any file").option(
12922
+ "--include-machine",
12923
+ "Also write machine and needs-review translations (default: accepted translations only)"
12924
+ ).action(async (opts) => {
12925
+ const { dir, settings } = settingsFor(opts);
12926
+ const client = clientFor(opts);
12927
+ const languages = opts.language ? [opts.language] : settings.targetLanguages ?? [];
12928
+ if (!languages.length) {
12929
+ throw new CliError(
12930
+ 'No target languages configured.\nRun "vg locale config --target-languages fr-FR,de-DE", or pass --language <code>.',
12931
+ ExitCode.ERROR
12932
+ );
12933
+ }
12934
+ const namespaces = settings.namespaces ?? ["common"];
12935
+ const results = [];
12936
+ for (const language of languages) {
12937
+ for (const namespace of namespaces) {
12938
+ const keys = await fetchAllKeys(client, settings.project, language, namespace);
12939
+ const catalog = {};
12940
+ for (const entry of keys) {
12941
+ const t = entry.translation;
12942
+ if (!t || t.value === null) continue;
12943
+ const accepted = t.status === "translated";
12944
+ if (!accepted && !opts.includeMachine) continue;
12945
+ catalog[entry.key] = t.value;
12946
+ }
12947
+ const target = resolveLocaleFiles(dir, settings.path, [language], [namespace])[0];
12948
+ const before = readCatalog(target.file, settings.format);
12949
+ const diff2 = diffCatalogs(before, catalog);
12950
+ const write = writeCatalog(target.file, settings.format, catalog, {
12951
+ dryRun: opts.dryRun
12952
+ });
12953
+ results.push({
12954
+ file: target.relative,
12955
+ changed: write.changed,
12956
+ added: diff2.added.length,
12957
+ updated: diff2.changed.length
12958
+ });
12959
+ }
12960
+ }
12961
+ const changed = results.filter((r) => r.changed);
12962
+ emit2(opts, { dryRun: Boolean(opts.dryRun), files: results }, () => {
12963
+ if (opts.dryRun) console.log(chalk7.bold("Dry run \u2014 no files were written."));
12964
+ for (const r of changed) {
12965
+ console.log(` ${chalk7.green(r.changed ? "update" : "ok")} ${r.file} (+${r.added} ~${r.updated})`);
12966
+ }
12967
+ const unchanged = results.length - changed.length;
12968
+ console.log(
12969
+ chalk7.dim(
12970
+ `${changed.length} file(s) ${opts.dryRun ? "would change" : "written"}, ${unchanged} unchanged.`
12971
+ )
12972
+ );
12973
+ });
12974
+ });
12975
+ }
12976
+ var saveMissingCmd = withCommonOptions(
12977
+ new Command("save-missing").description(
12978
+ "Push only keys that do not exist in Vibgrate Cloud yet (never overwrites a translation)"
12979
+ )
12980
+ ).option("--dry-run", "Report what would be sent without sending anything").action(async (opts) => {
12981
+ const { dir, settings } = settingsFor(opts);
12982
+ const client = clientFor(opts);
12983
+ const sourceLanguage = settings.sourceLanguage ?? "en-US";
12984
+ const { keys } = collectLocalKeys(dir, settings, []);
12985
+ const namespaces = settings.namespaces ?? ["common"];
12986
+ const existing = /* @__PURE__ */ new Set();
12987
+ for (const namespace of namespaces) {
12988
+ const remote = await fetchAllKeys(client, settings.project, sourceLanguage, namespace);
12989
+ for (const entry of remote) existing.add(`${entry.namespace}\0${entry.key}`);
12990
+ }
12991
+ const missing = keys.filter((k) => !existing.has(`${k.namespace}\0${k.key}`));
12992
+ if (!missing.length) {
12993
+ emit2(opts, { missing: 0 }, () => console.log(chalk7.dim("No missing keys \u2014 nothing to send.")));
12994
+ return;
12995
+ }
12996
+ if (opts.dryRun) {
12997
+ emit2(opts, { dryRun: true, missing: missing.length, keys: missing.map((k) => `${k.namespace}:${k.key}`) }, () => {
12998
+ console.log(chalk7.bold(`Dry run \u2014 ${missing.length} missing key(s) would be created.`));
12999
+ for (const k of missing.slice(0, 20)) console.log(` ${chalk7.green("+")} ${k.namespace}:${k.key}`);
13000
+ if (missing.length > 20) console.log(chalk7.dim(` \u2026and ${missing.length - 20} more`));
13001
+ });
13002
+ return;
13003
+ }
13004
+ let created = 0;
13005
+ for (let i = 0; i < missing.length; i += PUSH_BATCH_SIZE) {
13006
+ const res = await client.post(
13007
+ `/projects/${encodeURIComponent(settings.project)}/keys`,
13008
+ {
13009
+ version: settings.version,
13010
+ keys: missing.slice(i, i + PUSH_BATCH_SIZE).map((k) => ({
13011
+ namespace: k.namespace,
13012
+ key: k.key,
13013
+ sourceValue: k.sourceValue
13014
+ }))
13015
+ }
13016
+ );
13017
+ created += res.data?.created ?? 0;
13018
+ }
13019
+ emit2(opts, { created }, () => console.log(chalk7.green(`Created ${created} missing key(s).`)));
13020
+ });
13021
+ var addCmd2 = withCommonOptions(
13022
+ new Command("add").description("Add or update a single key").argument("<key>", "Key name").argument(
13023
+ "[value]",
13024
+ "Source value"
13025
+ )
13026
+ ).action(async (key2, value, opts) => {
13027
+ const { settings } = settingsFor(opts);
13028
+ const client = clientFor(opts);
13029
+ const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
13030
+ const res = await client.post(
13031
+ `/projects/${encodeURIComponent(settings.project)}/keys`,
13032
+ {
13033
+ version: settings.version,
13034
+ keys: [
13035
+ {
13036
+ namespace,
13037
+ key: key2,
13038
+ sourceValue: value ?? null,
13039
+ translations: opts.language && value !== void 0 ? [{ language: opts.language, value, origin: "manual" }] : []
13040
+ }
13041
+ ]
13042
+ }
13043
+ );
13044
+ emit2(
13045
+ opts,
13046
+ res.data,
13047
+ () => console.log(chalk7.green(`${res.data?.created ? "Created" : "Updated"} ${namespace}:${key2}`))
13048
+ );
13049
+ });
13050
+ var removeCmd = withCommonOptions(
13051
+ new Command("remove").description("Remove one or more keys").argument("<keys...>", "Key names")
13052
+ ).action(async (keys, opts) => {
13053
+ const { settings } = settingsFor(opts);
13054
+ const client = clientFor(opts);
13055
+ const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
13056
+ const res = await client.del(
13057
+ `/projects/${encodeURIComponent(settings.project)}/keys`,
13058
+ { namespace, keys }
13059
+ );
13060
+ emit2(
13061
+ opts,
13062
+ res.data,
13063
+ () => console.log(
13064
+ chalk7.green(
13065
+ `Removed ${res.data?.removed ?? 0} key(s) and ${res.data?.translationsRemoved ?? 0} translation(s).`
13066
+ )
13067
+ )
13068
+ );
13069
+ });
13070
+ var getCmd = withCommonOptions(
13071
+ new Command("get").description("Show one key and its translations").argument("<key>", "Key name")
13072
+ ).action(async (key2, opts) => {
13073
+ const { settings } = settingsFor(opts);
13074
+ const client = clientFor(opts);
13075
+ const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
13076
+ const res = await client.get(
13077
+ `/projects/${encodeURIComponent(settings.project)}/keys`,
13078
+ { namespace, q: key2, language: opts.language, limit: 50 }
13079
+ );
13080
+ const match = (res.data?.keys ?? []).find((k) => k.key === key2);
13081
+ if (!match) {
13082
+ throw new CliError(`Key "${key2}" not found in namespace "${namespace}".`, ExitCode.ERROR);
13083
+ }
13084
+ emit2(opts, match, () => {
13085
+ console.log(chalk7.bold(`${match.namespace}:${match.key}`));
13086
+ if (match.sourceValue !== null) console.log(` source: ${match.sourceValue}`);
13087
+ if (match.translation) {
13088
+ const value = match.translation.value ?? chalk7.dim("(not translated)");
13089
+ console.log(` ${opts.language}: ${value} ${chalk7.dim(`[${match.translation.status}]`)}`);
13090
+ }
13091
+ });
13092
+ });
13093
+ var formatCmd = withCommonOptions(
13094
+ new Command("format").description(
13095
+ "Re-format local locale files in place (local-only \u2014 no network, works on any plan)"
13096
+ )
13097
+ ).option("--dry-run", "Report which files would change without writing").action(async (opts) => {
13098
+ const dir = root(opts);
13099
+ const { config } = loadLocaleConfig(dir);
13100
+ const settings = resolveLocaleSettings(config, {
13101
+ format: opts.format,
13102
+ path: opts.path,
13103
+ namespaces: opts.namespace ? [opts.namespace] : void 0,
13104
+ targetLanguages: opts.language ? [opts.language] : void 0
13105
+ });
13106
+ const pathError = validatePathTemplate(settings.path);
13107
+ if (pathError) throw new CliError(pathError, ExitCode.ERROR);
13108
+ const languages = [
13109
+ settings.sourceLanguage ?? "en-US",
13110
+ ...settings.targetLanguages ?? []
13111
+ ];
13112
+ const files = resolveLocaleFiles(dir, settings.path, languages, settings.namespaces ?? ["common"]);
13113
+ const changed = [];
13114
+ for (const file of files) {
13115
+ if (!file.exists) continue;
13116
+ const catalog = readCatalog(file.file, settings.format);
13117
+ const result = writeCatalog(file.file, settings.format, catalog, { dryRun: opts.dryRun });
13118
+ if (result.changed) changed.push(file.relative);
13119
+ }
13120
+ emit2(opts, { dryRun: Boolean(opts.dryRun), changed }, () => {
13121
+ for (const f of changed) console.log(` ${chalk7.green("format")} ${f}`);
13122
+ console.log(
13123
+ chalk7.dim(`${changed.length} file(s) ${opts.dryRun ? "would be reformatted" : "reformatted"}.`)
13124
+ );
13125
+ });
13126
+ });
13127
+ function buildLocaleCommand(name) {
13128
+ const cmd = new Command(name).description(
13129
+ "Manage application translations \u2014 locale projects, keys, and translations in Vibgrate Cloud"
13130
+ );
13131
+ cmd.addCommand(configCmd);
13132
+ cmd.addCommand(makePushCommand("push", "Upload local locale files to Vibgrate Cloud"));
13133
+ cmd.addCommand(makePushCommand("sync", "Alias for push"));
13134
+ cmd.addCommand(makePullCommand("pull", "Write translations from Vibgrate Cloud into local files"));
13135
+ cmd.addCommand(makePullCommand("download", "Alias for pull"));
13136
+ cmd.addCommand(saveMissingCmd);
13137
+ cmd.addCommand(addCmd2);
13138
+ cmd.addCommand(removeCmd);
13139
+ cmd.addCommand(getCmd);
13140
+ cmd.addCommand(statusCmd);
13141
+ cmd.addCommand(formatCmd);
13142
+ cmd.addHelpText(
13143
+ "after",
13144
+ "\nLocal-only commands (no network, any plan): config, format\nCloud commands (Team plan or above): push, pull, save-missing, add, remove, get, status\n"
13145
+ );
13146
+ return cmd;
13147
+ }
13148
+ function registerLocale(program) {
13149
+ program.addCommand(buildLocaleCommand("locale"));
13150
+ const alias = buildLocaleCommand("localize");
13151
+ alias.description('Alias for "vg locale"');
13152
+ program.addCommand(alias, { hidden: true });
13153
+ }
12256
13154
  var OPENVEX_CONTEXT = "https://openvex.dev/ns/v0.2.0";
12257
13155
  var VEX_STATUSES = ["not_affected", "affected", "fixed", "under_investigation"];
12258
13156
  var VEX_JUSTIFICATIONS = [
@@ -12342,7 +13240,7 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
12342
13240
  try {
12343
13241
  const statements = [];
12344
13242
  if (opts.from) {
12345
- const fromPath = path43.resolve(opts.from);
13243
+ const fromPath = path46.resolve(opts.from);
12346
13244
  if (!await pathExists(fromPath)) {
12347
13245
  process.stderr.write(chalk7.red(`Statements file not found: ${fromPath}
12348
13246
  `));
@@ -12360,9 +13258,9 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
12360
13258
  });
12361
13259
  const json2 = JSON.stringify(doc, null, 2);
12362
13260
  if (opts.out) {
12363
- await writeTextFile(path43.resolve(opts.out), json2 + "\n");
13261
+ await writeTextFile(path46.resolve(opts.out), json2 + "\n");
12364
13262
  process.stderr.write(
12365
- chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${path43.resolve(opts.out)}
13263
+ chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${path46.resolve(opts.out)}
12366
13264
  `
12367
13265
  );
12368
13266
  } else {
@@ -12545,7 +13443,7 @@ function formatDeltaText(base, current) {
12545
13443
  return lines.join("\n");
12546
13444
  }
12547
13445
  async function readArtifactOrExit(filePath) {
12548
- const absolutePath = path43.resolve(filePath);
13446
+ const absolutePath = path46.resolve(filePath);
12549
13447
  if (!await pathExists(absolutePath)) {
12550
13448
  console.error(chalk7.red(`Artifact not found: ${absolutePath}`));
12551
13449
  process.exit(1);
@@ -12562,7 +13460,7 @@ var exportCommand = new Command("export").description("Export scan artifact as S
12562
13460
  const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
12563
13461
  const body = JSON.stringify(sbom, null, 2);
12564
13462
  if (opts.out) {
12565
- await writeTextFile(path43.resolve(opts.out), body);
13463
+ await writeTextFile(path46.resolve(opts.out), body);
12566
13464
  console.log(chalk7.green("\u2714") + ` SBOM written to ${opts.out}`);
12567
13465
  } else {
12568
13466
  console.log(body);
@@ -12573,7 +13471,7 @@ var deltaCommand = new Command("delta").description("Show SBOM delta between two
12573
13471
  const current = await readArtifactOrExit(opts.to);
12574
13472
  const report = formatDeltaText(base, current);
12575
13473
  if (opts.out) {
12576
- await writeTextFile(path43.resolve(opts.out), report);
13474
+ await writeTextFile(path46.resolve(opts.out), report);
12577
13475
  console.log(chalk7.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
12578
13476
  } else {
12579
13477
  console.log(report);
@@ -12703,66 +13601,66 @@ function resolveRegime(id) {
12703
13601
  }
12704
13602
  return regime;
12705
13603
  }
12706
- function evidenceDir(root2) {
12707
- return path43.join(root2, ".vibgrate", "evidence");
13604
+ function evidenceDir(root3) {
13605
+ return path46.join(root3, ".vibgrate", "evidence");
12708
13606
  }
12709
- function orgPath(root2) {
12710
- return path43.join(evidenceDir(root2), "org.json");
13607
+ function orgPath(root3) {
13608
+ return path46.join(evidenceDir(root3), "org.json");
12711
13609
  }
12712
- function productsPath(root2) {
12713
- return path43.join(evidenceDir(root2), "products.json");
13610
+ function productsPath(root3) {
13611
+ return path46.join(evidenceDir(root3), "products.json");
12714
13612
  }
12715
- function releasesDir(root2) {
12716
- return path43.join(evidenceDir(root2), "releases");
13613
+ function releasesDir(root3) {
13614
+ return path46.join(evidenceDir(root3), "releases");
12717
13615
  }
12718
- function releasePath(root2, productId, version) {
13616
+ function releasePath(root3, productId, version) {
12719
13617
  const safe = `${productId}@${version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
12720
- return path43.join(releasesDir(root2), `${safe}.json`);
13618
+ return path46.join(releasesDir(root3), `${safe}.json`);
12721
13619
  }
12722
13620
  var DEFAULT_ORG = {
12723
13621
  responsiblePersons: [],
12724
13622
  defaultRegime: DEFAULT_REGIME
12725
13623
  };
12726
- async function loadOrg(root2) {
12727
- const p = orgPath(root2);
13624
+ async function loadOrg(root3) {
13625
+ const p = orgPath(root3);
12728
13626
  if (!await pathExists(p)) return { ...DEFAULT_ORG };
12729
13627
  const org = await readJsonFile(p);
12730
13628
  return { ...DEFAULT_ORG, ...org, responsiblePersons: org.responsiblePersons ?? [] };
12731
13629
  }
12732
- async function saveOrg(root2, org) {
12733
- await writeJsonFile(orgPath(root2), org);
13630
+ async function saveOrg(root3, org) {
13631
+ await writeJsonFile(orgPath(root3), org);
12734
13632
  }
12735
- async function loadProducts(root2) {
12736
- const p = productsPath(root2);
13633
+ async function loadProducts(root3) {
13634
+ const p = productsPath(root3);
12737
13635
  if (!await pathExists(p)) return [];
12738
13636
  return readJsonFile(p);
12739
13637
  }
12740
- async function saveProducts(root2, products) {
12741
- await writeJsonFile(productsPath(root2), products);
13638
+ async function saveProducts(root3, products) {
13639
+ await writeJsonFile(productsPath(root3), products);
12742
13640
  }
12743
- async function getProduct(root2, id) {
12744
- const products = await loadProducts(root2);
13641
+ async function getProduct(root3, id) {
13642
+ const products = await loadProducts(root3);
12745
13643
  return products.find((p) => p.id === id || p.name === id);
12746
13644
  }
12747
- async function freezeRelease(root2, release) {
12748
- const p = releasePath(root2, release.productId, release.version);
13645
+ async function freezeRelease(root3, release) {
13646
+ const p = releasePath(root3, release.productId, release.version);
12749
13647
  if (await pathExists(p)) {
12750
13648
  throw new CliError(
12751
- `release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${path43.relative(root2, p)} only if it was frozen in error`,
13649
+ `release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${path46.relative(root3, p)} only if it was frozen in error`,
12752
13650
  ExitCode.USAGE_ERROR
12753
13651
  );
12754
13652
  }
12755
13653
  await writeJsonFile(p, release);
12756
13654
  return p;
12757
13655
  }
12758
- async function loadReleases(root2, productId) {
12759
- const dir = releasesDir(root2);
13656
+ async function loadReleases(root3, productId) {
13657
+ const dir = releasesDir(root3);
12760
13658
  if (!await pathExists(dir)) return [];
12761
13659
  const { readdir } = await import('fs/promises');
12762
13660
  const files = (await readdir(dir)).filter((f) => f.endsWith(".json")).sort();
12763
13661
  const releases = [];
12764
13662
  for (const f of files) {
12765
- const rel = await readJsonFile(path43.join(dir, f));
13663
+ const rel = await readJsonFile(path46.join(dir, f));
12766
13664
  if (!productId || rel.productId === productId) releases.push(rel);
12767
13665
  }
12768
13666
  return releases;
@@ -13455,14 +14353,14 @@ function verifyEvidenceEnvelope(env, opts = {}) {
13455
14353
  };
13456
14354
  }
13457
14355
  var DEFAULT_KEY = "attest-key.pem";
13458
- function resolveSigningKey(root2, explicit) {
14356
+ function resolveSigningKey(root3, explicit) {
13459
14357
  const chosen = process.env.VG_ATTEST_KEY;
13460
- const keyPath = chosen ? path43.resolve(chosen) : path43.join(root2, ".vibgrate", DEFAULT_KEY);
14358
+ const keyPath = chosen ? path46.resolve(chosen) : path46.join(root3, ".vibgrate", DEFAULT_KEY);
13461
14359
  let minted = false;
13462
14360
  if (!fs14.existsSync(keyPath)) {
13463
14361
  if (chosen) throw new CliError(`signing key not found: ${chosen}`, ExitCode.USAGE_ERROR);
13464
14362
  const kp = generateKeypair();
13465
- fs14.mkdirSync(path43.dirname(keyPath), { recursive: true });
14363
+ fs14.mkdirSync(path46.dirname(keyPath), { recursive: true });
13466
14364
  fs14.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
13467
14365
  fs14.writeFileSync(`${keyPath}.pub`, kp.publicPem);
13468
14366
  minted = true;
@@ -13480,16 +14378,16 @@ function resolveSigningKey(root2, explicit) {
13480
14378
  }
13481
14379
  function writeBundle(input) {
13482
14380
  const dir = input.outDir;
13483
- fs14.mkdirSync(path43.join(dir, "inputs", "releases"), { recursive: true });
13484
- const write = (rel, data) => fs14.writeFileSync(path43.join(dir, rel), `${JSON.stringify(data, null, 2)}
14381
+ fs14.mkdirSync(path46.join(dir, "inputs", "releases"), { recursive: true });
14382
+ const write = (rel, data) => fs14.writeFileSync(path46.join(dir, rel), `${JSON.stringify(data, null, 2)}
13485
14383
  `);
13486
14384
  write("result.json", input.result);
13487
- write(path43.join("inputs", "advisory.json"), input.advisory);
14385
+ write(path46.join("inputs", "advisory.json"), input.advisory);
13488
14386
  for (const rel of input.releases) {
13489
14387
  const safe = `${rel.productId}@${rel.version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
13490
- write(path43.join("inputs", "releases", `${safe}.json`), rel);
14388
+ write(path46.join("inputs", "releases", `${safe}.json`), rel);
13491
14389
  }
13492
- write(path43.join("inputs", "datapack.lock"), { dataPackVersion: input.result.meta.dataPackVersion, kernelVersion: input.result.meta.kernelVersion });
14390
+ write(path46.join("inputs", "datapack.lock"), { dataPackVersion: input.result.meta.dataPackVersion, kernelVersion: input.result.meta.kernelVersion });
13493
14391
  write("provenance.json", {
13494
14392
  method: "deterministic",
13495
14393
  modelDrafted: [],
@@ -13505,13 +14403,13 @@ function writeBundle(input) {
13505
14403
  disclaimer: input.regime.disclaimer
13506
14404
  });
13507
14405
  if (input.envelope) {
13508
- fs14.writeFileSync(path43.join(dir, "evidence.intoto.jsonl"), `${JSON.stringify(input.envelope)}
14406
+ fs14.writeFileSync(path46.join(dir, "evidence.intoto.jsonl"), `${JSON.stringify(input.envelope)}
13509
14407
  `);
13510
14408
  }
13511
14409
  if (input.timestampToken) {
13512
- fs14.writeFileSync(path43.join(dir, "timestamp.tsr"), input.timestampToken);
14410
+ fs14.writeFileSync(path46.join(dir, "timestamp.tsr"), input.timestampToken);
13513
14411
  }
13514
- fs14.writeFileSync(path43.join(dir, "VERIFY.md"), verifyDoc(input));
14412
+ fs14.writeFileSync(path46.join(dir, "VERIFY.md"), verifyDoc(input));
13515
14413
  return dir;
13516
14414
  }
13517
14415
  function verifyDoc(input) {
@@ -13571,14 +14469,14 @@ function undeterminedFields(result, regime) {
13571
14469
  if (result.products.some((p) => p.status === "affected" && p.memberStates.length === 0)) gaps.push("member_states");
13572
14470
  return gaps.filter((g) => regime.submission.requires.some((r) => g.startsWith(r)) || g.startsWith("exposure") || g.startsWith("member"));
13573
14471
  }
13574
- async function recordDrill(root2, record) {
13575
- const dir = path43.join(evidenceDir(root2), "drills");
13576
- const p = path43.join(dir, `${record.drillId}.json`);
14472
+ async function recordDrill(root3, record) {
14473
+ const dir = path46.join(evidenceDir(root3), "drills");
14474
+ const p = path46.join(dir, `${record.drillId}.json`);
13577
14475
  await writeJsonFile(p, record);
13578
14476
  return p;
13579
14477
  }
13580
- async function hasRecentDrill(root2, asOf) {
13581
- const dir = path43.join(evidenceDir(root2), "drills");
14478
+ async function hasRecentDrill(root3, asOf) {
14479
+ const dir = path46.join(evidenceDir(root3), "drills");
13582
14480
  if (!await pathExists(dir)) return void 0;
13583
14481
  const { readdir } = await import('fs/promises');
13584
14482
  const { readJsonFile: readJsonFile2 } = await import('./fs-4G5A65KS.js');
@@ -13586,7 +14484,7 @@ async function hasRecentDrill(root2, asOf) {
13586
14484
  if (files.length === 0) return void 0;
13587
14485
  const cutoff = new Date(new Date(asOf).getTime() - 90 * 24 * 3600 * 1e3).toISOString();
13588
14486
  for (const f of files) {
13589
- const rec = await readJsonFile2(path43.join(dir, f));
14487
+ const rec = await readJsonFile2(path46.join(dir, f));
13590
14488
  if (rec.ranAt >= cutoff) return true;
13591
14489
  }
13592
14490
  return false;
@@ -13665,8 +14563,8 @@ function formatRegimeList() {
13665
14563
  }
13666
14564
 
13667
14565
  // src/reporting/commands/evidence/index.ts
13668
- function root(opts) {
13669
- return path43.resolve(opts.cwd ?? process.cwd());
14566
+ function root2(opts) {
14567
+ return path46.resolve(opts.cwd ?? process.cwd());
13670
14568
  }
13671
14569
  function slug(name) {
13672
14570
  return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "product";
@@ -13680,14 +14578,14 @@ function nowIso() {
13680
14578
  function csv(v) {
13681
14579
  return v ? v.split(",").map((s) => s.trim()).filter(Boolean) : [];
13682
14580
  }
13683
- var cwdOption = ["-C, --cwd <dir>", "Run against this directory (default: current)"];
14581
+ var cwdOption2 = ["-C, --cwd <dir>", "Run against this directory (default: current)"];
13684
14582
  async function releasesByProductMap(rootDir, products) {
13685
14583
  const map = /* @__PURE__ */ new Map();
13686
14584
  for (const p of products) map.set(p.id, await loadReleases(rootDir, p.id));
13687
14585
  return map;
13688
14586
  }
13689
- var initCmd = new Command("init").description("Set org, coordinator, and the person with filing authority (per regime)").option(...cwdOption).option("--regime <id>", "Default regime for this repo", DEFAULT_REGIME).option("--legal-entity <name>", "Legal entity name").option("--establishment <cc>", "Main establishment (country code)").option("--eu-rep <name>", "EU authorised representative").option("--coordinator <csirt>", "Coordinator CSIRT / competent authority").option("--responsible <name>", "Add a responsible person").option("--role <role>", "Role of the responsible person").option("--filing-authority", "The responsible person has filing authority").option("--ooo <contact>", "Out-of-hours contact for the responsible person").action(async (opts) => {
13690
- const rootDir = root(opts);
14587
+ var initCmd = new Command("init").description("Set org, coordinator, and the person with filing authority (per regime)").option(...cwdOption2).option("--regime <id>", "Default regime for this repo", DEFAULT_REGIME).option("--legal-entity <name>", "Legal entity name").option("--establishment <cc>", "Main establishment (country code)").option("--eu-rep <name>", "EU authorised representative").option("--coordinator <csirt>", "Coordinator CSIRT / competent authority").option("--responsible <name>", "Add a responsible person").option("--role <role>", "Role of the responsible person").option("--filing-authority", "The responsible person has filing authority").option("--ooo <contact>", "Out-of-hours contact for the responsible person").action(async (opts) => {
14588
+ const rootDir = root2(opts);
13691
14589
  resolveRegime(opts.regime);
13692
14590
  const org = await loadOrg(rootDir);
13693
14591
  org.defaultRegime = opts.regime;
@@ -13709,8 +14607,8 @@ var initCmd = new Command("init").description("Set org, coordinator, and the per
13709
14607
  var regimesCmd = new Command("regimes").description("List available reporting regimes").action(() => {
13710
14608
  console.log(formatRegimeList());
13711
14609
  });
13712
- var productAdd = new Command("add").description("Register a product with digital elements (PDE)").argument("<name>", "Product name").option(...cwdOption).option("--regime <id>", "Regime whose classification vocabulary applies").option("--classification <id>", "Regime classification id (see `vg evidence regimes`)", "default").option("--markets <cc,cc>", "Member states / markets, comma-separated").option("--bind <ref>", "Bind to a repo/image/registry (repeatable)", (v, acc) => [...acc, v], []).option("--in-scope", "Record an in-scope determination").option("--out-of-scope", "Record an out-of-scope determination").option("--rationale <text>", "Scope determination rationale").option("--until <date>", "Declared support period end (YYYY-MM-DD)").action(async (name, opts) => {
13713
- const rootDir = root(opts);
14610
+ var productAdd = new Command("add").description("Register a product with digital elements (PDE)").argument("<name>", "Product name").option(...cwdOption2).option("--regime <id>", "Regime whose classification vocabulary applies").option("--classification <id>", "Regime classification id (see `vg evidence regimes`)", "default").option("--markets <cc,cc>", "Member states / markets, comma-separated").option("--bind <ref>", "Bind to a repo/image/registry (repeatable)", (v, acc) => [...acc, v], []).option("--in-scope", "Record an in-scope determination").option("--out-of-scope", "Record an out-of-scope determination").option("--rationale <text>", "Scope determination rationale").option("--until <date>", "Declared support period end (YYYY-MM-DD)").action(async (name, opts) => {
14611
+ const rootDir = root2(opts);
13714
14612
  const regime = resolveRegime(opts.regime ?? (await loadOrg(rootDir)).defaultRegime);
13715
14613
  if (opts.classification !== "default" && regime.classifications.length && !regime.classifications.some((c2) => c2.id === opts.classification)) {
13716
14614
  throw new CliError(`classification "${opts.classification}" is not valid for regime ${regime.id} \u2014 options: ${regime.classifications.map((c2) => c2.id).join(", ")}`, ExitCode.USAGE_ERROR);
@@ -13732,29 +14630,29 @@ var productAdd = new Command("add").description("Register a product with digital
13732
14630
  await saveProducts(rootDir, products);
13733
14631
  console.log(chalk7.green("\u2714") + ` product ${chalk7.bold(id)} registered (${regime.name})`);
13734
14632
  });
13735
- var productList = new Command("list").description("List registered products").option(...cwdOption).action(async (opts) => {
13736
- const products = await loadProducts(root(opts));
14633
+ var productList = new Command("list").description("List registered products").option(...cwdOption2).action(async (opts) => {
14634
+ const products = await loadProducts(root2(opts));
13737
14635
  if (!products.length) return console.log(chalk7.dim("no products registered \u2014 vg evidence product add <name>"));
13738
14636
  for (const p of products) {
13739
14637
  console.log(` ${chalk7.bold(p.id)} ${p.name} ${chalk7.dim(`[${p.classification}]`)} ${chalk7.dim(p.memberStates.join(" ") || "no markets")}`);
13740
14638
  }
13741
14639
  });
13742
- var productShow = new Command("show").description("Show a product and its frozen releases").argument("<id>").option(...cwdOption).action(async (id, opts) => {
13743
- const rootDir = root(opts);
14640
+ var productShow = new Command("show").description("Show a product and its frozen releases").argument("<id>").option(...cwdOption2).action(async (id, opts) => {
14641
+ const rootDir = root2(opts);
13744
14642
  const product = await getProduct(rootDir, id);
13745
14643
  if (!product) throw new CliError(`no such product: ${id}`, ExitCode.NOT_FOUND);
13746
14644
  const releases = await loadReleases(rootDir, product.id);
13747
14645
  console.log(JSON.stringify({ product, releases: releases.map((r) => ({ version: r.version, shipDate: r.shipDate, components: r.components.length })) }, null, 2));
13748
14646
  });
13749
14647
  var productCmd = new Command("product").description("Register and inspect products with digital elements").addCommand(productAdd).addCommand(productList).addCommand(productShow);
13750
- var releaseCmd = new Command("release").description("Freeze a shipped release into an immutable component manifest").argument("<product>", "Product id").argument("<version>", "Shipped version").option(...cwdOption).option("--from <file>", "Scan artifact or SBOM captured at release", ".vibgrate/scan_result.json").option("--ship-date <date>", "Ship date (YYYY-MM-DD)").option("--build-id <id>", "Build id").option("--digest <sha256>", "Artefact digest").option("--markets <cc,cc>", "Distribution markets/channels, comma-separated").action(async (productId, version, opts) => {
13751
- const rootDir = root(opts);
14648
+ var releaseCmd = new Command("release").description("Freeze a shipped release into an immutable component manifest").argument("<product>", "Product id").argument("<version>", "Shipped version").option(...cwdOption2).option("--from <file>", "Scan artifact or SBOM captured at release", ".vibgrate/scan_result.json").option("--ship-date <date>", "Ship date (YYYY-MM-DD)").option("--build-id <id>", "Build id").option("--digest <sha256>", "Artefact digest").option("--markets <cc,cc>", "Distribution markets/channels, comma-separated").action(async (productId, version, opts) => {
14649
+ const rootDir = root2(opts);
13752
14650
  const product = await getProduct(rootDir, productId);
13753
14651
  if (!product) throw new CliError(`no such product: ${productId} \u2014 register it with \`vg evidence product add\``, ExitCode.NOT_FOUND);
13754
14652
  const release = await buildRelease({
13755
14653
  productId: product.id,
13756
14654
  version,
13757
- from: path43.resolve(rootDir, opts.from),
14655
+ from: path46.resolve(rootDir, opts.from),
13758
14656
  shipDate: opts.shipDate,
13759
14657
  buildId: opts.buildId,
13760
14658
  artefactDigest: opts.digest,
@@ -13762,7 +14660,7 @@ var releaseCmd = new Command("release").description("Freeze a shipped release in
13762
14660
  frozenAt: nowIso()
13763
14661
  });
13764
14662
  const p = await freezeRelease(rootDir, release);
13765
- console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${path43.relative(rootDir, p)}`);
14663
+ console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${path46.relative(rootDir, p)}`);
13766
14664
  });
13767
14665
  async function runExposure(rootDir, vuln, opts) {
13768
14666
  const org = await loadOrg(rootDir);
@@ -13794,8 +14692,8 @@ function exposureExit(status) {
13794
14692
  if (status === "undetermined") return ExitCode.NOT_FOUND;
13795
14693
  return ExitCode.OK;
13796
14694
  }
13797
- var exposureCmd = new Command("exposure").description("Which shipped products contain this vulnerability \u2014 with signed evidence").argument("<vuln>", "Vulnerability id (CVE / GHSA / OSV / EUVD)").option(...cwdOption).option("--regime <id>", "Reporting regime").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped) instead of fetching").option("--offline", "No network; requires --advisory").option("--as-of <date>", "Determine as of this date (drives support status)").option("--products <substr>", "Restrict to products matching this substring").option("--include-eol", "Include releases whose support period has expired").option("--format <fmt>", "Output format (table|json)", "table").option("--generated-at <iso>", "Override the recorded timestamp (reproducibility)").option("--data-pack-version <v>", "Record the data pack version used").option("--pack", "Also print the submission pack").option("--stage <stage>", "Pack stage (regime-specific)").option("--bundle <dir>", "Write a signed evidence bundle to this directory").option("--no-sign", "Do not sign the evidence bundle").option("--tsa <url>", "RFC 3161 Time-Stamping Authority URL to timestamp the result").option("--pub <file>", "Trust root to pin the signer when re-verifying").action(async (vuln, opts) => {
13798
- const rootDir = root(opts);
14695
+ var exposureCmd = new Command("exposure").description("Which shipped products contain this vulnerability \u2014 with signed evidence").argument("<vuln>", "Vulnerability id (CVE / GHSA / OSV / EUVD)").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped) instead of fetching").option("--offline", "No network; requires --advisory").option("--as-of <date>", "Determine as of this date (drives support status)").option("--products <substr>", "Restrict to products matching this substring").option("--include-eol", "Include releases whose support period has expired").option("--format <fmt>", "Output format (table|json)", "table").option("--generated-at <iso>", "Override the recorded timestamp (reproducibility)").option("--data-pack-version <v>", "Record the data pack version used").option("--pack", "Also print the submission pack").option("--stage <stage>", "Pack stage (regime-specific)").option("--bundle <dir>", "Write a signed evidence bundle to this directory").option("--no-sign", "Do not sign the evidence bundle").option("--tsa <url>", "RFC 3161 Time-Stamping Authority URL to timestamp the result").option("--pub <file>", "Trust root to pin the signer when re-verifying").action(async (vuln, opts) => {
14696
+ const rootDir = root2(opts);
13799
14697
  const { result, regime, releases, advisory } = await runExposure(rootDir, vuln, opts);
13800
14698
  let timestampToken;
13801
14699
  if (opts.tsa) {
@@ -13814,7 +14712,7 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
13814
14712
  console.log("\n" + buildPack(result, regime, org, stage));
13815
14713
  }
13816
14714
  if (opts.bundle) {
13817
- const dir = path43.resolve(rootDir, opts.bundle, `evidence-${result.meta.evidenceId}`);
14715
+ const dir = path46.resolve(rootDir, opts.bundle, `evidence-${result.meta.evidenceId}`);
13818
14716
  let envelope;
13819
14717
  if (opts.sign !== false) {
13820
14718
  const { key: key2, minted } = resolveSigningKey(rootDir);
@@ -13822,12 +14720,12 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
13822
14720
  envelope = signEvidenceStatement(buildEvidenceStatement(result, VERSION), key2);
13823
14721
  }
13824
14722
  writeBundle({ outDir: dir, result, advisory, releases, regime, envelope, timestampToken, cliVersion: VERSION });
13825
- console.error(chalk7.green("\u2714") + ` evidence bundle written to ${path43.relative(rootDir, dir)}${envelope ? " (signed)" : " (unsigned)"}${timestampToken ? " \xB7 RFC 3161" : ""}`);
14723
+ console.error(chalk7.green("\u2714") + ` evidence bundle written to ${path46.relative(rootDir, dir)}${envelope ? " (signed)" : " (unsigned)"}${timestampToken ? " \xB7 RFC 3161" : ""}`);
13826
14724
  }
13827
14725
  process.exitCode = exposureExit(result.overallStatus);
13828
14726
  });
13829
- var readinessCmd = new Command("readiness").description("Deterministic gap report against the regime obligations").option(...cwdOption).option("--regime <id>", "Reporting regime").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
13830
- const rootDir = root(opts);
14727
+ var readinessCmd = new Command("readiness").description("Deterministic gap report against the regime obligations").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
14728
+ const rootDir = root2(opts);
13831
14729
  const org = await loadOrg(rootDir);
13832
14730
  const regime = resolveRegime(opts.regime ?? org.defaultRegime);
13833
14731
  const products = await loadProducts(rootDir);
@@ -13836,8 +14734,8 @@ var readinessCmd = new Command("readiness").description("Deterministic gap repor
13836
14734
  const report = computeReadiness({ regime, org, products, releasesByProduct, recentDrill });
13837
14735
  console.log(opts.format === "json" ? JSON.stringify(report, null, 2) : formatReadiness(report, regime));
13838
14736
  });
13839
- var supportCmd = new Command("support-period").description("Declare or show a product support period").argument("<product>", "Product id").option(...cwdOption).option("--from <date>", "Declared support start (YYYY-MM-DD)").option("--until <date>", "Declared support end (YYYY-MM-DD)").action(async (productId, opts) => {
13840
- const rootDir = root(opts);
14737
+ var supportCmd = new Command("support-period").description("Declare or show a product support period").argument("<product>", "Product id").option(...cwdOption2).option("--from <date>", "Declared support start (YYYY-MM-DD)").option("--until <date>", "Declared support end (YYYY-MM-DD)").action(async (productId, opts) => {
14738
+ const rootDir = root2(opts);
13841
14739
  const products = await loadProducts(rootDir);
13842
14740
  const product = products.find((p) => p.id === productId || p.name === productId);
13843
14741
  if (!product) throw new CliError(`no such product: ${productId}`, ExitCode.NOT_FOUND);
@@ -13847,22 +14745,22 @@ var supportCmd = new Command("support-period").description("Declare or show a pr
13847
14745
  }
13848
14746
  console.log(` ${chalk7.bold(product.id)} support period: ${product.supportPeriod?.declaredFrom ?? "\u2014"} \u2192 ${chalk7.bold(product.supportPeriod?.declaredUntil ?? "not declared")}`);
13849
14747
  });
13850
- var packCmd = new Command("pack").description("Build the submission pack a human pastes into the reporting platform").argument("<vuln>", "Vulnerability id").option(...cwdOption).option("--regime <id>", "Reporting regime").option("--stage <stage>", "Reporting stage").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped)").option("--offline", "No network; requires --advisory").option("--out <file>", "Write the pack to this file").action(async (vuln, opts) => {
13851
- const rootDir = root(opts);
14748
+ var packCmd = new Command("pack").description("Build the submission pack a human pastes into the reporting platform").argument("<vuln>", "Vulnerability id").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--stage <stage>", "Reporting stage").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped)").option("--offline", "No network; requires --advisory").option("--out <file>", "Write the pack to this file").action(async (vuln, opts) => {
14749
+ const rootDir = root2(opts);
13852
14750
  const { result, regime } = await runExposure(rootDir, vuln, opts);
13853
14751
  const org = await loadOrg(rootDir);
13854
14752
  const stage = opts.stage ?? regime.clocks[0]?.stage ?? "early-warning";
13855
14753
  const md = buildPack(result, regime, org, stage);
13856
14754
  if (opts.out) {
13857
- fs14.writeFileSync(path43.resolve(rootDir, opts.out), md);
14755
+ fs14.writeFileSync(path46.resolve(rootDir, opts.out), md);
13858
14756
  console.error(chalk7.green("\u2714") + ` pack written to ${opts.out}`);
13859
14757
  } else {
13860
14758
  console.log(md);
13861
14759
  }
13862
14760
  process.exitCode = exposureExit(result.overallStatus);
13863
14761
  });
13864
- var drillCmd = new Command("drill").description("Timed dry-run of the determination step against a simulated advisory").option(...cwdOption).option("--regime <id>", "Reporting regime").option("--scenario <name>", "Scenario seed (deterministic pick)", "kev-recent").option("--elapsed <seconds>", "Record the wall-clock your team actually took").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
13865
- const rootDir = root(opts);
14762
+ var drillCmd = new Command("drill").description("Timed dry-run of the determination step against a simulated advisory").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--scenario <name>", "Scenario seed (deterministic pick)", "kev-recent").option("--elapsed <seconds>", "Record the wall-clock your team actually took").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
14763
+ const rootDir = root2(opts);
13866
14764
  const org = await loadOrg(rootDir);
13867
14765
  const regime = resolveRegime(opts.regime ?? org.defaultRegime);
13868
14766
  const products = await loadProducts(rootDir);
@@ -13896,18 +14794,18 @@ var drillCmd = new Command("drill").description("Timed dry-run of the determinat
13896
14794
  }
13897
14795
  });
13898
14796
  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) => {
13899
- const abs = path43.resolve(bundlePath);
13900
- const envPath = fs14.statSync(abs).isDirectory() ? path43.join(abs, "evidence.intoto.jsonl") : abs;
14797
+ const abs = path46.resolve(bundlePath);
14798
+ const envPath = fs14.statSync(abs).isDirectory() ? path46.join(abs, "evidence.intoto.jsonl") : abs;
13901
14799
  if (!fs14.existsSync(envPath)) throw new CliError(`no evidence.intoto.jsonl at ${bundlePath}`, ExitCode.NOT_FOUND);
13902
14800
  const envelope = JSON.parse(fs14.readFileSync(envPath, "utf8").trim().split("\n")[0]);
13903
- const resultPath = path43.join(path43.dirname(envPath), "result.json");
14801
+ const resultPath = path46.join(path46.dirname(envPath), "result.json");
13904
14802
  const result = fs14.existsSync(resultPath) ? JSON.parse(fs14.readFileSync(resultPath, "utf8")) : void 0;
13905
- const publicKeyPem = opts.pub ? fs14.readFileSync(path43.resolve(opts.pub), "utf8") : void 0;
14803
+ const publicKeyPem = opts.pub ? fs14.readFileSync(path46.resolve(opts.pub), "utf8") : void 0;
13906
14804
  const v = verifyEvidenceEnvelope(envelope, { publicKeyPem, result });
13907
14805
  const color = v.status === "verified" ? chalk7.green : v.status === "failed" ? chalk7.red : chalk7.yellow;
13908
14806
  console.log(" " + color(v.status.toUpperCase()) + ` ${v.reason}`);
13909
14807
  if (v.evidenceId) console.log(" " + chalk7.dim(`evidence ${v.evidenceId} \xB7 regime ${v.regime} \xB7 advisory ${v.advisoryId} \xB7 ${v.overallStatus}`));
13910
- const tsrPath = path43.join(path43.dirname(envPath), "timestamp.tsr");
14808
+ const tsrPath = path46.join(path46.dirname(envPath), "timestamp.tsr");
13911
14809
  if (fs14.existsSync(tsrPath) && result) {
13912
14810
  const t = verifyTimestamp(fs14.readFileSync(tsrPath), exposureSubjectDigest(result));
13913
14811
  const tcolor = t.imprintMatches ? chalk7.green : chalk7.red;
@@ -13915,8 +14813,8 @@ var verifyCmd = new Command("verify").description("Verify an evidence bundle off
13915
14813
  }
13916
14814
  process.exitCode = v.status === "verified" ? ExitCode.OK : v.status === "unverified" ? ExitCode.GATE_FAILED : ExitCode.ERROR;
13917
14815
  });
13918
- var watchCmd = new Command("watch").description('Check CISA KEV for new exposure against your shipped components (surfaces the listing; does not decide "actively exploited")').option(...cwdOption).option("--regime <id>", "Reporting regime").option("--since <date>", "Only KEV entries added on/after this date (YYYY-MM-DD)").option("--webhook <url>", "POST the alert payload to this URL when there are hits").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
13919
- const rootDir = root(opts);
14816
+ var watchCmd = new Command("watch").description('Check CISA KEV for new exposure against your shipped components (surfaces the listing; does not decide "actively exploited")').option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--since <date>", "Only KEV entries added on/after this date (YYYY-MM-DD)").option("--webhook <url>", "POST the alert payload to this URL when there are hits").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
14817
+ const rootDir = root2(opts);
13920
14818
  const org = await loadOrg(rootDir);
13921
14819
  const regime = resolveRegime(opts.regime ?? org.defaultRegime);
13922
14820
  const products = await loadProducts(rootDir);
@@ -13967,8 +14865,8 @@ var watchCmd = new Command("watch").description('Check CISA KEV for new exposure
13967
14865
  console.log(" " + chalk7.dim('watch surfaces the CISA KEV listing. Whether a vulnerability is "actively exploited" for a filing is your determination, not ours.'));
13968
14866
  process.exitCode = hits.some((h) => h.result.overallStatus === "affected") ? ExitCode.GATE_FAILED : hits.some((h) => h.result.overallStatus === "undetermined") ? ExitCode.NOT_FOUND : ExitCode.OK;
13969
14867
  });
13970
- var exportCmd = new Command("export").description("Air-gap bundle of all evidence state (org, products, frozen releases, readiness)").option(...cwdOption).option("--out <dir>", "Output directory", "evidence-export").option("--regime <id>", "Reporting regime for the readiness snapshot").action(async (opts) => {
13971
- const rootDir = root(opts);
14868
+ var exportCmd = new Command("export").description("Air-gap bundle of all evidence state (org, products, frozen releases, readiness)").option(...cwdOption2).option("--out <dir>", "Output directory", "evidence-export").option("--regime <id>", "Reporting regime for the readiness snapshot").action(async (opts) => {
14869
+ const rootDir = root2(opts);
13972
14870
  const org = await loadOrg(rootDir);
13973
14871
  const regime = resolveRegime(opts.regime ?? org.defaultRegime);
13974
14872
  const products = await loadProducts(rootDir);
@@ -13976,15 +14874,15 @@ var exportCmd = new Command("export").description("Air-gap bundle of all evidenc
13976
14874
  const releases = [];
13977
14875
  for (const list of releasesByProduct.values()) releases.push(...list);
13978
14876
  const report = computeReadiness({ regime, org, products, releasesByProduct, recentDrill: await hasRecentDrill(rootDir, today()) });
13979
- const dir = path43.resolve(rootDir, opts.out);
13980
- await writeJsonFile(path43.join(dir, "org.json"), org);
13981
- await writeJsonFile(path43.join(dir, "products.json"), products);
13982
- for (const r of releases) await writeJsonFile(path43.join(dir, "releases", `${r.productId}@${r.version}.json`.replace(/[^A-Za-z0-9._@-]/g, "_")), r);
13983
- await writeJsonFile(path43.join(dir, "readiness.json"), report);
13984
- console.error(chalk7.green("\u2714") + ` exported evidence state to ${path43.relative(rootDir, dir)} (${products.length} products, ${releases.length} frozen releases)`);
14877
+ const dir = path46.resolve(rootDir, opts.out);
14878
+ await writeJsonFile(path46.join(dir, "org.json"), org);
14879
+ await writeJsonFile(path46.join(dir, "products.json"), products);
14880
+ for (const r of releases) await writeJsonFile(path46.join(dir, "releases", `${r.productId}@${r.version}.json`.replace(/[^A-Za-z0-9._@-]/g, "_")), r);
14881
+ await writeJsonFile(path46.join(dir, "readiness.json"), report);
14882
+ console.error(chalk7.green("\u2714") + ` exported evidence state to ${path46.relative(rootDir, dir)} (${products.length} products, ${releases.length} frozen releases)`);
13985
14883
  });
13986
- 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) => {
13987
- const rootDir = root(opts);
14884
+ var pushCmd = new Command("push").description("Push the product registry (and an optional exposure result) to Vibgrate Cloud").option(...cwdOption2).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) => {
14885
+ const rootDir = root2(opts);
13988
14886
  const dsn = resolveDsn(opts.dsn);
13989
14887
  if (!dsn) throw new CliError("no DSN \u2014 run `vg login`, set VIBGRATE_DSN, or pass --dsn", ExitCode.USAGE_ERROR);
13990
14888
  const parsed = parseDsn$1(dsn);
@@ -14007,8 +14905,8 @@ var pushCmd = new Command("push").description("Push the product registry (and an
14007
14905
  }));
14008
14906
  let exposure;
14009
14907
  if (opts.result) {
14010
- const raw = path43.resolve(rootDir, opts.result);
14011
- const resultPath = fs14.existsSync(raw) && fs14.statSync(raw).isDirectory() ? path43.join(raw, "result.json") : raw;
14908
+ const raw = path46.resolve(rootDir, opts.result);
14909
+ const resultPath = fs14.existsSync(raw) && fs14.statSync(raw).isDirectory() ? path46.join(raw, "result.json") : raw;
14012
14910
  if (!fs14.existsSync(resultPath)) throw new CliError(`no result.json at ${opts.result}`, ExitCode.NOT_FOUND);
14013
14911
  exposure = JSON.parse(fs14.readFileSync(resultPath, "utf8"));
14014
14912
  }
@@ -14090,7 +14988,9 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
14090
14988
  "dsn",
14091
14989
  "update",
14092
14990
  "sbom",
14093
- "evidence"
14991
+ "evidence",
14992
+ "locale",
14993
+ "localize"
14094
14994
  ]);
14095
14995
  function buildProgram() {
14096
14996
  const program = new Command();
@@ -14150,6 +15050,7 @@ ${aliasNote}Docs: https://vibgrate.com/help`;
14150
15050
  program.addCommand(updateCommand);
14151
15051
  program.addCommand(sbomCommand);
14152
15052
  program.addCommand(evidenceCommand);
15053
+ registerLocale(program);
14153
15054
  return program;
14154
15055
  }
14155
15056
  var VALUE_FLAGS = /* @__PURE__ */ new Set([
@@ -14206,7 +15107,7 @@ function dispatch(argv, cwd) {
14206
15107
  }
14207
15108
  const looksLikeQuestion = /\s/.test(first) || first.endsWith("?");
14208
15109
  if (looksLikeQuestion) return ["ask", ...args];
14209
- if (fs14.existsSync(path43.resolve(cwd, first))) {
15110
+ if (fs14.existsSync(path46.resolve(cwd, first))) {
14210
15111
  return [hasBuildOnlyFlag(args) ? "build" : "scan", ...args];
14211
15112
  }
14212
15113
  const moved = MOVED_COMMANDS[first];
@@ -14251,9 +15152,9 @@ async function main(argv = process.argv) {
14251
15152
  }
14252
15153
  function readCwd(raw) {
14253
15154
  const i = raw.findIndex((a) => a === "-C" || a === "--cwd");
14254
- if (i >= 0 && raw[i + 1]) return path43.resolve(raw[i + 1]);
15155
+ if (i >= 0 && raw[i + 1]) return path46.resolve(raw[i + 1]);
14255
15156
  const eq = raw.find((a) => a.startsWith("--cwd="));
14256
- if (eq) return path43.resolve(eq.slice("--cwd=".length));
15157
+ if (eq) return path46.resolve(eq.slice("--cwd=".length));
14257
15158
  return process.cwd();
14258
15159
  }
14259
15160
  function handleError(err) {