@vibgrate/cli 2026.720.1 → 2026.720.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, detectAssistants, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, formatForExt, hostedBase, verifyDeterminism, renderHtml, isModelReady, SAVINGS_TOOLS, DEFAULT_RATE_LABEL, readStoredCredentials, projectCredentialsPath, DEFAULT_RATE_PER_M, homeCredentialsPath, SKIP_DIRS, SKIP_FILES } from './chunk-VQLOVQZP.js';
3
- import { resolvedGrammarFiles, grammarSetVersion, hashString, canonicalize, hashBytes } from './chunk-VFO5UDAT.js';
4
- import { loadAdvancedScanHook, baselineCommand } from './chunk-VYSTOEAK.js';
5
- import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-AJDUMRVI.js';
2
+ import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, savingsLedgerPath, serveStdio, ASSISTANTS, detectAssistants, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, refreshInstalledInstructions, SMALL_REPO_FILES, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, formatForExt, hostedBase, verifyDeterminism, renderHtml, isModelReady, SAVINGS_TOOLS, sanitizeClient, readStoredCredentials, projectCredentialsPath, homeCredentialsPath, SKIP_DIRS, SKIP_FILES } from './chunk-ZFDVHKID.js';
3
+ import { resolvedGrammarFiles, grammarSetVersion, hashString, canonicalize, hashBytes } from './chunk-BHA7QIPD.js';
4
+ import { loadAdvancedScanHook, baselineCommand } from './chunk-IUKCKMR6.js';
5
+ import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-B6ZWB3WH.js';
6
6
  import './chunk-RXP66R2E.js';
7
7
  import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-GI6W53LM.js';
8
- import * as fs8 from 'fs';
9
- import fs8__default, { realpathSync } from 'fs';
8
+ import * as fs10 from 'fs';
9
+ import fs10__default, { realpathSync } from 'fs';
10
10
  import * as path from 'path';
11
11
  import path__default from 'path';
12
12
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -16,7 +16,7 @@ import * as crypto from 'crypto';
16
16
  import { randomUUID } from 'crypto';
17
17
  import chalk8 from 'chalk';
18
18
  import * as os from 'os';
19
- import * as fs15 from 'fs/promises';
19
+ import * as fs17 from 'fs/promises';
20
20
  import * as readline from 'readline';
21
21
  import semver from 'semver';
22
22
 
