@vibgrate/cli 2026.718.2 → 2026.720.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.
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, readStoredCredentials, projectCredentialsPath, homeCredentialsPath, SKIP_DIRS, SKIP_FILES } from './chunk-H5M7CXE6.js';
3
- import { resolvedGrammarFiles, grammarSetVersion, hashString, canonicalize, hashBytes } from './chunk-VFO5UDAT.js';
4
- import { loadAdvancedScanHook, baselineCommand } from './chunk-BFXDF6ZE.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-RME7BFMU.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, 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-7R4O5CZD.js';
3
+ import { resolvedGrammarFiles, grammarSetVersion, hashString, canonicalize, hashBytes } from './chunk-BHA7QIPD.js';
4
+ import { loadAdvancedScanHook, baselineCommand } from './chunk-DDQ5JGBV.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-OD5654VF.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 fs9 from 'fs';
9
+ import fs9__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 fs16 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 (!fs9.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
+ fs9.mkdirSync(path.dirname(keyPath), { recursive: true });
187
+ fs9.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
188
+ fs9.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(fs9.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
+ fs9.mkdirSync(path.dirname(outPath), { recursive: true });
211
+ fs9.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 (!fs9.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(fs9.readFileSync(bundlePath, "utf8"));
236
+ const publicKeyPem = opts.pub ? fs9.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
  }
@@ -288,21 +288,23 @@ var ROBOT = [
288
288
  " " + c.dim("\u2524") + teal("\u2502") + " " + c.dim("\u2581\u2581") + " " + teal("\u2502") + c.dim("\u251C"),
289
289
  " " + teal("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F")
290
290
  ];
291
- function logoLines(root) {
291
+ function logoLines(root, opts = {}) {
292
+ const product = opts.product ?? "Graph";
293
+ const tagline = opts.tagline ?? "Code Intelligence Engine";
292
294
  return [
293
295
  "",
294
296
  ` ${ROBOT[0]}`,
295
297
  ` ${ROBOT[1]}`,
296
298
  ` ${ROBOT[2]}`,
297
299
  ` ${ROBOT[3]}`,
298
- ` ${c.bold.white("Vibgrate")} ${teal("Graph")} ${c.dim(`\xB7 Code Intelligence Engine \xB7 v${VERSION}`)}`,
300
+ ` ${c.bold.white("Vibgrate")} ${teal(product)} ${c.dim(`\xB7 ${tagline} \xB7 v${VERSION}`)}`,
299
301
  ...root ? [` ${c.dim(root)}`] : [],
300
302
  ""
301
303
  ];
302
304
  }
303
- function printLogo(root) {
305
+ function printLogo(root, opts = {}) {
304
306
  if (!process.stderr.isTTY) return;
305
- process.stderr.write(`${logoLines(root).join("\n")}
307
+ process.stderr.write(`${logoLines(root, opts).join("\n")}
306
308
  `);
307
309
  }
308
310
 
@@ -584,8 +586,8 @@ function writeExport(graph, target) {
584
586
  ExitCode.USAGE_ERROR
585
587
  );
586
588
  }
587
- fs8.mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
588
- fs8.writeFileSync(target, content);
589
+ fs9.mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
590
+ fs9.writeFileSync(target, content);
589
591
  }
590
592
  function collect(value, prev) {
591
593
  return [...prev, value];
@@ -604,7 +606,7 @@ async function runStatus(global) {
604
606
  const root = path.resolve(global.cwd ?? ".");
605
607
  const graphPath = global.graph ?? defaultGraphPath(root);
606
608
  const graph = loadGraph(root, graphPath);
607
- const hasCache = fs8.existsSync(path.join(cacheDir(root), "parse-cache.json"));
609
+ const hasCache = fs9.existsSync(path.join(cacheDir(root), "parse-cache.json"));
608
610
  let stale = null;
609
611
  let staleExact = false;
610
612
  if (graph) {
@@ -1313,14 +1315,14 @@ function isCI(env = process.env) {
1313
1315
  function installId(env = process.env) {
1314
1316
  if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();
1315
1317
  try {
1316
- const existing = fs8.readFileSync(INSTALL_ID_FILE, "utf8").trim();
1318
+ const existing = fs9.readFileSync(INSTALL_ID_FILE, "utf8").trim();
1317
1319
  if (isUuid(existing)) return existing;
1318
1320
  } catch {
1319
1321
  }
1320
1322
  const id = randomUuid();
1321
1323
  try {
1322
- fs8.mkdirSync(INSTALL_ID_DIR, { recursive: true });
1323
- fs8.writeFileSync(INSTALL_ID_FILE, id + "\n", { mode: 384 });
1324
+ fs9.mkdirSync(INSTALL_ID_DIR, { recursive: true });
1325
+ fs9.writeFileSync(INSTALL_ID_FILE, id + "\n", { mode: 384 });
1324
1326
  } catch {
1325
1327
  }
1326
1328
  return id;
@@ -1344,7 +1346,7 @@ function ledgerFilePath(root) {
1344
1346
  }
1345
1347
  function readOffset(root) {
1346
1348
  try {
1347
- const raw = JSON.parse(fs8.readFileSync(offsetPath(root), "utf8"));
1349
+ const raw = JSON.parse(fs9.readFileSync(offsetPath(root), "utf8"));
1348
1350
  return typeof raw.offset === "number" && raw.offset >= 0 ? raw.offset : 0;
1349
1351
  } catch {
1350
1352
  return 0;
@@ -1352,8 +1354,8 @@ function readOffset(root) {
1352
1354
  }
1353
1355
  function writeOffset(root, offset) {
1354
1356
  try {
1355
- fs8.mkdirSync(cacheDir(root), { recursive: true });
1356
- fs8.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + "\n");
1357
+ fs9.mkdirSync(cacheDir(root), { recursive: true });
1358
+ fs9.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + "\n");
1357
1359
  } catch {
1358
1360
  }
1359
1361
  }
@@ -1361,7 +1363,7 @@ function buildBatch(root, fromOffset, id = installId()) {
1361
1363
  const file = ledgerFilePath(root);
1362
1364
  let size = 0;
1363
1365
  try {
1364
- size = fs8.statSync(file).size;
1366
+ size = fs9.statSync(file).size;
1365
1367
  } catch {
1366
1368
  return { batch: null, newOffset: 0 };
1367
1369
  }
@@ -1369,13 +1371,13 @@ function buildBatch(root, fromOffset, id = installId()) {
1369
1371
  if (start >= size) return { batch: null, newOffset: size };
1370
1372
  let text = "";
1371
1373
  try {
1372
- const fd = fs8.openSync(file, "r");
1374
+ const fd = fs9.openSync(file, "r");
1373
1375
  try {
1374
1376
  const buf = Buffer.alloc(size - start);
1375
- fs8.readSync(fd, buf, 0, buf.length, start);
1377
+ fs9.readSync(fd, buf, 0, buf.length, start);
1376
1378
  text = buf.toString("utf8");
1377
1379
  } finally {
1378
- fs8.closeSync(fd);
1380
+ fs9.closeSync(fd);
1379
1381
  }
1380
1382
  } catch {
1381
1383
  return { batch: null, newOffset: fromOffset };
@@ -1508,6 +1510,317 @@ function isLoopbackOrigin(origin) {
1508
1510
  return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
1509
1511
  }
1510
1512
 
1513
+ // src/mcp/serve-stats.ts
1514
+ function zeroRow(key2) {
1515
+ return { key: key2, calls: 0, complete: 0, partial: 0, miss: 0, timed: 0, totalMs: 0, vgTokens: 0, baselineTokens: 0 };
1516
+ }
1517
+ var SessionStats = class {
1518
+ startedAt;
1519
+ revision = 0;
1520
+ lastCallAt = null;
1521
+ totals = zeroRow("total");
1522
+ byClient = /* @__PURE__ */ new Map();
1523
+ byTool = /* @__PURE__ */ new Map();
1524
+ bySource = /* @__PURE__ */ new Map();
1525
+ constructor(now2 = Date.now()) {
1526
+ this.startedAt = now2;
1527
+ }
1528
+ record(sample, now2 = Date.now()) {
1529
+ this.revision++;
1530
+ this.lastCallAt = now2;
1531
+ const rows = [
1532
+ this.totals,
1533
+ this.rowFor(this.byClient, sample.client),
1534
+ this.rowFor(this.byTool, sample.tool),
1535
+ this.rowFor(this.bySource, sample.source ?? "mcp")
1536
+ ];
1537
+ for (const row of rows) {
1538
+ row.calls++;
1539
+ row[sample.outcome]++;
1540
+ if (typeof sample.ms === "number") {
1541
+ row.timed++;
1542
+ row.totalMs += sample.ms;
1543
+ }
1544
+ row.vgTokens += sample.vgTokens;
1545
+ row.baselineTokens += sample.baselineTokens;
1546
+ }
1547
+ }
1548
+ snapshot() {
1549
+ const sorted = (m) => [...m.values()].map((r) => ({ ...r })).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
1550
+ return {
1551
+ startedAt: this.startedAt,
1552
+ revision: this.revision,
1553
+ lastCallAt: this.lastCallAt,
1554
+ totals: { ...this.totals },
1555
+ clients: sorted(this.byClient),
1556
+ tools: sorted(this.byTool),
1557
+ sources: sorted(this.bySource)
1558
+ };
1559
+ }
1560
+ rowFor(m, key2) {
1561
+ let row = m.get(key2);
1562
+ if (!row) {
1563
+ row = zeroRow(key2);
1564
+ m.set(key2, row);
1565
+ }
1566
+ return row;
1567
+ }
1568
+ };
1569
+ function fmtUptime(ms) {
1570
+ const s = Math.max(0, Math.floor(ms / 1e3));
1571
+ if (s < 60) return `${s}s`;
1572
+ const m = Math.floor(s / 60);
1573
+ if (m < 60) return `${m}m ${String(s % 60).padStart(2, "0")}s`;
1574
+ const h = Math.floor(m / 60);
1575
+ if (h < 24) return `${h}h ${String(m % 60).padStart(2, "0")}m`;
1576
+ const d = Math.floor(h / 24);
1577
+ return `${d}d ${String(h % 24).padStart(2, "0")}h`;
1578
+ }
1579
+ function fmtTokens(n) {
1580
+ if (n >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
1581
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
1582
+ return String(n);
1583
+ }
1584
+ function fmtAvgMs(row) {
1585
+ if (row.timed === 0) return "\u2014";
1586
+ const avg = row.totalMs / row.timed;
1587
+ return avg >= 1e3 ? `${(avg / 1e3).toFixed(1)}s` : `${Math.round(avg)}ms`;
1588
+ }
1589
+ var teal2 = (s) => c.hex("#3FB0A4")(s);
1590
+ var mint2 = (s) => c.hex("#4FE3C1")(s);
1591
+ var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1592
+ var MAX_TOOL_ROWS = 8;
1593
+ function serveStatusLines(snap, now2, tick = 0) {
1594
+ const spinner = mint2(SPINNER[tick % SPINNER.length]);
1595
+ const up = fmtUptime(now2 - snap.startedAt);
1596
+ const lines = [];
1597
+ if (snap.totals.calls === 0) {
1598
+ lines.push(
1599
+ ` ${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")}`
1600
+ );
1601
+ return lines;
1602
+ }
1603
+ const t = snap.totals;
1604
+ const answered = Math.round((t.complete + t.partial) / t.calls * 100);
1605
+ const cli = snap.sources.find((s) => s.key === "cli");
1606
+ const via = cli ? c.dim(" \xB7 via ") + snap.sources.map((s) => `${c.white(s.key)} ${teal2(String(s.calls))}`).join(c.dim(" \xB7 ")) : "";
1607
+ const ago = snap.lastCallAt === null ? "" : c.dim(` \xB7 last call ${fmtUptime(now2 - snap.lastCallAt)} ago`);
1608
+ lines.push(
1609
+ ` ${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}`
1610
+ );
1611
+ lines.push(
1612
+ ` ${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
+ );
1614
+ const shown = snap.tools.slice(0, MAX_TOOL_ROWS);
1615
+ const nameW = Math.max(...shown.map((r) => r.key.length));
1616
+ for (const row of shown) {
1617
+ let line = ` ${c.white(row.key.padEnd(nameW))} ${teal2(String(row.calls).padStart(4))} ${c.dim("\xB7")} ` + c.dim(`avg ${fmtAvgMs(row)}`);
1618
+ if (SAVINGS_TOOLS.has(row.key) && row.baselineTokens > 0) {
1619
+ line += c.dim(" \xB7 ctx ") + c.white(fmtTokens(row.vgTokens)) + c.dim(` vs \u2248${fmtTokens(row.baselineTokens)} grep/read`);
1620
+ }
1621
+ if (row.miss > 0) line += c.dim(" \xB7 ") + c.yellow(`${row.miss} miss`);
1622
+ lines.push(line);
1623
+ }
1624
+ if (snap.tools.length > shown.length) {
1625
+ lines.push(` ${c.dim(`\u2026 +${snap.tools.length - shown.length} more tools`)}`);
1626
+ }
1627
+ if (t.baselineTokens > 0) {
1628
+ const savedTokens = Math.max(0, t.baselineTokens - t.vgTokens);
1629
+ const ratio = t.vgTokens > 0 ? (t.baselineTokens / t.vgTokens).toFixed(1) : "\u2014";
1630
+ lines.push(
1631
+ ` ${c.dim("session")} ctx ${c.bold.white(fmtTokens(t.vgTokens))} ` + c.dim(`vs grep/read \u2248${fmtTokens(t.baselineTokens)} \u2192 `) + mint2(`${ratio}\xD7 fewer`)
1632
+ );
1633
+ lines.push(
1634
+ ` ${c.dim("est. saved \u2248")} ${mint2(`${fmtTokens(savedTokens)} context tokens`)} ${c.dim("(estimate)")}`
1635
+ );
1636
+ }
1637
+ return lines;
1638
+ }
1639
+ function serveHeartbeatLine(snap, now2) {
1640
+ const up = fmtUptime(now2 - snap.startedAt);
1641
+ const t = snap.totals;
1642
+ if (t.calls === 0) return c.dim(`vg \xB7 serving for ${up} \u2014 no tool calls yet`);
1643
+ const clients = snap.clients.map((cl) => `${cl.key} ${cl.calls}`).join(", ");
1644
+ const cli = snap.sources.find((s) => s.key === "cli");
1645
+ let line = `vg \xB7 serving for ${up} \u2014 ${t.calls} call${t.calls === 1 ? "" : "s"} (${clients})`;
1646
+ if (cli) line += ` \xB7 ${cli.calls} via CLI`;
1647
+ if (t.baselineTokens > 0) {
1648
+ const savedTokens = Math.max(0, t.baselineTokens - t.vgTokens);
1649
+ line += ` \xB7 ctx ${fmtTokens(t.vgTokens)} vs \u2248${fmtTokens(t.baselineTokens)} grep/read \xB7 est. saved \u2248 ${fmtTokens(savedTokens)} tokens`;
1650
+ }
1651
+ return c.dim(line);
1652
+ }
1653
+ var TTY_INTERVAL_MS = 1e3;
1654
+ var HEARTBEAT_INTERVAL_MS = 15 * 60 * 1e3;
1655
+ var ServeStatusDisplay = class {
1656
+ constructor(stats, stream = process.stderr) {
1657
+ this.stats = stats;
1658
+ this.stream = stream;
1659
+ this.isTTY = (stream.isTTY ?? false) && process.env.VIBGRATE_PROGRESS_MODE !== "plain";
1660
+ }
1661
+ stats;
1662
+ stream;
1663
+ timer = null;
1664
+ tick = 0;
1665
+ lastRowCount = 0;
1666
+ lastFrame = "";
1667
+ lastHeartbeatRevision = -1;
1668
+ cursorHidden = false;
1669
+ isTTY;
1670
+ start() {
1671
+ if (this.timer) return;
1672
+ if (this.isTTY) {
1673
+ this.hideCursor();
1674
+ process.on("exit", this.restoreCursor);
1675
+ process.once("SIGINT", () => {
1676
+ this.restoreCursor();
1677
+ process.exit(130);
1678
+ });
1679
+ process.once("SIGTERM", () => {
1680
+ this.restoreCursor();
1681
+ process.exit(143);
1682
+ });
1683
+ this.timer = setInterval(() => this.renderTTY(), TTY_INTERVAL_MS);
1684
+ this.renderTTY();
1685
+ } else {
1686
+ this.timer = setInterval(() => this.renderHeartbeat(), HEARTBEAT_INTERVAL_MS);
1687
+ }
1688
+ this.timer.unref?.();
1689
+ }
1690
+ stop() {
1691
+ if (this.timer) {
1692
+ clearInterval(this.timer);
1693
+ this.timer = null;
1694
+ }
1695
+ if (this.isTTY) {
1696
+ let buf = "";
1697
+ if (this.lastRowCount > 0) buf += `\x1B[${this.lastRowCount}A\x1B[J`;
1698
+ this.stream.write(buf);
1699
+ this.lastRowCount = 0;
1700
+ this.restoreCursor();
1701
+ }
1702
+ }
1703
+ renderTTY() {
1704
+ this.tick++;
1705
+ const lines = serveStatusLines(this.stats.snapshot(), Date.now(), this.tick);
1706
+ const content = lines.join("\n") + "\n";
1707
+ if (content === this.lastFrame) return;
1708
+ this.lastFrame = content;
1709
+ let buf = "";
1710
+ if (this.lastRowCount > 0) buf += `\x1B[${this.lastRowCount}A\x1B[J`;
1711
+ buf += content;
1712
+ this.stream.write(buf);
1713
+ this.lastRowCount = this.countRows(lines);
1714
+ }
1715
+ renderHeartbeat() {
1716
+ const snap = this.stats.snapshot();
1717
+ if (snap.revision === this.lastHeartbeatRevision) return;
1718
+ this.lastHeartbeatRevision = snap.revision;
1719
+ this.stream.write(serveHeartbeatLine(snap, Date.now()) + "\n");
1720
+ }
1721
+ /** Terminal rows the frame occupies, accounting for line wrap. */
1722
+ countRows(lines) {
1723
+ const columns = Math.max(this.stream.columns ?? 80, 20);
1724
+ return lines.reduce((total, line) => {
1725
+ const visible = line.replace(/\x1b\[[0-9;]*m/g, "").length;
1726
+ return total + Math.max(1, Math.ceil(visible / columns));
1727
+ }, 0);
1728
+ }
1729
+ hideCursor() {
1730
+ if (this.cursorHidden) return;
1731
+ this.stream.write("\x1B[?25l");
1732
+ this.cursorHidden = true;
1733
+ }
1734
+ restoreCursor = () => {
1735
+ if (!this.cursorHidden) return;
1736
+ this.stream.write("\x1B[?25h");
1737
+ this.cursorHidden = false;
1738
+ };
1739
+ };
1740
+ var POLL_INTERVAL_MS = 2e3;
1741
+ var LedgerTail = class {
1742
+ constructor(file, stats, intervalMs = POLL_INTERVAL_MS) {
1743
+ this.file = file;
1744
+ this.stats = stats;
1745
+ this.intervalMs = intervalMs;
1746
+ }
1747
+ file;
1748
+ stats;
1749
+ intervalMs;
1750
+ timer = null;
1751
+ /** Byte offset of the next unread ledger byte (always at a line boundary). */
1752
+ offset = 0;
1753
+ start() {
1754
+ if (this.timer) return;
1755
+ this.offset = this.sizeOf();
1756
+ this.timer = setInterval(() => this.poll(), this.intervalMs);
1757
+ this.timer.unref?.();
1758
+ }
1759
+ stop() {
1760
+ if (this.timer) {
1761
+ clearInterval(this.timer);
1762
+ this.timer = null;
1763
+ }
1764
+ }
1765
+ /** One poll cycle — exposed for tests (deterministic, no timers needed). */
1766
+ poll(now2 = Date.now()) {
1767
+ let size;
1768
+ try {
1769
+ size = this.sizeOf();
1770
+ } catch {
1771
+ return;
1772
+ }
1773
+ if (size < this.offset) this.offset = 0;
1774
+ if (size === this.offset) return;
1775
+ let chunk;
1776
+ try {
1777
+ const fd = fs9.openSync(this.file, "r");
1778
+ try {
1779
+ chunk = Buffer.alloc(size - this.offset);
1780
+ fs9.readSync(fd, chunk, 0, chunk.length, this.offset);
1781
+ } finally {
1782
+ fs9.closeSync(fd);
1783
+ }
1784
+ } catch {
1785
+ return;
1786
+ }
1787
+ const lastNewline = chunk.lastIndexOf(10);
1788
+ if (lastNewline < 0) return;
1789
+ const text = chunk.subarray(0, lastNewline).toString("utf8");
1790
+ this.offset += lastNewline + 1;
1791
+ for (const line of text.split("\n")) ingestLedgerLine(this.stats, line, now2);
1792
+ }
1793
+ sizeOf() {
1794
+ try {
1795
+ return fs9.statSync(this.file).size;
1796
+ } catch {
1797
+ return 0;
1798
+ }
1799
+ }
1800
+ };
1801
+ function ingestLedgerLine(stats, line, now2) {
1802
+ if (!line.trim()) return;
1803
+ let e;
1804
+ try {
1805
+ e = JSON.parse(line);
1806
+ } catch {
1807
+ return;
1808
+ }
1809
+ if (e.source !== "cli" || typeof e.tool !== "string") return;
1810
+ stats.record(
1811
+ {
1812
+ tool: e.tool,
1813
+ client: sanitizeClient(e.client),
1814
+ outcome: e.outcome ?? "complete",
1815
+ source: "cli",
1816
+ // CLI lines carry no wall time — leave ms absent ("not measured").
1817
+ vgTokens: e.vgTokens ?? 0,
1818
+ baselineTokens: e.baselineTokens ?? 0
1819
+ },
1820
+ now2
1821
+ );
1822
+ }
1823
+
1511
1824
  // src/commands/serve.ts
1512
1825
  var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
1513
1826
  function registerServe(program) {
@@ -1519,6 +1832,11 @@ function registerServe(program) {
1519
1832
  const local = global.local === true;
1520
1833
  const optOut = telemetryOptOut();
1521
1834
  const shareStats = opts.shareStats === true && !local && optOut === null;
1835
+ const quiet = global.quiet === true;
1836
+ const stats = quiet ? void 0 : new SessionStats();
1837
+ if (!quiet) {
1838
+ printLogo(root, { product: "AI Context", tagline: "Local-first MCP for your AI" });
1839
+ }
1522
1840
  const serveOpts = {
1523
1841
  // A *requested* --share-stats always implies local recording, even when
1524
1842
  // the upload itself is suppressed (--local / env opt-out) — the local
@@ -1528,7 +1846,8 @@ function registerServe(program) {
1528
1846
  shareStats,
1529
1847
  local,
1530
1848
  dedup: opts.dedup === true,
1531
- refresh
1849
+ refresh,
1850
+ stats
1532
1851
  };
1533
1852
  await ensureServableGraph(root, graphPath, global, refresh);
1534
1853
  if (opts.shareStats === true && local) {
@@ -1537,19 +1856,22 @@ function registerServe(program) {
1537
1856
  info(c.dim(`vg \xB7 --share-stats disabled by ${optOut}: recording locally, not uploading.`));
1538
1857
  }
1539
1858
  if (shareStats) startSharing(root);
1859
+ const display = stats ? new ServeStatusDisplay(stats) : void 0;
1860
+ if (stats) new LedgerTail(savingsLedgerPath(root), stats).start();
1540
1861
  const freshness = refresh ? "auto-refresh" : "as built";
1541
1862
  if (opts.http) {
1542
- await serveHttp(graphPath, opts.host ?? "127.0.0.1", Number(opts.port) || 7437, serveOpts, freshness);
1863
+ await serveHttp(graphPath, opts.host ?? "127.0.0.1", Number(opts.port) || 7437, serveOpts, freshness, () => display?.start());
1543
1864
  } else {
1544
1865
  info(c.dim(`vg \xB7 MCP server on stdio (read-only, ${freshness}). Connect your assistant to this process.`));
1545
1866
  await serveStdio(graphPath, serveOpts);
1867
+ display?.start();
1546
1868
  }
1547
1869
  });
1548
1870
  applyGlobalOptions(cmd);
1549
1871
  }
1550
1872
  async function ensureServableGraph(root, graphPath, global, refresh, opts = {}) {
1551
1873
  if (refresh) {
1552
- if (!fs8.existsSync(graphPath)) {
1874
+ if (!fs9.existsSync(graphPath)) {
1553
1875
  info(c.dim("vg \xB7 no map found \u2014 building it before serving\u2026"));
1554
1876
  await runBuild(
1555
1877
  [],
@@ -1566,7 +1888,7 @@ async function ensureServableGraph(root, graphPath, global, refresh, opts = {})
1566
1888
  }
1567
1889
  }
1568
1890
  }
1569
- if (!fs8.existsSync(graphPath)) {
1891
+ if (!fs9.existsSync(graphPath)) {
1570
1892
  throw new CliError(
1571
1893
  `no map found at ${graphPath} \u2014 run \`vg\` to build one first`,
1572
1894
  ExitCode.NOT_FOUND
@@ -1598,7 +1920,7 @@ function startSharing(root) {
1598
1920
  });
1599
1921
  process.once("beforeExit", finalFlush);
1600
1922
  }
1601
- async function serveHttp(graphPath, host, port, opts, freshness) {
1923
+ async function serveHttp(graphPath, host, port, opts, freshness, onReady) {
1602
1924
  const { createServer: createHttp } = await import('http');
1603
1925
  const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
1604
1926
  const source = new GraphSource(graphPath, opts.refresh !== false);
@@ -1630,6 +1952,7 @@ async function serveHttp(graphPath, host, port, opts, freshness) {
1630
1952
  });
1631
1953
  await new Promise((resolve17) => httpServer.listen(port, host, resolve17));
1632
1954
  info(c.dim(`vg \xB7 MCP server on http://${host}:${port}/mcp (read-only, local, ${freshness})`));
1955
+ onReady?.();
1633
1956
  await new Promise(() => {
1634
1957
  });
1635
1958
  }
@@ -1848,7 +2171,7 @@ function manifestHash(root) {
1848
2171
  const walk2 = (dir) => {
1849
2172
  let entries;
1850
2173
  try {
1851
- entries = fs8.readdirSync(dir, { withFileTypes: true });
2174
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
1852
2175
  } catch {
1853
2176
  return;
1854
2177
  }
@@ -1864,7 +2187,7 @@ function manifestHash(root) {
1864
2187
  if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
1865
2188
  try {
1866
2189
  const rel3 = path.relative(root, abs).split(path.sep).join("/");
1867
- files.push({ rel: rel3, hash: hashBytes(fs8.readFileSync(abs)) });
2190
+ files.push({ rel: rel3, hash: hashBytes(fs9.readFileSync(abs)) });
1868
2191
  } catch {
1869
2192
  }
1870
2193
  }
@@ -1875,7 +2198,7 @@ function manifestHash(root) {
1875
2198
  }
1876
2199
  function loadScanCache(root, key2) {
1877
2200
  try {
1878
- const raw = JSON.parse(fs8.readFileSync(cachePath(root), "utf8"));
2201
+ const raw = JSON.parse(fs9.readFileSync(cachePath(root), "utf8"));
1879
2202
  if (raw.version === CACHE_VERSION && raw.manifestHash === key2.manifestHash && raw.toolVersion === key2.toolVersion && raw.offline === key2.offline && raw.artifact) {
1880
2203
  return { manifestHash: raw.manifestHash, artifact: raw.artifact };
1881
2204
  }
@@ -1885,8 +2208,8 @@ function loadScanCache(root, key2) {
1885
2208
  }
1886
2209
  function writeScanCache(root, key2, artifact) {
1887
2210
  try {
1888
- fs8.mkdirSync(cacheDir(root), { recursive: true });
1889
- fs8.writeFileSync(
2211
+ fs9.mkdirSync(cacheDir(root), { recursive: true });
2212
+ fs9.writeFileSync(
1890
2213
  cachePath(root),
1891
2214
  stableStringify(
1892
2215
  { version: CACHE_VERSION, manifestHash: key2.manifestHash, toolVersion: key2.toolVersion, offline: key2.offline, artifact },
@@ -2710,7 +3033,7 @@ function hoverMarkdown(dep, contribution) {
2710
3033
  return lines.join("\n");
2711
3034
  }
2712
3035
  function startLanguageServer(opts) {
2713
- if (!fs8.existsSync(opts.root)) {
3036
+ if (!fs9.existsSync(opts.root)) {
2714
3037
  process.stderr.write(`vg lsp: root does not exist: ${opts.root}
2715
3038
  `);
2716
3039
  process.exit(1);
@@ -2849,7 +3172,7 @@ function registerShare(program) {
2849
3172
  const global = readGlobal(this);
2850
3173
  const root = rootOf(global);
2851
3174
  const gitDir = path.join(root, ".git");
2852
- if (!fs8.existsSync(gitDir)) {
3175
+ if (!fs9.existsSync(gitDir)) {
2853
3176
  throw new CliError("not a git repository \u2014 run `vg share` inside a git repo", ExitCode.USAGE_ERROR);
2854
3177
  }
2855
3178
  if (opts.undo) {
@@ -2871,22 +3194,22 @@ function registerShare(program) {
2871
3194
  }
2872
3195
  function writeVibgrateGitignore(root, reports) {
2873
3196
  const dir = path.join(root, ".vibgrate");
2874
- fs8.mkdirSync(dir, { recursive: true });
3197
+ fs9.mkdirSync(dir, { recursive: true });
2875
3198
  const lines = ["# Managed by `vg share` \u2014 keep graph.json committed, ignore the rest", "cache/", "facts.jsonl"];
2876
3199
  if (!reports) lines.push("graph.html", "GRAPH_REPORT.md");
2877
- fs8.writeFileSync(path.join(dir, ".gitignore"), `${lines.join("\n")}
3200
+ fs9.writeFileSync(path.join(dir, ".gitignore"), `${lines.join("\n")}
2878
3201
  `);
2879
3202
  }
2880
3203
  function writeGitAttributes(root) {
2881
3204
  const file = path.join(root, ".gitattributes");
2882
3205
  const line = ".vibgrate/graph.json merge=vg";
2883
- let content = fs8.existsSync(file) ? fs8.readFileSync(file, "utf8") : "";
3206
+ let content = fs9.existsSync(file) ? fs9.readFileSync(file, "utf8") : "";
2884
3207
  if (!content.split("\n").some((l) => l.trim() === line)) {
2885
3208
  content = content.length ? `${content.replace(/\s*$/, "")}
2886
3209
  ${line}
2887
3210
  ` : `${line}
2888
3211
  `;
2889
- fs8.writeFileSync(file, content);
3212
+ fs9.writeFileSync(file, content);
2890
3213
  }
2891
3214
  }
2892
3215
  function configureMergeDriver(root) {
@@ -2895,7 +3218,7 @@ function configureMergeDriver(root) {
2895
3218
  }
2896
3219
  function installPreCommitHook(root) {
2897
3220
  const hooksDir = hookDir(root);
2898
- fs8.mkdirSync(hooksDir, { recursive: true });
3221
+ fs9.mkdirSync(hooksDir, { recursive: true });
2899
3222
  const file = path.join(hooksDir, "pre-commit");
2900
3223
  const block = [
2901
3224
  HOOK_BEGIN,
@@ -2905,7 +3228,7 @@ function installPreCommitHook(root) {
2905
3228
  "fi",
2906
3229
  HOOK_END
2907
3230
  ].join("\n");
2908
- let content = fs8.existsSync(file) ? fs8.readFileSync(file, "utf8") : "#!/bin/sh\n";
3231
+ let content = fs9.existsSync(file) ? fs9.readFileSync(file, "utf8") : "#!/bin/sh\n";
2909
3232
  const re = new RegExp(`${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}`);
2910
3233
  if (re.test(content)) content = content.replace(re, block);
2911
3234
  else content = `${content.replace(/\s*$/, "")}
@@ -2914,21 +3237,21 @@ ${block}
2914
3237
  `;
2915
3238
  if (!content.startsWith("#!")) content = `#!/bin/sh
2916
3239
  ${content}`;
2917
- fs8.writeFileSync(file, content);
2918
- fs8.chmodSync(file, 493);
3240
+ fs9.writeFileSync(file, content);
3241
+ fs9.chmodSync(file, 493);
2919
3242
  }
2920
3243
  function undo(root) {
2921
3244
  const hookFile = path.join(hookDir(root), "pre-commit");
2922
- if (fs8.existsSync(hookFile)) {
3245
+ if (fs9.existsSync(hookFile)) {
2923
3246
  const re = new RegExp(`\\n*${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}\\n*`);
2924
- const next2 = fs8.readFileSync(hookFile, "utf8").replace(re, "\n");
2925
- if (next2.trim() === "#!/bin/sh") fs8.rmSync(hookFile);
2926
- else fs8.writeFileSync(hookFile, next2);
3247
+ const next2 = fs9.readFileSync(hookFile, "utf8").replace(re, "\n");
3248
+ if (next2.trim() === "#!/bin/sh") fs9.rmSync(hookFile);
3249
+ else fs9.writeFileSync(hookFile, next2);
2927
3250
  }
2928
3251
  const attr = path.join(root, ".gitattributes");
2929
- if (fs8.existsSync(attr)) {
2930
- const next2 = fs8.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
2931
- fs8.writeFileSync(attr, next2);
3252
+ if (fs9.existsSync(attr)) {
3253
+ const next2 = fs9.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
3254
+ fs9.writeFileSync(attr, next2);
2932
3255
  }
2933
3256
  try {
2934
3257
  git(root, ["config", "--remove-section", "merge.vg"]);
@@ -3061,7 +3384,7 @@ function grepBaselineTokens(term, files) {
3061
3384
  if (f.size > 512 * 1024) continue;
3062
3385
  let content = "";
3063
3386
  try {
3064
- content = fs8.readFileSync(f.abs, "utf8");
3387
+ content = fs9.readFileSync(f.abs, "utf8");
3065
3388
  } catch {
3066
3389
  continue;
3067
3390
  }
@@ -3071,7 +3394,7 @@ function grepBaselineTokens(term, files) {
3071
3394
  }
3072
3395
  function safeSize(abs) {
3073
3396
  try {
3074
- return fs8.statSync(abs).size;
3397
+ return fs9.statSync(abs).size;
3075
3398
  } catch {
3076
3399
  return 0;
3077
3400
  }
@@ -3405,7 +3728,7 @@ function loadStandards(root) {
3405
3728
  const p = path__default.join(root, rel3);
3406
3729
  let text;
3407
3730
  try {
3408
- text = fs8__default.readFileSync(p, "utf8");
3731
+ text = fs9__default.readFileSync(p, "utf8");
3409
3732
  } catch {
3410
3733
  continue;
3411
3734
  }
@@ -3558,17 +3881,18 @@ function printBreakdown(usage) {
3558
3881
  info("");
3559
3882
  info(c.bold(" by command") + c.dim(" (complete = full result \xB7 partial = capped/paginated \xB7 miss = no result)"));
3560
3883
  const nameW = Math.max(7, ...usage.commands.map((cmd) => cmd.tool.length));
3561
- const header = " " + "command".padEnd(nameW) + ["calls", "complete", "partial", "miss", "success%"].map((h) => h.padStart(9)).join("");
3884
+ const header = " " + "command".padEnd(nameW) + ["calls", "complete", "partial", "miss", "success%", "avg ms"].map((h) => h.padStart(9)).join("");
3562
3885
  info(c.dim(header));
3563
3886
  for (const cmd of usage.commands) {
3564
3887
  info(
3565
- " " + cmd.tool.padEnd(nameW) + String(cmd.calls).padStart(9) + String(cmd.complete).padStart(9) + String(cmd.partial).padStart(9) + String(cmd.miss).padStart(9) + `${cmd.successPct}%`.padStart(9)
3888
+ " " + cmd.tool.padEnd(nameW) + String(cmd.calls).padStart(9) + String(cmd.complete).padStart(9) + String(cmd.partial).padStart(9) + String(cmd.miss).padStart(9) + `${cmd.successPct}%`.padStart(9) + // Absent ≠ zero: calls recorded before timing existed show '—'.
3889
+ (cmd.avgMs === null ? "\u2014" : String(cmd.avgMs)).padStart(9)
3566
3890
  );
3567
3891
  }
3568
3892
  const t = usage.totals;
3569
- info(c.dim(" " + "\u2500".repeat(nameW + 45)));
3893
+ info(c.dim(" " + "\u2500".repeat(nameW + 54)));
3570
3894
  info(
3571
- " " + c.bold("total".padEnd(nameW)) + String(t.calls).padStart(9) + String(t.complete).padStart(9) + String(t.partial).padStart(9) + String(t.miss).padStart(9) + "".padStart(9)
3895
+ " " + c.bold("total".padEnd(nameW)) + String(t.calls).padStart(9) + String(t.complete).padStart(9) + String(t.partial).padStart(9) + String(t.miss).padStart(9) + "".padStart(9) + "".padStart(9)
3572
3896
  );
3573
3897
  info(c.dim(` avg success across commands: ${usage.avgSuccessPct}%`));
3574
3898
  }
@@ -3643,7 +3967,7 @@ async function addCmd(root, rest, opts, asJson) {
3643
3967
  }
3644
3968
  function readPackageVersion(root) {
3645
3969
  try {
3646
- const pkg = JSON.parse(fs8__default.readFileSync(path__default.join(root, "package.json"), "utf8"));
3970
+ const pkg = JSON.parse(fs9__default.readFileSync(path__default.join(root, "package.json"), "utf8"));
3647
3971
  return typeof pkg.version === "string" ? pkg.version : void 0;
3648
3972
  } catch {
3649
3973
  return void 0;
@@ -3651,7 +3975,7 @@ function readPackageVersion(root) {
3651
3975
  }
3652
3976
  function readOptionalFile(p) {
3653
3977
  try {
3654
- const t = fs8__default.readFileSync(p, "utf8");
3978
+ const t = fs9__default.readFileSync(p, "utf8");
3655
3979
  return t.trim() ? t : void 0;
3656
3980
  } catch {
3657
3981
  return void 0;
@@ -3797,8 +4121,8 @@ function registerExport(program) {
3797
4121
  out(content.trimEnd());
3798
4122
  return;
3799
4123
  }
3800
- fs8.mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
3801
- fs8.writeFileSync(file, content);
4124
+ fs9.mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
4125
+ fs9.writeFileSync(file, content);
3802
4126
  info(`${c.green("\u2714")} wrote ${file} ${c.dim(`(${format})`)}`);
3803
4127
  });
3804
4128
  applyGlobalOptions(cmd);
@@ -3821,24 +4145,24 @@ function registerBundle(program) {
3821
4145
  } catch {
3822
4146
  throw new CliError("no grammars found to bundle (run a build first)", ExitCode.ERROR);
3823
4147
  }
3824
- fs8.mkdirSync(path.join(outDir, "grammars"), { recursive: true });
4148
+ fs9.mkdirSync(path.join(outDir, "grammars"), { recursive: true });
3825
4149
  let grammarCount = 0;
3826
4150
  for (const { fileName, absPath } of grammarFiles) {
3827
- fs8.copyFileSync(absPath, path.join(outDir, "grammars", fileName));
4151
+ fs9.copyFileSync(absPath, path.join(outDir, "grammars", fileName));
3828
4152
  grammarCount++;
3829
4153
  }
3830
4154
  const included = [`grammars/ (${grammarCount} .wasm)`];
3831
4155
  const graphSrc = global.graph ?? defaultGraphPath(root);
3832
- if (fs8.existsSync(graphSrc)) {
3833
- fs8.copyFileSync(graphSrc, path.join(outDir, "graph.json"));
4156
+ if (fs9.existsSync(graphSrc)) {
4157
+ fs9.copyFileSync(graphSrc, path.join(outDir, "graph.json"));
3834
4158
  included.push("graph.json");
3835
4159
  }
3836
4160
  const cat = catalogPath(root);
3837
- if (fs8.existsSync(cat)) {
3838
- fs8.copyFileSync(cat, path.join(outDir, "vibgrate.lib.json"));
4161
+ if (fs9.existsSync(cat)) {
4162
+ fs9.copyFileSync(cat, path.join(outDir, "vibgrate.lib.json"));
3839
4163
  included.push("vibgrate.lib.json");
3840
- if (fs8.existsSync(libDir(root))) {
3841
- fs8.cpSync(libDir(root), path.join(outDir, "lib"), { recursive: true });
4164
+ if (fs9.existsSync(libDir(root))) {
4165
+ fs9.cpSync(libDir(root), path.join(outDir, "lib"), { recursive: true });
3842
4166
  included.push("lib/");
3843
4167
  }
3844
4168
  }
@@ -3850,7 +4174,7 @@ function registerBundle(program) {
3850
4174
  included,
3851
4175
  usage: "Run `vg build --grammars <this dir>/grammars` to build fully offline."
3852
4176
  };
3853
- fs8.writeFileSync(path.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
4177
+ fs9.writeFileSync(path.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
3854
4178
  `);
3855
4179
  if (global.json) {
3856
4180
  json({ out: path.relative(root, outDir), ...manifest });
@@ -3874,7 +4198,7 @@ async function runDoctor(global) {
3874
4198
  const root = rootOf(global);
3875
4199
  const local = global.local === true;
3876
4200
  const graphPath = global.graph ?? defaultGraphPath(root);
3877
- const configFile = CONFIG_BASENAMES.find((f) => fs8.existsSync(path.join(root, f))) ?? null;
4201
+ const configFile = CONFIG_BASENAMES.find((f) => fs9.existsSync(path.join(root, f))) ?? null;
3878
4202
  const graph = loadGraph(root, graphPath);
3879
4203
  let staleFiles = null;
3880
4204
  if (graph) {
@@ -3902,7 +4226,7 @@ async function runDoctor(global) {
3902
4226
  },
3903
4227
  libCatalog: {
3904
4228
  path: path.relative(root, catalogPath(root)),
3905
- present: fs8.existsSync(catalogPath(root)),
4229
+ present: fs9.existsSync(catalogPath(root)),
3906
4230
  libraries: libCount
3907
4231
  },
3908
4232
  credentials: creds,
@@ -3994,7 +4318,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
3994
4318
  console.log(chalk8.green("\u2714") + ` Created ${chalk8.bold("vibgrate.config.ts")}`);
3995
4319
  }
3996
4320
  if (opts.baseline) {
3997
- const { runBaseline } = await import('./baseline-DKPQWOQ6.js');
4321
+ const { runBaseline } = await import('./baseline-WIZAFIJS.js');
3998
4322
  await runBaseline(rootDir);
3999
4323
  }
4000
4324
  console.log("");
@@ -4104,9 +4428,9 @@ async function fileReferencesIdentifier(rootDir, relFile, identifier, readFile3)
4104
4428
  }
4105
4429
  async function defaultReadFile(absPath) {
4106
4430
  try {
4107
- const stat2 = await fs15.stat(absPath);
4431
+ const stat2 = await fs16.stat(absPath);
4108
4432
  if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES) return null;
4109
- return await fs15.readFile(absPath, "utf8");
4433
+ return await fs16.readFile(absPath, "utf8");
4110
4434
  } catch {
4111
4435
  return null;
4112
4436
  }
@@ -4299,22 +4623,22 @@ async function detectAiAssistant(rootDir) {
4299
4623
  return null;
4300
4624
  }
4301
4625
  function printAiContextPrompt(detectedAssistant) {
4302
- const teal2 = chalk8.hex("#3FB0A4");
4303
- const mint2 = chalk8.hex("#4FE3C1");
4626
+ const teal3 = chalk8.hex("#3FB0A4");
4627
+ const mint3 = chalk8.hex("#4FE3C1");
4304
4628
  const cli = resolveCliInvocation();
4305
4629
  const installArgs = detectedAssistant ? `install ${detectedAssistant}` : "install --all";
4306
4630
  const installCmd = `${cli} ${installArgs}`;
4307
4631
  const detectedNote = detectedAssistant ? chalk8.dim(` (${detectedAssistant} config detected)`) : "";
4308
4632
  console.log("");
4309
- console.log(teal2(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
4310
- console.log(teal2(" \u2502") + " " + mint2("\u25C6") + " " + chalk8.bold.white("Get AI-aware answers in your editor") + " " + teal2("\u2502"));
4311
- console.log(teal2(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
4633
+ console.log(teal3(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
4634
+ console.log(teal3(" \u2502") + " " + mint3("\u25C6") + " " + chalk8.bold.white("Get AI-aware answers in your editor") + " " + teal3("\u2502"));
4635
+ console.log(teal3(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
4312
4636
  console.log("");
4313
4637
  console.log(" " + chalk8.bold.white(installCmd) + detectedNote);
4314
4638
  console.log("");
4315
- console.log(" " + teal2("\xB7") + " " + chalk8.white("Code map in your assistant") + chalk8.dim(" \u2014 call trees, impact surfaces, import paths"));
4316
- console.log(" " + teal2("\xB7") + " " + chalk8.white("Offline drift") + chalk8.dim(" \u2014 DriftScore and upgrade priorities, right inside your editor"));
4317
- console.log(" " + teal2("\xB7") + " " + chalk8.white("Version-correct library docs") + chalk8.dim(" \u2014 pinned to your lockfile, no hallucinated APIs"));
4639
+ console.log(" " + teal3("\xB7") + " " + chalk8.white("Code map in your assistant") + chalk8.dim(" \u2014 call trees, impact surfaces, import paths"));
4640
+ console.log(" " + teal3("\xB7") + " " + chalk8.white("Offline drift") + chalk8.dim(" \u2014 DriftScore and upgrade priorities, right inside your editor"));
4641
+ console.log(" " + teal3("\xB7") + " " + chalk8.white("Version-correct library docs") + chalk8.dim(" \u2014 pinned to your lockfile, no hallucinated APIs"));
4318
4642
  console.log("");
4319
4643
  }
4320
4644
 
@@ -4751,7 +5075,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
4751
5075
  function scanStaleness(rootDir, artifactPath) {
4752
5076
  let ref;
4753
5077
  try {
4754
- ref = fs8.statSync(artifactPath).mtimeMs;
5078
+ ref = fs9.statSync(artifactPath).mtimeMs;
4755
5079
  } catch {
4756
5080
  return { stale: false };
4757
5081
  }
@@ -4763,7 +5087,7 @@ function scanStaleness(rootDir, artifactPath) {
4763
5087
  if (depth > 8 || scanned > MAX_ENTRIES) return;
4764
5088
  let entries;
4765
5089
  try {
4766
- entries = fs8.readdirSync(dir, { withFileTypes: true });
5090
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
4767
5091
  } catch {
4768
5092
  return;
4769
5093
  }
@@ -4778,7 +5102,7 @@ function scanStaleness(rootDir, artifactPath) {
4778
5102
  const abs = path.join(dir, e.name);
4779
5103
  let mtimeMs;
4780
5104
  try {
4781
- mtimeMs = fs8.statSync(abs).mtimeMs;
5105
+ mtimeMs = fs9.statSync(abs).mtimeMs;
4782
5106
  } catch {
4783
5107
  continue;
4784
5108
  }
@@ -4912,7 +5236,7 @@ function analyzeTree(root, packages) {
4912
5236
  if (depth > MAX_DEPTH || filesSeen >= MAX_FILES) return;
4913
5237
  let entries;
4914
5238
  try {
4915
- entries = fs8.readdirSync(dir, { withFileTypes: true });
5239
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
4916
5240
  } catch {
4917
5241
  return;
4918
5242
  }
@@ -4930,8 +5254,8 @@ function analyzeTree(root, packages) {
4930
5254
  const full = path.join(dir, entry.name);
4931
5255
  let content;
4932
5256
  try {
4933
- if (fs8.statSync(full).size > MAX_FILE_BYTES2) continue;
4934
- content = fs8.readFileSync(full, "utf8");
5257
+ if (fs9.statSync(full).size > MAX_FILE_BYTES2) continue;
5258
+ content = fs9.readFileSync(full, "utf8");
4935
5259
  } catch {
4936
5260
  continue;
4937
5261
  }
@@ -5299,8 +5623,8 @@ async function fetchLatestVersion() {
5299
5623
  }
5300
5624
  async function writeCache(data) {
5301
5625
  try {
5302
- await fs15.mkdir(CACHE_DIR, { recursive: true });
5303
- await fs15.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5626
+ await fs16.mkdir(CACHE_DIR, { recursive: true });
5627
+ await fs16.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5304
5628
  } catch {
5305
5629
  }
5306
5630
  }
@@ -5311,7 +5635,7 @@ function detectGlobalInstall(execPath = process.argv[1] || "", cwd = process.cwd
5311
5635
  if (!execPath) return null;
5312
5636
  let resolved = execPath;
5313
5637
  try {
5314
- resolved = fs8.realpathSync(execPath);
5638
+ resolved = fs9.realpathSync(execPath);
5315
5639
  } catch {
5316
5640
  }
5317
5641
  const p = resolved.replace(/\\/g, "/");
@@ -5840,15 +6164,15 @@ Applied the ${chosen.label} plan:`));
5840
6164
  }
5841
6165
  function formatText(artifact) {
5842
6166
  const lines = [];
5843
- const teal2 = chalk8.hex("#3FB0A4");
5844
- const mint2 = chalk8.hex("#4FE3C1");
6167
+ const teal3 = chalk8.hex("#3FB0A4");
6168
+ const mint3 = chalk8.hex("#4FE3C1");
5845
6169
  lines.push("");
5846
- lines.push(" " + teal2("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E") + mint2("\u279C"));
5847
- lines.push(" " + chalk8.dim("\u2524") + teal2("\u2502") + " " + mint2("\u25FC") + " " + mint2("\u25FC") + " " + teal2("\u2502") + chalk8.dim("\u251C") + " " + chalk8.bold.white("vibgrate"));
5848
- lines.push(" " + chalk8.dim("\u2524") + teal2("\u2502") + " " + chalk8.dim("\u2581\u2581") + " " + teal2("\u2502") + chalk8.dim("\u251C") + " " + chalk8.dim(`Code Intelligence Engine v${VERSION}`));
5849
- lines.push(" " + teal2("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
6170
+ lines.push(" " + teal3("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E") + mint3("\u279C"));
6171
+ lines.push(" " + chalk8.dim("\u2524") + teal3("\u2502") + " " + mint3("\u25FC") + " " + mint3("\u25FC") + " " + teal3("\u2502") + chalk8.dim("\u251C") + " " + chalk8.bold.white("vibgrate"));
6172
+ lines.push(" " + chalk8.dim("\u2524") + teal3("\u2502") + " " + chalk8.dim("\u2581\u2581") + " " + teal3("\u2502") + chalk8.dim("\u251C") + " " + chalk8.dim(`Code Intelligence Engine v${VERSION}`));
6173
+ lines.push(" " + teal3("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
5850
6174
  lines.push("");
5851
- lines.push(...titleBox("Vibgrate Drift Report", teal2));
6175
+ lines.push(...titleBox("Vibgrate Drift Report", teal3));
5852
6176
  lines.push("");
5853
6177
  for (const project of artifact.projects) {
5854
6178
  lines.push(chalk8.bold(` \u2500\u2500 ${project.name} `) + chalk8.dim(`(${project.type}) ${project.path}`));
@@ -7251,7 +7575,7 @@ function dispatch(argv, cwd) {
7251
7575
  }
7252
7576
  const looksLikeQuestion = /\s/.test(first) || first.endsWith("?");
7253
7577
  if (looksLikeQuestion) return ["ask", ...args];
7254
- if (fs8.existsSync(path.resolve(cwd, first))) {
7578
+ if (fs9.existsSync(path.resolve(cwd, first))) {
7255
7579
  return [hasBuildOnlyFlag(args) ? "build" : "scan", ...args];
7256
7580
  }
7257
7581
  const moved = MOVED_COMMANDS[first];