@@ -178,14 +178,14 @@ async function signGraphAttestation(root, graph, opts) {
178
178
  const explicitKey = opts.key ?? process.env.VG_ATTEST_KEY;
179
179
  const keyPath = explicitKey ? path.resolve(explicitKey) : path.join(root, ".vibgrate", DEFAULT_KEY);
180
180
  let keyGeneratedAt;
181
- if (!fs8.existsSync(keyPath)) {
181
+ if (!fs10.existsSync(keyPath)) {
182
182
  if (explicitKey) {
183
183
  throw new CliError(`signing key not found: ${explicitKey}`, ExitCode.USAGE_ERROR);
184
184
  }
185
185
  const kp = generateKeypair();
186
- fs8.mkdirSync(path.dirname(keyPath), { recursive: true });
187
- fs8.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
188
- fs8.writeFileSync(`${keyPath}.pub`, kp.publicPem);
186
+ fs10.mkdirSync(path.dirname(keyPath), { recursive: true });
187
+ fs10.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
188
+ fs10.writeFileSync(`${keyPath}.pub`, kp.publicPem);
189
189
  keyGeneratedAt = keyPath;
190
190
  notices.push(
191
191
  `minted a new Ed25519 signing key at ${rel(root, keyPath)} (keyid ${kp.keyid}) \u2014 keep it, add it to .gitignore, and reuse it to re-sign reproducibly`
@@ -193,7 +193,7 @@ async function signGraphAttestation(root, graph, opts) {
193
193
  }
194
194
  let privateKey;
195
195
  try {
196
- privateKey = crypto.createPrivateKey(fs8.readFileSync(keyPath, "utf8"));
196
+ privateKey = crypto.createPrivateKey(fs10.readFileSync(keyPath, "utf8"));
197
197
  } catch {
198
198
  throw new CliError(`could not read an Ed25519 private key from ${rel(root, keyPath)}`, ExitCode.USAGE_ERROR);
199
199
  }
@@ -207,8 +207,8 @@ async function signGraphAttestation(root, graph, opts) {
207
207
  const statement = buildStatement(graph, { commit });
208
208
  const envelope = signStatement(statement, privateKey);
209
209
  const outPath = opts.attestation ? path.resolve(opts.attestation) : path.join(root, ".vibgrate", DEFAULT_ATTESTATION);
210
- fs8.mkdirSync(path.dirname(outPath), { recursive: true });
211
- fs8.writeFileSync(outPath, serializeEnvelope(envelope));
210
+ fs10.mkdirSync(path.dirname(outPath), { recursive: true });
211
+ fs10.writeFileSync(outPath, serializeEnvelope(envelope));
212
212
  return {
213
213
  summary: {
214
214
  keyid: envelope.signatures[0]?.keyid ?? "",
@@ -223,7 +223,7 @@ async function signGraphAttestation(root, graph, opts) {
223
223
  }
224
224
  function verifyGraphAttestation(root, opts) {
225
225
  const bundlePath = opts.attestation ? path.resolve(opts.attestation) : path.join(root, ".vibgrate", DEFAULT_ATTESTATION);
226
- if (!fs8.existsSync(bundlePath)) {
226
+ if (!fs10.existsSync(bundlePath)) {
227
227
  if (opts.attestation || opts.pub) {
228
228
  throw new CliError(
229
229
  `no attestation at ${rel(root, bundlePath)} \u2014 sign one with \`vg build --attest\``,
@@ -232,8 +232,8 @@ function verifyGraphAttestation(root, opts) {
232
232
  }
233
233
  return { bundlePath, missing: true };
234
234
  }
235
- const envelope = parseEnvelope(fs8.readFileSync(bundlePath, "utf8"));
236
- const publicKeyPem = opts.pub ? fs8.readFileSync(path.resolve(opts.pub), "utf8") : void 0;
235
+ const envelope = parseEnvelope(fs10.readFileSync(bundlePath, "utf8"));
236
+ const publicKeyPem = opts.pub ? fs10.readFileSync(path.resolve(opts.pub), "utf8") : void 0;
237
237
  const graph = loadGraph(root) ?? void 0;
238
238
  return { bundlePath, missing: false, result: verifyEnvelope(envelope, { publicKeyPem, graph }) };
239
239
  }
@@ -431,6 +431,15 @@ async function runBuild(paths, opts, global) {
431
431
  });
432
432
  }
433
433
  if (opts.export) writeExport(result.graph, opts.export);
434
+ if (!global.graph) {
435
+ const fileCount = result.graph.nodes.filter((n) => n.kind === "file").length;
436
+ const refreshed = refreshInstalledInstructions(root, fileCount > 0 && fileCount < SMALL_REPO_FILES);
437
+ if (interactive) {
438
+ for (const r of refreshed) {
439
+ info(c.dim(`vg \xB7 refreshed assistant instructions ${r.file} (v${r.from} \u2192 v${r.to})`));
440
+ }
441
+ }
442
+ }
434
443
  let attestation;
435
444
  const attestNotices = [];
436
445
  if (opts.attest) {
@@ -586,8 +595,8 @@ function writeExport(graph, target) {
586
595
  ExitCode.USAGE_ERROR
587
596
  );
588
597
  }
589
- fs8.mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
590
- fs8.writeFileSync(target, content);
598
+ fs10.mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
599
+ fs10.writeFileSync(target, content);
591
600
  }
592
601
  function collect(value, prev) {
593
602
  return [...prev, value];
@@ -606,7 +615,7 @@ async function runStatus(global) {
606
615
  const root = path.resolve(global.cwd ?? ".");
607
616
  const graphPath = global.graph ?? defaultGraphPath(root);
608
617
  const graph = loadGraph(root, graphPath);
609
- const hasCache = fs8.existsSync(path.join(cacheDir(root), "parse-cache.json"));
618
+ const hasCache = fs10.existsSync(path.join(cacheDir(root), "parse-cache.json"));
610
619
  let stale = null;
611
620
  let staleExact = false;
612
621
  if (graph) {
@@ -1315,14 +1324,14 @@ function isCI(env = process.env) {
1315
1324
  function installId(env = process.env) {
1316
1325
  if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();
1317
1326
  try {
1318
- const existing = fs8.readFileSync(INSTALL_ID_FILE, "utf8").trim();
1327
+ const existing = fs10.readFileSync(INSTALL_ID_FILE, "utf8").trim();
1319
1328
  if (isUuid(existing)) return existing;
1320
1329
  } catch {
1321
1330
  }
1322
1331
  const id = randomUuid();
1323
1332
  try {
1324
- fs8.mkdirSync(INSTALL_ID_DIR, { recursive: true });
1325
- fs8.writeFileSync(INSTALL_ID_FILE, id + "\n", { mode: 384 });
1333
+ fs10.mkdirSync(INSTALL_ID_DIR, { recursive: true });
1334
+ fs10.writeFileSync(INSTALL_ID_FILE, id + "\n", { mode: 384 });
1326
1335
  } catch {
1327
1336
  }
1328
1337
  return id;
@@ -1346,7 +1355,7 @@ function ledgerFilePath(root) {
1346
1355
  }
1347
1356
  function readOffset(root) {
1348
1357
  try {
1349
- const raw = JSON.parse(fs8.readFileSync(offsetPath(root), "utf8"));
1358
+ const raw = JSON.parse(fs10.readFileSync(offsetPath(root), "utf8"));
1350
1359
  return typeof raw.offset === "number" && raw.offset >= 0 ? raw.offset : 0;
1351
1360
  } catch {
1352
1361
  return 0;
@@ -1354,8 +1363,8 @@ function readOffset(root) {
1354
1363
  }
1355
1364
  function writeOffset(root, offset) {
1356
1365
  try {
1357
- fs8.mkdirSync(cacheDir(root), { recursive: true });
1358
- fs8.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + "\n");
1366
+ fs10.mkdirSync(cacheDir(root), { recursive: true });
1367
+ fs10.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + "\n");
1359
1368
  } catch {
1360
1369
  }
1361
1370
  }
@@ -1363,7 +1372,7 @@ function buildBatch(root, fromOffset, id = installId()) {
1363
1372
  const file = ledgerFilePath(root);
1364
1373
  let size = 0;
1365
1374
  try {
1366
- size = fs8.statSync(file).size;
1375
+ size = fs10.statSync(file).size;
1367
1376
  } catch {
1368
1377
  return { batch: null, newOffset: 0 };
1369
1378
  }
@@ -1371,13 +1380,13 @@ function buildBatch(root, fromOffset, id = installId()) {
1371
1380
  if (start >= size) return { batch: null, newOffset: size };
1372
1381
  let text = "";
1373
1382
  try {
1374
- const fd = fs8.openSync(file, "r");
1383
+ const fd = fs10.openSync(file, "r");
1375
1384
  try {
1376
1385
  const buf = Buffer.alloc(size - start);
1377
- fs8.readSync(fd, buf, 0, buf.length, start);
1386
+ fs10.readSync(fd, buf, 0, buf.length, start);
1378
1387
  text = buf.toString("utf8");
1379
1388
  } finally {
1380
- fs8.closeSync(fd);
1389
+ fs10.closeSync(fd);
1381
1390
  }
1382
1391
  } catch {
1383
1392
  return { batch: null, newOffset: fromOffset };
@@ -1509,10 +1518,143 @@ function isLoopbackOrigin(origin) {
1509
1518
  if (host === "localhost" || host === "[::1]" || host === "::1") return true;
1510
1519
  return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
1511
1520
  }
1521
+ var PUBLISH_INTERVAL_MS = 2e3;
1522
+ var KEEPALIVE_MS = 1e4;
1523
+ var STALE_MS = 6e4;
1524
+ function liveStatsDir(root) {
1525
+ return path.join(cacheDir(root), "serve-live");
1526
+ }
1527
+ var LiveStatsBus = class {
1528
+ constructor(dir, stats, pid = process.pid) {
1529
+ this.dir = dir;
1530
+ this.stats = stats;
1531
+ this.pid = pid;
1532
+ this.file = path.join(dir, `serve-${this.pid}.json`);
1533
+ }
1534
+ dir;
1535
+ stats;
1536
+ pid;
1537
+ timer = null;
1538
+ lastRevision = -1;
1539
+ lastWriteAt = 0;
1540
+ file;
1541
+ start() {
1542
+ if (this.timer) return;
1543
+ this.publish();
1544
+ this.timer = setInterval(() => this.publish(), PUBLISH_INTERVAL_MS);
1545
+ this.timer.unref?.();
1546
+ process.on("exit", this.unlinkOwn);
1547
+ }
1548
+ stop() {
1549
+ if (this.timer) {
1550
+ clearInterval(this.timer);
1551
+ this.timer = null;
1552
+ }
1553
+ process.removeListener("exit", this.unlinkOwn);
1554
+ this.unlinkOwn();
1555
+ }
1556
+ /** Write the current snapshot when it changed (or as a keepalive). Never throws. */
1557
+ publish(now2 = Date.now()) {
1558
+ const snap = this.stats.snapshot();
1559
+ if (snap.revision === this.lastRevision && now2 - this.lastWriteAt < KEEPALIVE_MS) return;
1560
+ this.lastRevision = snap.revision;
1561
+ this.lastWriteAt = now2;
1562
+ const body = { pid: this.pid, updatedAt: now2, snapshot: snap };
1563
+ try {
1564
+ fs10.mkdirSync(this.dir, { recursive: true });
1565
+ const tmp = `${this.file}.tmp`;
1566
+ fs10.writeFileSync(tmp, JSON.stringify(body));
1567
+ fs10.renameSync(tmp, this.file);
1568
+ } catch {
1569
+ }
1570
+ }
1571
+ /**
1572
+ * Fresh snapshots of the OTHER serve processes in this repo. Stale files
1573
+ * (crashed processes) are swept best-effort while reading.
1574
+ */
1575
+ siblings(now2 = Date.now()) {
1576
+ let entries;
1577
+ try {
1578
+ entries = fs10.readdirSync(this.dir);
1579
+ } catch {
1580
+ return [];
1581
+ }
1582
+ const out2 = [];
1583
+ for (const name of entries.sort()) {
1584
+ if (!/^serve-\d+\.json$/.test(name)) continue;
1585
+ const file = path.join(this.dir, name);
1586
+ let parsed;
1587
+ try {
1588
+ parsed = JSON.parse(fs10.readFileSync(file, "utf8"));
1589
+ } catch {
1590
+ continue;
1591
+ }
1592
+ if (typeof parsed?.pid !== "number" || typeof parsed.updatedAt !== "number" || !parsed.snapshot?.totals) continue;
1593
+ if (now2 - parsed.updatedAt > STALE_MS) {
1594
+ try {
1595
+ fs10.unlinkSync(file);
1596
+ } catch {
1597
+ }
1598
+ continue;
1599
+ }
1600
+ if (parsed.pid === this.pid) continue;
1601
+ out2.push(parsed.snapshot);
1602
+ }
1603
+ return out2;
1604
+ }
1605
+ unlinkOwn = () => {
1606
+ try {
1607
+ fs10.unlinkSync(this.file);
1608
+ } catch {
1609
+ }
1610
+ };
1611
+ };
1612
+ function addInto(target, add) {
1613
+ target.calls += add.calls;
1614
+ target.complete += add.complete;
1615
+ target.partial += add.partial;
1616
+ target.miss += add.miss;
1617
+ target.timed += add.timed;
1618
+ target.totalMs += add.totalMs;
1619
+ target.vgTokens += add.vgTokens;
1620
+ target.baselineTokens += add.baselineTokens;
1621
+ }
1622
+ function mergeRows(base, extra) {
1623
+ const byKey = new Map(base.map((r) => [r.key, { ...r }]));
1624
+ for (const row of extra) {
1625
+ const hit = byKey.get(row.key);
1626
+ if (hit) addInto(hit, row);
1627
+ else byKey.set(row.key, { ...row });
1628
+ }
1629
+ return [...byKey.values()].sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
1630
+ }
1631
+ function mergeSnapshots(own, siblings) {
1632
+ if (!siblings.length) return own;
1633
+ const merged = {
1634
+ startedAt: own.startedAt,
1635
+ revision: own.revision,
1636
+ lastCallAt: own.lastCallAt,
1637
+ totals: { ...own.totals },
1638
+ clients: [...own.clients.map((r) => ({ ...r }))],
1639
+ tools: [...own.tools.map((r) => ({ ...r }))],
1640
+ sources: [...own.sources.map((r) => ({ ...r }))]
1641
+ };
1642
+ for (const s of siblings) {
1643
+ merged.revision += s.revision;
1644
+ if (s.lastCallAt !== null && (merged.lastCallAt === null || s.lastCallAt > merged.lastCallAt)) {
1645
+ merged.lastCallAt = s.lastCallAt;
1646
+ }
1647
+ addInto(merged.totals, s.totals);
1648
+ merged.clients = mergeRows(merged.clients, s.clients);
1649
+ merged.tools = mergeRows(merged.tools, s.tools);
1650
+ merged.sources = mergeRows(merged.sources, s.sources);
1651
+ }
1652
+ return merged;
1653
+ }
1512
1654
 
1513
1655
  // src/mcp/serve-stats.ts
1514
1656
  function zeroRow(key2) {
1515
- return { key: key2, calls: 0, complete: 0, partial: 0, miss: 0, totalMs: 0, vgTokens: 0, baselineTokens: 0 };
1657
+ return { key: key2, calls: 0, complete: 0, partial: 0, miss: 0, timed: 0, totalMs: 0, vgTokens: 0, baselineTokens: 0 };
1516
1658
  }
1517
1659
  var SessionStats = class {
1518
1660
  startedAt;
@@ -1521,16 +1663,26 @@ var SessionStats = class {
1521
1663
  totals = zeroRow("total");
1522
1664
  byClient = /* @__PURE__ */ new Map();
1523
1665
  byTool = /* @__PURE__ */ new Map();
1666
+ bySource = /* @__PURE__ */ new Map();
1524
1667
  constructor(now2 = Date.now()) {
1525
1668
  this.startedAt = now2;
1526
1669
  }
1527
1670
  record(sample, now2 = Date.now()) {
1528
1671
  this.revision++;
1529
1672
  this.lastCallAt = now2;
1530
- for (const row of [this.totals, this.rowFor(this.byClient, sample.client), this.rowFor(this.byTool, sample.tool)]) {
1673
+ const rows = [
1674
+ this.totals,
1675
+ this.rowFor(this.byClient, sample.client),
1676
+ this.rowFor(this.byTool, sample.tool),
1677
+ this.rowFor(this.bySource, sample.source ?? "mcp")
1678
+ ];
1679
+ for (const row of rows) {
1531
1680
  row.calls++;
1532
1681
  row[sample.outcome]++;
1533
- row.totalMs += sample.ms;
1682
+ if (typeof sample.ms === "number") {
1683
+ row.timed++;
1684
+ row.totalMs += sample.ms;
1685
+ }
1534
1686
  row.vgTokens += sample.vgTokens;
1535
1687
  row.baselineTokens += sample.baselineTokens;
1536
1688
  }
@@ -1543,7 +1695,8 @@ var SessionStats = class {
1543
1695
  lastCallAt: this.lastCallAt,
1544
1696
  totals: { ...this.totals },
1545
1697
  clients: sorted(this.byClient),
1546
- tools: sorted(this.byTool)
1698
+ tools: sorted(this.byTool),
1699
+ sources: sorted(this.bySource)
1547
1700
  };
1548
1701
  }
1549
1702
  rowFor(m, key2) {
@@ -1571,29 +1724,33 @@ function fmtTokens(n) {
1571
1724
  return String(n);
1572
1725
  }
1573
1726
  function fmtAvgMs(row) {
1574
- if (row.calls === 0) return "\u2014";
1575
- const avg = row.totalMs / row.calls;
1727
+ if (row.timed === 0) return "\u2014";
1728
+ const avg = row.totalMs / row.timed;
1576
1729
  return avg >= 1e3 ? `${(avg / 1e3).toFixed(1)}s` : `${Math.round(avg)}ms`;
1577
1730
  }
1578
1731
  var teal2 = (s) => c.hex("#3FB0A4")(s);
1579
1732
  var mint2 = (s) => c.hex("#4FE3C1")(s);
1580
1733
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1581
1734
  var MAX_TOOL_ROWS = 8;
1582
- function serveStatusLines(snap, now2, tick = 0) {
1735
+ function serveStatusLines(snap, now2, tick = 0, siblingServers = 0) {
1583
1736
  const spinner = mint2(SPINNER[tick % SPINNER.length]);
1584
1737
  const up = fmtUptime(now2 - snap.startedAt);
1585
1738
  const lines = [];
1739
+ const siblingNote = siblingServers > 0 ? ` ${c.dim(`incl. ${siblingServers} assistant-spawned server${siblingServers === 1 ? "" : "s"} in this repo`)}` : null;
1586
1740
  if (snap.totals.calls === 0) {
1587
1741
  lines.push(
1588
- ` ${spinner} ${c.bold.white("vg serve")} ${c.dim("\xB7")} up ${teal2(up)} ${c.dim("\xB7 waiting for your assistant\u2019s first tool call\u2026")}`
1742
+ ` ${spinner} ${c.bold.white("vg serve")} ${c.dim("\xB7")} up ${teal2(up)} ${c.dim("\xB7 waiting for your assistant\u2019s first call (MCP tool call, or `vg \u2026 --client=<ai>` CLI)\u2026")}`
1589
1743
  );
1744
+ if (siblingNote) lines.push(siblingNote);
1590
1745
  return lines;
1591
1746
  }
1592
1747
  const t = snap.totals;
1593
1748
  const answered = Math.round((t.complete + t.partial) / t.calls * 100);
1749
+ const cli = snap.sources.find((s) => s.key === "cli");
1750
+ const via = cli ? c.dim(" \xB7 via ") + snap.sources.map((s) => `${c.white(s.key)} ${teal2(String(s.calls))}`).join(c.dim(" \xB7 ")) : "";
1594
1751
  const ago = snap.lastCallAt === null ? "" : c.dim(` \xB7 last call ${fmtUptime(now2 - snap.lastCallAt)} ago`);
1595
1752
  lines.push(
1596
- ` ${spinner} ${c.bold.white("vg serve")} ${c.dim("\xB7")} up ${teal2(up)} ${c.dim("\xB7")} ${c.bold.white(String(t.calls))} call${t.calls === 1 ? "" : "s"} ${c.dim("\xB7")} ${mint2(`${answered}%`)} answered${ago}`
1753
+ ` ${spinner} ${c.bold.white("vg serve")} ${c.dim("\xB7")} up ${teal2(up)} ${c.dim("\xB7")} ${c.bold.white(String(t.calls))} call${t.calls === 1 ? "" : "s"} ${c.dim("\xB7")} ${mint2(`${answered}%`)} answered${via}${ago}`
1597
1754
  );
1598
1755
  lines.push(
1599
1756
  ` ${c.dim("clients")} ` + snap.clients.map((cl) => `${c.white(cl.key)} ${teal2(String(cl.calls))}${c.dim(` (avg ${fmtAvgMs(cl)})`)}`).join(c.dim(" \xB7 "))
@@ -1613,15 +1770,15 @@ function serveStatusLines(snap, now2, tick = 0) {
1613
1770
  }
1614
1771
  if (t.baselineTokens > 0) {
1615
1772
  const savedTokens = Math.max(0, t.baselineTokens - t.vgTokens);
1616
- const savedUsd = savedTokens / 1e6 * DEFAULT_RATE_PER_M;
1617
1773
  const ratio = t.vgTokens > 0 ? (t.baselineTokens / t.vgTokens).toFixed(1) : "\u2014";
1618
1774
  lines.push(
1619
1775
  ` ${c.dim("session")} ctx ${c.bold.white(fmtTokens(t.vgTokens))} ` + c.dim(`vs grep/read \u2248${fmtTokens(t.baselineTokens)} \u2192 `) + mint2(`${ratio}\xD7 fewer`)
1620
1776
  );
1621
1777
  lines.push(
1622
- ` ${c.dim("est. saved \u2248")} ${mint2(`${fmtTokens(savedTokens)} tokens ($${savedUsd.toFixed(2)})`)} ` + c.dim(`(${DEFAULT_RATE_LABEL}; estimate)`)
1778
+ ` ${c.dim("est. saved \u2248")} ${mint2(`${fmtTokens(savedTokens)} context tokens`)} ${c.dim("(estimate)")}`
1623
1779
  );
1624
1780
  }
1781
+ if (siblingNote) lines.push(siblingNote);
1625
1782
  return lines;
1626
1783
  }
1627
1784
  function serveHeartbeatLine(snap, now2) {
@@ -1629,7 +1786,9 @@ function serveHeartbeatLine(snap, now2) {
1629
1786
  const t = snap.totals;
1630
1787
  if (t.calls === 0) return c.dim(`vg \xB7 serving for ${up} \u2014 no tool calls yet`);
1631
1788
  const clients = snap.clients.map((cl) => `${cl.key} ${cl.calls}`).join(", ");
1789
+ const cli = snap.sources.find((s) => s.key === "cli");
1632
1790
  let line = `vg \xB7 serving for ${up} \u2014 ${t.calls} call${t.calls === 1 ? "" : "s"} (${clients})`;
1791
+ if (cli) line += ` \xB7 ${cli.calls} via CLI`;
1633
1792
  if (t.baselineTokens > 0) {
1634
1793
  const savedTokens = Math.max(0, t.baselineTokens - t.vgTokens);
1635
1794
  line += ` \xB7 ctx ${fmtTokens(t.vgTokens)} vs \u2248${fmtTokens(t.baselineTokens)} grep/read \xB7 est. saved \u2248 ${fmtTokens(savedTokens)} tokens`;
@@ -1639,13 +1798,15 @@ function serveHeartbeatLine(snap, now2) {
1639
1798
  var TTY_INTERVAL_MS = 1e3;
1640
1799
  var HEARTBEAT_INTERVAL_MS = 15 * 60 * 1e3;
1641
1800
  var ServeStatusDisplay = class {
1642
- constructor(stats, stream = process.stderr) {
1801
+ constructor(stats, stream = process.stderr, siblings) {
1643
1802
  this.stats = stats;
1644
1803
  this.stream = stream;
1804
+ this.siblings = siblings;
1645
1805
  this.isTTY = (stream.isTTY ?? false) && process.env.VIBGRATE_PROGRESS_MODE !== "plain";
1646
1806
  }
1647
1807
  stats;
1648
1808
  stream;
1809
+ siblings;
1649
1810
  timer = null;
1650
1811
  tick = 0;
1651
1812
  lastRowCount = 0;
@@ -1688,7 +1849,9 @@ var ServeStatusDisplay = class {
1688
1849
  }
1689
1850
  renderTTY() {
1690
1851
  this.tick++;
1691
- const lines = serveStatusLines(this.stats.snapshot(), Date.now(), this.tick);
1852
+ const sibs = this.siblings?.() ?? [];
1853
+ const snap = mergeSnapshots(this.stats.snapshot(), sibs);
1854
+ const lines = serveStatusLines(snap, Date.now(), this.tick, sibs.length);
1692
1855
  const content = lines.join("\n") + "\n";
1693
1856
  if (content === this.lastFrame) return;
1694
1857
  this.lastFrame = content;
@@ -1723,6 +1886,89 @@ var ServeStatusDisplay = class {
1723
1886
  this.cursorHidden = false;
1724
1887
  };
1725
1888
  };
1889
+ var POLL_INTERVAL_MS = 2e3;
1890
+ var LedgerTail = class {
1891
+ constructor(file, stats, intervalMs = POLL_INTERVAL_MS) {
1892
+ this.file = file;
1893
+ this.stats = stats;
1894
+ this.intervalMs = intervalMs;
1895
+ }
1896
+ file;
1897
+ stats;
1898
+ intervalMs;
1899
+ timer = null;
1900
+ /** Byte offset of the next unread ledger byte (always at a line boundary). */
1901
+ offset = 0;
1902
+ start() {
1903
+ if (this.timer) return;
1904
+ this.offset = this.sizeOf();
1905
+ this.timer = setInterval(() => this.poll(), this.intervalMs);
1906
+ this.timer.unref?.();
1907
+ }
1908
+ stop() {
1909
+ if (this.timer) {
1910
+ clearInterval(this.timer);
1911
+ this.timer = null;
1912
+ }
1913
+ }
1914
+ /** One poll cycle — exposed for tests (deterministic, no timers needed). */
1915
+ poll(now2 = Date.now()) {
1916
+ let size;
1917
+ try {
1918
+ size = this.sizeOf();
1919
+ } catch {
1920
+ return;
1921
+ }
1922
+ if (size < this.offset) this.offset = 0;
1923
+ if (size === this.offset) return;
1924
+ let chunk;
1925
+ try {
1926
+ const fd = fs10.openSync(this.file, "r");
1927
+ try {
1928
+ chunk = Buffer.alloc(size - this.offset);
1929
+ fs10.readSync(fd, chunk, 0, chunk.length, this.offset);
1930
+ } finally {
1931
+ fs10.closeSync(fd);
1932
+ }
1933
+ } catch {
1934
+ return;
1935
+ }
1936
+ const lastNewline = chunk.lastIndexOf(10);
1937
+ if (lastNewline < 0) return;
1938
+ const text = chunk.subarray(0, lastNewline).toString("utf8");
1939
+ this.offset += lastNewline + 1;
1940
+ for (const line of text.split("\n")) ingestLedgerLine(this.stats, line, now2);
1941
+ }
1942
+ sizeOf() {
1943
+ try {
1944
+ return fs10.statSync(this.file).size;
1945
+ } catch {
1946
+ return 0;
1947
+ }
1948
+ }
1949
+ };
1950
+ function ingestLedgerLine(stats, line, now2) {
1951
+ if (!line.trim()) return;
1952
+ let e;
1953
+ try {
1954
+ e = JSON.parse(line);
1955
+ } catch {
1956
+ return;
1957
+ }
1958
+ if (e.source !== "cli" || typeof e.tool !== "string") return;
1959
+ stats.record(
1960
+ {
1961
+ tool: e.tool,
1962
+ client: sanitizeClient(e.client),
1963
+ outcome: e.outcome ?? "complete",
1964
+ source: "cli",
1965
+ // CLI lines carry no wall time — leave ms absent ("not measured").
1966
+ vgTokens: e.vgTokens ?? 0,
1967
+ baselineTokens: e.baselineTokens ?? 0
1968
+ },
1969
+ now2
1970
+ );
1971
+ }
1726
1972
 
1727
1973
  // src/commands/serve.ts
1728
1974
  var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
@@ -1759,7 +2005,11 @@ function registerServe(program) {
1759
2005
  info(c.dim(`vg \xB7 --share-stats disabled by ${optOut}: recording locally, not uploading.`));
1760
2006
  }
1761
2007
  if (shareStats) startSharing(root);
1762
- const display = stats ? new ServeStatusDisplay(stats) : void 0;
2008
+ const interactive = process.stderr.isTTY === true;
2009
+ const bus = stats ? new LiveStatsBus(liveStatsDir(root), stats) : void 0;
2010
+ if (bus && !interactive) bus.start();
2011
+ const display = stats ? new ServeStatusDisplay(stats, process.stderr, bus && interactive ? () => bus.siblings() : void 0) : void 0;
2012
+ if (stats && interactive) new LedgerTail(savingsLedgerPath(root), stats).start();
1763
2013
  const freshness = refresh ? "auto-refresh" : "as built";
1764
2014
  if (opts.http) {
1765
2015
  await serveHttp(graphPath, opts.host ?? "127.0.0.1", Number(opts.port) || 7437, serveOpts, freshness, () => display?.start());
@@ -1773,7 +2023,7 @@ function registerServe(program) {
1773
2023
  }
1774
2024
  async function ensureServableGraph(root, graphPath, global, refresh, opts = {}) {
1775
2025
  if (refresh) {
1776
- if (!fs8.existsSync(graphPath)) {
2026
+ if (!fs10.existsSync(graphPath)) {
1777
2027
  info(c.dim("vg \xB7 no map found \u2014 building it before serving\u2026"));
1778
2028
  await runBuild(
1779
2029
  [],
@@ -1790,7 +2040,7 @@ async function ensureServableGraph(root, graphPath, global, refresh, opts = {})
1790
2040
  }
1791
2041
  }
1792
2042
  }
1793
- if (!fs8.existsSync(graphPath)) {
2043
+ if (!fs10.existsSync(graphPath)) {
1794
2044
  throw new CliError(
1795
2045
  `no map found at ${graphPath} \u2014 run \`vg\` to build one first`,
1796
2046
  ExitCode.NOT_FOUND
@@ -2073,7 +2323,7 @@ function manifestHash(root) {
2073
2323
  const walk2 = (dir) => {
2074
2324
  let entries;
2075
2325
  try {
2076
- entries = fs8.readdirSync(dir, { withFileTypes: true });
2326
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
2077
2327
  } catch {
2078
2328
  return;
2079
2329
  }
@@ -2089,7 +2339,7 @@ function manifestHash(root) {
2089
2339
  if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
2090
2340
  try {
2091
2341
  const rel3 = path.relative(root, abs).split(path.sep).join("/");
2092
- files.push({ rel: rel3, hash: hashBytes(fs8.readFileSync(abs)) });
2342
+ files.push({ rel: rel3, hash: hashBytes(fs10.readFileSync(abs)) });
2093
2343
  } catch {
2094
2344
  }
2095
2345
  }
@@ -2100,7 +2350,7 @@ function manifestHash(root) {
2100
2350
  }
2101
2351
  function loadScanCache(root, key2) {
2102
2352
  try {
2103
- const raw = JSON.parse(fs8.readFileSync(cachePath(root), "utf8"));
2353
+ const raw = JSON.parse(fs10.readFileSync(cachePath(root), "utf8"));
2104
2354
  if (raw.version === CACHE_VERSION && raw.manifestHash === key2.manifestHash && raw.toolVersion === key2.toolVersion && raw.offline === key2.offline && raw.artifact) {
2105
2355
  return { manifestHash: raw.manifestHash, artifact: raw.artifact };
2106
2356
  }
@@ -2110,8 +2360,8 @@ function loadScanCache(root, key2) {
2110
2360
  }
2111
2361
  function writeScanCache(root, key2, artifact) {
2112
2362
  try {
2113
- fs8.mkdirSync(cacheDir(root), { recursive: true });
2114
- fs8.writeFileSync(
2363
+ fs10.mkdirSync(cacheDir(root), { recursive: true });
2364
+ fs10.writeFileSync(
2115
2365
  cachePath(root),
2116
2366
  stableStringify(
2117
2367
  { version: CACHE_VERSION, manifestHash: key2.manifestHash, toolVersion: key2.toolVersion, offline: key2.offline, artifact },
@@ -2935,7 +3185,7 @@ function hoverMarkdown(dep, contribution) {
2935
3185
  return lines.join("\n");
2936
3186
  }
2937
3187
  function startLanguageServer(opts) {
2938
- if (!fs8.existsSync(opts.root)) {
3188
+ if (!fs10.existsSync(opts.root)) {
2939
3189
  process.stderr.write(`vg lsp: root does not exist: ${opts.root}
2940
3190
  `);
2941
3191
  process.exit(1);
@@ -2979,7 +3229,6 @@ function registerLsp(program) {
2979
3229
  }
2980
3230
 
2981
3231
  // src/commands/install.ts
2982
- var SMALL_REPO_FILES = 150;
2983
3232
  function registerInstall(program) {
2984
3233
  const install = program.command("install").description("add vg to your AI assistant(s): skill + MCP + advisory nudge").argument("[tools...]", `assistant ids: ${ASSISTANTS.map((a) => a.id).join(", ")}`).option("--all", "install for every supported assistant").option("--detect", "detect assistants in use (repo footprint, home config, PATH) and install for those; with --list, only report what was detected").option("--list", "show the support matrix and exit").option("--no-hook", "skip the advisory nudge").action(function(tools, opts) {
2985
3234
  const global = readGlobal(this);
@@ -3074,7 +3323,7 @@ function registerShare(program) {
3074
3323
  const global = readGlobal(this);
3075
3324
  const root = rootOf(global);
3076
3325
  const gitDir = path.join(root, ".git");
3077
- if (!fs8.existsSync(gitDir)) {
3326
+ if (!fs10.existsSync(gitDir)) {
3078
3327
  throw new CliError("not a git repository \u2014 run `vg share` inside a git repo", ExitCode.USAGE_ERROR);
3079
3328
  }
3080
3329
  if (opts.undo) {
@@ -3096,22 +3345,22 @@ function registerShare(program) {
3096
3345
  }
3097
3346
  function writeVibgrateGitignore(root, reports) {
3098
3347
  const dir = path.join(root, ".vibgrate");
3099
- fs8.mkdirSync(dir, { recursive: true });
3348
+ fs10.mkdirSync(dir, { recursive: true });
3100
3349
  const lines = ["# Managed by `vg share` \u2014 keep graph.json committed, ignore the rest", "cache/", "facts.jsonl"];
3101
3350
  if (!reports) lines.push("graph.html", "GRAPH_REPORT.md");
3102
- fs8.writeFileSync(path.join(dir, ".gitignore"), `${lines.join("\n")}
3351
+ fs10.writeFileSync(path.join(dir, ".gitignore"), `${lines.join("\n")}
3103
3352
  `);
3104
3353
  }
3105
3354
  function writeGitAttributes(root) {
3106
3355
  const file = path.join(root, ".gitattributes");
3107
3356
  const line = ".vibgrate/graph.json merge=vg";
3108
- let content = fs8.existsSync(file) ? fs8.readFileSync(file, "utf8") : "";
3357
+ let content = fs10.existsSync(file) ? fs10.readFileSync(file, "utf8") : "";
3109
3358
  if (!content.split("\n").some((l) => l.trim() === line)) {
3110
3359
  content = content.length ? `${content.replace(/\s*$/, "")}
3111
3360
  ${line}
3112
3361
  ` : `${line}
3113
3362
  `;
3114
- fs8.writeFileSync(file, content);
3363
+ fs10.writeFileSync(file, content);
3115
3364
  }
3116
3365
  }
3117
3366
  function configureMergeDriver(root) {
@@ -3120,7 +3369,7 @@ function configureMergeDriver(root) {
3120
3369
  }
3121
3370
  function installPreCommitHook(root) {
3122
3371
  const hooksDir = hookDir(root);
3123
- fs8.mkdirSync(hooksDir, { recursive: true });
3372
+ fs10.mkdirSync(hooksDir, { recursive: true });
3124
3373
  const file = path.join(hooksDir, "pre-commit");
3125
3374
  const block = [
3126
3375
  HOOK_BEGIN,
@@ -3130,7 +3379,7 @@ function installPreCommitHook(root) {
3130
3379
  "fi",
3131
3380
  HOOK_END
3132
3381
  ].join("\n");
3133
- let content = fs8.existsSync(file) ? fs8.readFileSync(file, "utf8") : "#!/bin/sh\n";
3382
+ let content = fs10.existsSync(file) ? fs10.readFileSync(file, "utf8") : "#!/bin/sh\n";
3134
3383
  const re = new RegExp(`${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}`);
3135
3384
  if (re.test(content)) content = content.replace(re, block);
3136
3385
  else content = `${content.replace(/\s*$/, "")}
@@ -3139,21 +3388,21 @@ ${block}
3139
3388
  `;
3140
3389
  if (!content.startsWith("#!")) content = `#!/bin/sh
3141
3390
  ${content}`;
3142
- fs8.writeFileSync(file, content);
3143
- fs8.chmodSync(file, 493);
3391
+ fs10.writeFileSync(file, content);
3392
+ fs10.chmodSync(file, 493);
3144
3393
  }
3145
3394
  function undo(root) {
3146
3395
  const hookFile = path.join(hookDir(root), "pre-commit");
3147
- if (fs8.existsSync(hookFile)) {
3396
+ if (fs10.existsSync(hookFile)) {
3148
3397
  const re = new RegExp(`\\n*${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}\\n*`);
3149
- const next2 = fs8.readFileSync(hookFile, "utf8").replace(re, "\n");
3150
- if (next2.trim() === "#!/bin/sh") fs8.rmSync(hookFile);
3151
- else fs8.writeFileSync(hookFile, next2);
3398
+ const next2 = fs10.readFileSync(hookFile, "utf8").replace(re, "\n");
3399
+ if (next2.trim() === "#!/bin/sh") fs10.rmSync(hookFile);
3400
+ else fs10.writeFileSync(hookFile, next2);
3152
3401
  }
3153
3402
  const attr = path.join(root, ".gitattributes");
3154
- if (fs8.existsSync(attr)) {
3155
- const next2 = fs8.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
3156
- fs8.writeFileSync(attr, next2);
3403
+ if (fs10.existsSync(attr)) {
3404
+ const next2 = fs10.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
3405
+ fs10.writeFileSync(attr, next2);
3157
3406
  }
3158
3407
  try {
3159
3408
  git(root, ["config", "--remove-section", "merge.vg"]);
@@ -3286,7 +3535,7 @@ function grepBaselineTokens(term, files) {
3286
3535
  if (f.size > 512 * 1024) continue;
3287
3536
  let content = "";
3288
3537
  try {
3289
- content = fs8.readFileSync(f.abs, "utf8");
3538
+ content = fs10.readFileSync(f.abs, "utf8");
3290
3539
  } catch {
3291
3540
  continue;
3292
3541
  }
@@ -3296,7 +3545,7 @@ function grepBaselineTokens(term, files) {
3296
3545
  }
3297
3546
  function safeSize(abs) {
3298
3547
  try {
3299
- return fs8.statSync(abs).size;
3548
+ return fs10.statSync(abs).size;
3300
3549
  } catch {
3301
3550
  return 0;
3302
3551
  }
@@ -3630,7 +3879,7 @@ function loadStandards(root) {
3630
3879
  const p = path__default.join(root, rel3);
3631
3880
  let text;
3632
3881
  try {
3633
- text = fs8__default.readFileSync(p, "utf8");
3882
+ text = fs10__default.readFileSync(p, "utf8");
3634
3883
  } catch {
3635
3884
  continue;
3636
3885
  }
@@ -3869,7 +4118,7 @@ async function addCmd(root, rest, opts, asJson) {
3869
4118
  }
3870
4119
  function readPackageVersion(root) {
3871
4120
  try {
3872
- const pkg = JSON.parse(fs8__default.readFileSync(path__default.join(root, "package.json"), "utf8"));
4121
+ const pkg = JSON.parse(fs10__default.readFileSync(path__default.join(root, "package.json"), "utf8"));
3873
4122
  return typeof pkg.version === "string" ? pkg.version : void 0;
3874
4123
  } catch {
3875
4124
  return void 0;
@@ -3877,7 +4126,7 @@ function readPackageVersion(root) {
3877
4126
  }
3878
4127
  function readOptionalFile(p) {
3879
4128
  try {
3880
- const t = fs8__default.readFileSync(p, "utf8");
4129
+ const t = fs10__default.readFileSync(p, "utf8");
3881
4130
  return t.trim() ? t : void 0;
3882
4131
  } catch {
3883
4132
  return void 0;
@@ -4023,8 +4272,8 @@ function registerExport(program) {
4023
4272
  out(content.trimEnd());
4024
4273
  return;
4025
4274
  }
4026
- fs8.mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
4027
- fs8.writeFileSync(file, content);
4275
+ fs10.mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
4276
+ fs10.writeFileSync(file, content);
4028
4277
  info(`${c.green("\u2714")} wrote ${file} ${c.dim(`(${format})`)}`);
4029
4278
  });
4030
4279
  applyGlobalOptions(cmd);
@@ -4047,24 +4296,24 @@ function registerBundle(program) {
4047
4296
  } catch {
4048
4297
  throw new CliError("no grammars found to bundle (run a build first)", ExitCode.ERROR);
4049
4298
  }
4050
- fs8.mkdirSync(path.join(outDir, "grammars"), { recursive: true });
4299
+ fs10.mkdirSync(path.join(outDir, "grammars"), { recursive: true });
4051
4300
  let grammarCount = 0;
4052
4301
  for (const { fileName, absPath } of grammarFiles) {
4053
- fs8.copyFileSync(absPath, path.join(outDir, "grammars", fileName));
4302
+ fs10.copyFileSync(absPath, path.join(outDir, "grammars", fileName));
4054
4303
  grammarCount++;
4055
4304
  }
4056
4305
  const included = [`grammars/ (${grammarCount} .wasm)`];
4057
4306
  const graphSrc = global.graph ?? defaultGraphPath(root);
4058
- if (fs8.existsSync(graphSrc)) {
4059
- fs8.copyFileSync(graphSrc, path.join(outDir, "graph.json"));
4307
+ if (fs10.existsSync(graphSrc)) {
4308
+ fs10.copyFileSync(graphSrc, path.join(outDir, "graph.json"));
4060
4309
  included.push("graph.json");
4061
4310
  }
4062
4311
  const cat = catalogPath(root);
4063
- if (fs8.existsSync(cat)) {
4064
- fs8.copyFileSync(cat, path.join(outDir, "vibgrate.lib.json"));
4312
+ if (fs10.existsSync(cat)) {
4313
+ fs10.copyFileSync(cat, path.join(outDir, "vibgrate.lib.json"));
4065
4314
  included.push("vibgrate.lib.json");
4066
- if (fs8.existsSync(libDir(root))) {
4067
- fs8.cpSync(libDir(root), path.join(outDir, "lib"), { recursive: true });
4315
+ if (fs10.existsSync(libDir(root))) {
4316
+ fs10.cpSync(libDir(root), path.join(outDir, "lib"), { recursive: true });
4068
4317
  included.push("lib/");
4069
4318
  }
4070
4319
  }
@@ -4076,7 +4325,7 @@ function registerBundle(program) {
4076
4325
  included,
4077
4326
  usage: "Run `vg build --grammars <this dir>/grammars` to build fully offline."
4078
4327
  };
4079
- fs8.writeFileSync(path.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
4328
+ fs10.writeFileSync(path.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
4080
4329
  `);
4081
4330
  if (global.json) {
4082
4331
  json({ out: path.relative(root, outDir), ...manifest });
@@ -4100,7 +4349,7 @@ async function runDoctor(global) {
4100
4349
  const root = rootOf(global);
4101
4350
  const local = global.local === true;
4102
4351
  const graphPath = global.graph ?? defaultGraphPath(root);
4103
- const configFile = CONFIG_BASENAMES.find((f) => fs8.existsSync(path.join(root, f))) ?? null;
4352
+ const configFile = CONFIG_BASENAMES.find((f) => fs10.existsSync(path.join(root, f))) ?? null;
4104
4353
  const graph = loadGraph(root, graphPath);
4105
4354
  let staleFiles = null;
4106
4355
  if (graph) {
@@ -4128,7 +4377,7 @@ async function runDoctor(global) {
4128
4377
  },
4129
4378
  libCatalog: {
4130
4379
  path: path.relative(root, catalogPath(root)),
4131
- present: fs8.existsSync(catalogPath(root)),
4380
+ present: fs10.existsSync(catalogPath(root)),
4132
4381
  libraries: libCount
4133
4382
  },
4134
4383
  credentials: creds,
@@ -4220,7 +4469,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
4220
4469
  console.log(chalk8.green("\u2714") + ` Created ${chalk8.bold("vibgrate.config.ts")}`);
4221
4470
  }
4222
4471
  if (opts.baseline) {
4223
- const { runBaseline } = await import('./baseline-5QPV6API.js');
4472
+ const { runBaseline } = await import('./baseline-FALF7ULT.js');
4224
4473
  await runBaseline(rootDir);
4225
4474
  }
4226
4475
  console.log("");
@@ -4330,9 +4579,9 @@ async function fileReferencesIdentifier(rootDir, relFile, identifier, readFile3)
4330
4579
  }
4331
4580
  async function defaultReadFile(absPath) {
4332
4581
  try {
4333
- const stat2 = await fs15.stat(absPath);
4582
+ const stat2 = await fs17.stat(absPath);
4334
4583
  if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES) return null;
4335
- return await fs15.readFile(absPath, "utf8");
4584
+ return await fs17.readFile(absPath, "utf8");
4336
4585
  } catch {
4337
4586
  return null;
4338
4587
  }
@@ -4977,7 +5226,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
4977
5226
  function scanStaleness(rootDir, artifactPath) {
4978
5227
  let ref;
4979
5228
  try {
4980
- ref = fs8.statSync(artifactPath).mtimeMs;
5229
+ ref = fs10.statSync(artifactPath).mtimeMs;
4981
5230
  } catch {
4982
5231
  return { stale: false };
4983
5232
  }
@@ -4989,7 +5238,7 @@ function scanStaleness(rootDir, artifactPath) {
4989
5238
  if (depth > 8 || scanned > MAX_ENTRIES) return;
4990
5239
  let entries;
4991
5240
  try {
4992
- entries = fs8.readdirSync(dir, { withFileTypes: true });
5241
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
4993
5242
  } catch {
4994
5243
  return;
4995
5244
  }
@@ -5004,7 +5253,7 @@ function scanStaleness(rootDir, artifactPath) {
5004
5253
  const abs = path.join(dir, e.name);
5005
5254
  let mtimeMs;
5006
5255
  try {
5007
- mtimeMs = fs8.statSync(abs).mtimeMs;
5256
+ mtimeMs = fs10.statSync(abs).mtimeMs;
5008
5257
  } catch {
5009
5258
  continue;
5010
5259
  }
@@ -5138,7 +5387,7 @@ function analyzeTree(root, packages) {
5138
5387
  if (depth > MAX_DEPTH || filesSeen >= MAX_FILES) return;
5139
5388
  let entries;
5140
5389
  try {
5141
- entries = fs8.readdirSync(dir, { withFileTypes: true });
5390
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
5142
5391
  } catch {
5143
5392
  return;
5144
5393
  }
@@ -5156,8 +5405,8 @@ function analyzeTree(root, packages) {
5156
5405
  const full = path.join(dir, entry.name);
5157
5406
  let content;
5158
5407
  try {
5159
- if (fs8.statSync(full).size > MAX_FILE_BYTES2) continue;
5160
- content = fs8.readFileSync(full, "utf8");
5408
+ if (fs10.statSync(full).size > MAX_FILE_BYTES2) continue;
5409
+ content = fs10.readFileSync(full, "utf8");
5161
5410
  } catch {
5162
5411
  continue;
5163
5412
  }
@@ -5525,8 +5774,8 @@ async function fetchLatestVersion() {
5525
5774
  }
5526
5775
  async function writeCache(data) {
5527
5776
  try {
5528
- await fs15.mkdir(CACHE_DIR, { recursive: true });
5529
- await fs15.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5777
+ await fs17.mkdir(CACHE_DIR, { recursive: true });
5778
+ await fs17.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5530
5779
  } catch {
5531
5780
  }
5532
5781
  }
@@ -5537,7 +5786,7 @@ function detectGlobalInstall(execPath = process.argv[1] || "", cwd = process.cwd
5537
5786
  if (!execPath) return null;
5538
5787
  let resolved = execPath;
5539
5788
  try {
5540
- resolved = fs8.realpathSync(execPath);
5789
+ resolved = fs10.realpathSync(execPath);
5541
5790
  } catch {
5542
5791
  }
5543
5792
  const p = resolved.replace(/\\/g, "/");
@@ -7477,7 +7726,7 @@ function dispatch(argv, cwd) {
7477
7726
  }
7478
7727
  const looksLikeQuestion = /\s/.test(first) || first.endsWith("?");
7479
7728
  if (looksLikeQuestion) return ["ask", ...args];
7480
- if (fs8.existsSync(path.resolve(cwd, first))) {
7729
+ if (fs10.existsSync(path.resolve(cwd, first))) {
7481
7730
  return [hasBuildOnlyFlag(args) ? "build" : "scan", ...args];
7482
7731
  }
7483
7732
  const moved = MOVED_COMMANDS[first];