@vibgrate/cli 2026.708.1 → 2026.708.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,14 +1,14 @@
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, countPending, resolveOne, GraphIndex, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, 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, fetchHostedDocs, selectForBudget, formatForExt, verifyDeterminism, renderHtml, isModelReady } from './chunk-2J7DPIAL.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, countPending, resolveOne, GraphIndex, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, 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, fetchHostedDocs, selectForBudget, formatForExt, verifyDeterminism, renderHtml, isModelReady } from './chunk-2DJ3UXG7.js';
3
3
  import { resolvedGrammarFiles, grammarSetVersion, canonicalize } from './chunk-X5YT263H.js';
4
- import { loadAdvancedScanHook, baselineCommand } from './chunk-FMCI3MXH.js';
5
- import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-SRWMZBLG.js';
4
+ import { loadAdvancedScanHook, baselineCommand } from './chunk-JXGVMXYE.js';
5
+ import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-QUELGOCR.js';
6
6
  import './chunk-RXP66R2E.js';
7
7
  import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-GI6W53LM.js';
8
8
  import * as fs5 from 'fs';
9
- import fs5__default, { realpathSync } from 'fs';
9
+ import fs5__default, { realpathSync, existsSync, readFileSync } from 'fs';
10
10
  import * as path from 'path';
11
- import path__default from 'path';
11
+ import path__default, { join } from 'path';
12
12
  import { fileURLToPath } from 'url';
13
13
  import { Command, CommanderError, Option } from 'commander';
14
14
  import { execSync, spawn, spawnSync, execFileSync, execFile } from 'child_process';
@@ -295,7 +295,7 @@ function logoLines(root) {
295
295
  ` ${ROBOT[1]}`,
296
296
  ` ${ROBOT[2]}`,
297
297
  ` ${ROBOT[3]}`,
298
- ` ${c.bold.white("vibgrate")} ${teal("graph")} ${c.dim(`\xB7 deterministic code graph \xB7 v${VERSION}`)}`,
298
+ ` ${c.bold.white("Vibgrate")} ${teal("Graph")} ${c.dim(`\xB7 Code Intelligence Engine \xB7 v${VERSION}`)}`,
299
299
  ...root ? [` ${c.dim(root)}`] : [],
300
300
  ""
301
301
  ];
@@ -2034,23 +2034,29 @@ function registerSavings(program) {
2034
2034
  const cmd = program.command("savings").description("local, privacy-safe report of tokens/$ saved vs a grep baseline (estimates)").option("--days <n>", "window in days", "30").action(function() {
2035
2035
  const global = readGlobal(this);
2036
2036
  const days = Number(this.opts().days) || 30;
2037
- const report = readSavings(rootOf(global), days, Date.now());
2037
+ const root = rootOf(global);
2038
+ const now2 = Date.now();
2039
+ const report = readSavings(root, days, now2);
2040
+ const usage = readUsage(root, days, now2);
2038
2041
  if (global.json) {
2039
- json(report);
2042
+ json({ ...report, usage });
2040
2043
  return;
2041
2044
  }
2042
2045
  info(`${c.cyan("vg savings")} \xB7 last ${days} days ${c.dim("(local, nothing left your machine)")}`);
2043
- if (!report.enabled && report.queries === 0) {
2044
- info(c.dim(" recording is off. Enable with `vg serve --savings`, then query via MCP."));
2046
+ if (usage.totals.calls === 0) {
2047
+ info(
2048
+ c.dim(
2049
+ report.enabled ? " no calls recorded yet in this window" : " recording is off. Enable with `vg serve --savings`, then query via MCP."
2050
+ )
2051
+ );
2045
2052
  return;
2046
2053
  }
2047
- if (report.queries === 0) {
2048
- info(c.dim(" no queries recorded yet in this window"));
2049
- return;
2054
+ if (report.queries > 0) {
2055
+ info(` queries ${report.queries} \xB7 context tokens ${fmt(report.vgTokens)} (grep/read baseline \u2248 ${fmt(report.baselineTokens)}) \u2192 ${report.ratio}\xD7 fewer`);
2056
+ info(` est. cost (${report.rateLabel}): $${report.estCostVg} vs $${report.estCostBaseline} \u2192 saved \u2248 $${report.saved}`);
2057
+ info(c.dim(" estimates with stated assumptions (~4 chars/token; ~400 tokens/file baseline); scales with repo size."));
2050
2058
  }
2051
- info(` queries ${report.queries} \xB7 context tokens ${fmt(report.vgTokens)} (grep/read baseline \u2248 ${fmt(report.baselineTokens)}) \u2192 ${report.ratio}\xD7 fewer`);
2052
- info(` est. cost (${report.rateLabel}): $${report.estCostVg} vs $${report.estCostBaseline} \u2192 saved \u2248 $${report.saved}`);
2053
- info(c.dim(" estimates with stated assumptions (~4 chars/token; ~400 tokens/file baseline); scales with repo size."));
2059
+ printBreakdown(usage);
2054
2060
  });
2055
2061
  applyGlobalOptions(cmd);
2056
2062
  }
@@ -2059,6 +2065,24 @@ function fmt(n) {
2059
2065
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
2060
2066
  return String(n);
2061
2067
  }
2068
+ function printBreakdown(usage) {
2069
+ info("");
2070
+ info(c.bold(" by command") + c.dim(" (complete = full result \xB7 partial = capped/paginated \xB7 miss = no result)"));
2071
+ const nameW = Math.max(7, ...usage.commands.map((cmd) => cmd.tool.length));
2072
+ const header = " " + "command".padEnd(nameW) + ["calls", "complete", "partial", "miss", "success%"].map((h) => h.padStart(9)).join("");
2073
+ info(c.dim(header));
2074
+ for (const cmd of usage.commands) {
2075
+ info(
2076
+ " " + 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)
2077
+ );
2078
+ }
2079
+ const t = usage.totals;
2080
+ info(c.dim(" " + "\u2500".repeat(nameW + 45)));
2081
+ info(
2082
+ " " + 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)
2083
+ );
2084
+ info(c.dim(` avg success across commands: ${usage.avgSuccessPct}%`));
2085
+ }
2062
2086
  function registerLib(program) {
2063
2087
  const cmd = program.command("lib").description("library currency: version-correct, drift-annotated usage docs").argument("[args...]", "a library name, or: add <source> | publish <name> | resolve <name> | refresh").option("--name <name>", "library name for `add`").option("--version <v>", "pin the doc version for `add`/`publish`").option("--online", "(deprecated; network is on by default) allow network for add/refresh URL sources").option("-b, --budget <n>", "trim docs to ~N tokens").option("--readme <path>", "README path for `publish` (default ./README.md)").option("--dts <path>", "TypeScript declaration (.d.ts) path for `publish`").option("--language <lang>", "primary language for `publish`").option("--region <region>", "data-residency region for the hosted catalog (same as scans; default us)").option("--ingest <url>", "hosted catalog/ingest URL override (host extracted; wins over --region)").action(async function(args, opts) {
2064
2088
  const global = readGlobal(this);
@@ -2343,7 +2367,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
2343
2367
  console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold("vibgrate.config.ts")}`);
2344
2368
  }
2345
2369
  if (opts.baseline) {
2346
- const { runBaseline } = await import('./baseline-RRO4NNNF.js');
2370
+ const { runBaseline } = await import('./baseline-AUC3HAXW.js');
2347
2371
  await runBaseline(rootDir);
2348
2372
  }
2349
2373
  console.log("");
@@ -3169,11 +3193,11 @@ function estimateDriftScore(artifact, upgraded) {
3169
3193
  }
3170
3194
  return computeDriftScore(projects).score;
3171
3195
  }
3172
- function pmCommandFor(ecosystem, pkg, version, pm = "npm") {
3196
+ function pmCommandFor(ecosystem, pkg, version, pm = "npm", opts = {}) {
3173
3197
  switch (ecosystem) {
3174
3198
  case "npm":
3175
- if (pm === "pnpm") return { cmd: "pnpm", args: ["add", `${pkg}@${version}`] };
3176
- if (pm === "yarn") return { cmd: "yarn", args: ["add", `${pkg}@${version}`] };
3199
+ if (pm === "pnpm") return { cmd: "pnpm", args: opts.workspaceRoot ? ["add", "-w", `${pkg}@${version}`] : ["add", `${pkg}@${version}`] };
3200
+ if (pm === "yarn") return { cmd: "yarn", args: opts.workspaceRoot ? ["add", "-W", `${pkg}@${version}`] : ["add", `${pkg}@${version}`] };
3177
3201
  if (pm === "bun") return { cmd: "bun", args: ["add", `${pkg}@${version}`] };
3178
3202
  return { cmd: "npm", args: ["install", `${pkg}@${version}`] };
3179
3203
  case "pypi":
@@ -3200,19 +3224,37 @@ function pmCommandFor(ecosystem, pkg, version, pm = "npm") {
3200
3224
  }
3201
3225
  function defaultRun(cmd, cwd) {
3202
3226
  const res = spawnSync(cmd.cmd, cmd.args, { cwd, stdio: "inherit", shell: false });
3203
- if (res.error) return { ok: false, detail: res.error.message };
3227
+ if (res.error) {
3228
+ if (res.error.code === "ENOENT") {
3229
+ return { ok: false, toolMissing: true, detail: `${cmd.cmd} is not installed or not on PATH` };
3230
+ }
3231
+ return { ok: false, detail: res.error.message };
3232
+ }
3204
3233
  if (res.status !== 0) return { ok: false, detail: `exit ${res.status ?? "signal"}` };
3205
3234
  return { ok: true };
3206
3235
  }
3236
+ function isWorkspaceRoot(dir) {
3237
+ try {
3238
+ if (existsSync(join(dir, "pnpm-workspace.yaml"))) return true;
3239
+ const pkgPath = join(dir, "package.json");
3240
+ if (existsSync(pkgPath)) {
3241
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
3242
+ if (pkg?.workspaces) return true;
3243
+ }
3244
+ } catch {
3245
+ }
3246
+ return false;
3247
+ }
3207
3248
  function applyPlan(rootDir, upgrades, opts = {}) {
3208
3249
  const run = opts.run ?? defaultRun;
3250
+ const workspaceRoot = isWorkspaceRoot(rootDir);
3209
3251
  const results = [];
3210
3252
  for (const u of upgrades) {
3211
3253
  if (!u.to) {
3212
3254
  results.push({ package: u.package, to: u.to, status: "skipped", detail: "no target version" });
3213
3255
  continue;
3214
3256
  }
3215
- const command = pmCommandFor(u.ecosystem, u.package, u.to, opts.packageManager);
3257
+ const command = pmCommandFor(u.ecosystem, u.package, u.to, opts.packageManager, { workspaceRoot });
3216
3258
  if (!command) {
3217
3259
  results.push({ package: u.package, to: u.to, status: "manual", detail: `${u.ecosystem}: update the manifest to ${u.to} manually` });
3218
3260
  continue;
@@ -3225,8 +3267,8 @@ function applyPlan(rootDir, upgrades, opts = {}) {
3225
3267
  results.push({
3226
3268
  package: u.package,
3227
3269
  to: u.to,
3228
- status: outcome.ok ? "applied" : "failed",
3229
- detail: outcome.detail
3270
+ status: outcome.ok ? "applied" : outcome.toolMissing ? "manual" : "failed",
3271
+ detail: outcome.toolMissing && outcome.detail ? `${outcome.detail} \u2014 upgrade manually` : outcome.detail
3230
3272
  });
3231
3273
  }
3232
3274
  return results;
@@ -3466,6 +3508,44 @@ function npmPackageManager(artifact) {
3466
3508
  }
3467
3509
  return "npm";
3468
3510
  }
3511
+ function toNpmPackageManager(pm) {
3512
+ return pm === "pnpm" || pm === "yarn" || pm === "bun" ? pm : "npm";
3513
+ }
3514
+ function projectsDeclaring(rootDir, artifact, pkg, ecosystem) {
3515
+ const out2 = [];
3516
+ const seen = /* @__PURE__ */ new Set();
3517
+ for (const project of artifact.projects ?? []) {
3518
+ if (ecosystemId(project.type) !== ecosystem) continue;
3519
+ if (!(project.dependencies ?? []).some((d) => d.package === pkg)) continue;
3520
+ const dir = path.resolve(rootDir, project.path && project.path !== "." ? project.path : ".");
3521
+ if (seen.has(dir)) continue;
3522
+ seen.add(dir);
3523
+ out2.push({ dir, pm: toNpmPackageManager(project.packageManager) });
3524
+ }
3525
+ return out2;
3526
+ }
3527
+ function applyChosenPlan(rootDir, artifact, upgrades, opts) {
3528
+ const fallbackPm = npmPackageManager(artifact);
3529
+ const groups = /* @__PURE__ */ new Map();
3530
+ for (const u of upgrades) {
3531
+ let targets = projectsDeclaring(rootDir, artifact, u.package, u.ecosystem);
3532
+ if (targets.length === 0) targets = [{ dir: rootDir, pm: fallbackPm }];
3533
+ for (const t of targets) {
3534
+ const group = groups.get(t.dir) ?? { dir: t.dir, pm: t.pm, upgrades: [] };
3535
+ group.upgrades.push(u);
3536
+ groups.set(t.dir, group);
3537
+ }
3538
+ }
3539
+ const results = [];
3540
+ for (const group of groups.values()) {
3541
+ const applied = applyPlan(group.dir, group.upgrades, { dryRun: opts.dryRun, packageManager: group.pm });
3542
+ const rel3 = path.relative(rootDir, group.dir) || ".";
3543
+ for (const r of applied) {
3544
+ results.push(rel3 === "." ? r : { ...r, detail: [r.detail, `in ${rel3}`].filter(Boolean).join(" \xB7 ") });
3545
+ }
3546
+ }
3547
+ return results;
3548
+ }
3469
3549
  function promptPlanSelection(response) {
3470
3550
  const plans = response.plans.filter((p) => p.upgrades.length > 0);
3471
3551
  console.log(chalk7.bold("\nSelect a plan to apply:"));
@@ -3520,8 +3600,7 @@ async function runApplyFlow(rootDir, artifact, response, opts) {
3520
3600
  console.log(chalk7.dim("Selected plan has no upgrades."));
3521
3601
  return;
3522
3602
  }
3523
- const pm = npmPackageManager(artifact);
3524
- const results = applyPlan(rootDir, chosen.upgrades, { dryRun: opts.dryRun, packageManager: pm });
3603
+ const results = applyChosenPlan(rootDir, artifact, chosen.upgrades, { dryRun: opts.dryRun });
3525
3604
  if (opts.dryRun) {
3526
3605
  console.log(chalk7.bold(`
3527
3606
  Dry run \u2014 ${chosen.label} plan (${chosen.upgrades.length} upgrade(s)):`));
@@ -3559,7 +3638,7 @@ function formatText(artifact) {
3559
3638
  lines.push("");
3560
3639
  lines.push(" " + teal2("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E") + mint2("\u279C"));
3561
3640
  lines.push(" " + chalk7.dim("\u2524") + teal2("\u2502") + " " + mint2("\u25FC") + " " + mint2("\u25FC") + " " + teal2("\u2502") + chalk7.dim("\u251C") + " " + chalk7.bold.white("vibgrate"));
3562
- lines.push(" " + chalk7.dim("\u2524") + teal2("\u2502") + " " + chalk7.dim("\u2581\u2581") + " " + teal2("\u2502") + chalk7.dim("\u251C") + " " + chalk7.dim(`Drift Intelligence Engine v${VERSION}`));
3641
+ lines.push(" " + chalk7.dim("\u2524") + teal2("\u2502") + " " + chalk7.dim("\u2581\u2581") + " " + teal2("\u2502") + chalk7.dim("\u251C") + " " + chalk7.dim(`Code Intelligence Engine v${VERSION}`));
3563
3642
  lines.push(" " + teal2("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
3564
3643
  lines.push("");
3565
3644
  lines.push(...titleBox("Vibgrate Drift Report", teal2));
@@ -4532,11 +4611,23 @@ async function detectPackageManager(cwd) {
4532
4611
  if (await pathExists(path.join(cwd, "yarn.lock"))) return "yarn";
4533
4612
  return "npm";
4534
4613
  }
4535
- function getInstallCommand(pm, pkg, version, isDev) {
4614
+ async function detectWorkspaceRoot(cwd) {
4615
+ if (await pathExists(path.join(cwd, "pnpm-workspace.yaml"))) return true;
4616
+ try {
4617
+ const pkgPath = path.join(cwd, "package.json");
4618
+ const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
4619
+ const pkg = JSON.parse(raw);
4620
+ return pkg.workspaces != null;
4621
+ } catch {
4622
+ return false;
4623
+ }
4624
+ }
4625
+ function getInstallCommand(pm, pkg, version, isDev, opts = {}) {
4536
4626
  const spec = `${pkg}@${version}`;
4627
+ const ws = opts.workspaceRoot && pm === "pnpm" ? " -w" : "";
4537
4628
  switch (pm) {
4538
4629
  case "pnpm":
4539
- return isDev ? `pnpm add -D ${spec}` : `pnpm add ${spec}`;
4630
+ return isDev ? `pnpm add${ws} -D ${spec}` : `pnpm add${ws} ${spec}`;
4540
4631
  case "yarn":
4541
4632
  return isDev ? `yarn add --dev ${spec}` : `yarn add ${spec}`;
4542
4633
  case "bun":
@@ -4556,45 +4647,72 @@ async function isDevDependency(cwd) {
4556
4647
  return true;
4557
4648
  }
4558
4649
  }
4559
- var updateCommand = new Command("update").description("Update vibgrate to the latest version").option("--check", "Only check for updates, do not install").option("--pm <manager>", "Package manager to use (npm, pnpm, yarn, bun)").option("--global", "Update global installation").action(async (opts) => {
4560
- console.log(chalk7.dim(`Current version: ${VERSION}`));
4561
- console.log(chalk7.dim("Checking npm registry..."));
4562
- const latest = await fetchLatestVersion();
4563
- if (!latest) {
4564
- console.error(chalk7.red("Could not reach the npm registry. Check your network connection."));
4565
- process.exit(1);
4566
- }
4567
- const semver2 = await import('semver');
4568
- if (!semver2.gt(latest, VERSION)) {
4569
- console.log(chalk7.green("\u2714") + ` You are on the latest version (${VERSION}).`);
4570
- return;
4571
- }
4572
- console.log(chalk7.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
4573
- if (opts.check) {
4574
- console.log(chalk7.dim('Run "vibgrate update" to install.'));
4575
- return;
4576
- }
4577
- const cwd = process.cwd();
4578
- const globalPm = detectGlobalInstall();
4579
- const isGlobal = opts.global || globalPm !== null;
4580
- const pm = opts.pm || (globalPm ?? await detectPackageManager(cwd));
4581
- let cmd;
4582
- if (isGlobal) {
4583
- cmd = getGlobalUpdateCommand(pm, "@vibgrate/cli", latest);
4584
- console.log(chalk7.dim(`Updating global installation with ${pm}: ${cmd}`));
4585
- } else {
4586
- const isDev = await isDevDependency(cwd);
4587
- cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev);
4588
- console.log(chalk7.dim(`Using ${pm}: ${cmd}`));
4589
- }
4590
- try {
4591
- execSync(cmd, { cwd, stdio: "inherit" });
4592
- console.log(chalk7.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
4593
- } catch {
4594
- console.error(chalk7.red(`Update failed. Run manually: ${cmd}`));
4595
- process.exit(1);
4650
+ async function confirmWorkspaceRoot(pm) {
4651
+ if (!(process.stdin.isTTY && process.stdout.isTTY)) return false;
4652
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4653
+ return new Promise((resolve16) => {
4654
+ rl.question(chalk7.yellow(`Update the ${pm} workspace root with -w? [y/N]: `), (ans) => {
4655
+ rl.close();
4656
+ const t = ans.trim().toLowerCase();
4657
+ resolve16(t === "y" || t === "yes");
4658
+ });
4659
+ });
4660
+ }
4661
+ var updateCommand = new Command("update").description("Update vibgrate to the latest version").option("--check", "Only check for updates, do not install").option("--pm <manager>", "Package manager to use (npm, pnpm, yarn, bun)").option("--global", "Update global installation").option("-y, --yes", "Skip confirmation prompts (e.g. installing at a workspace root)").option("-w, --workspace-root", "Allow updating the pnpm workspace root (implies --yes for that prompt)").action(
4662
+ async (opts) => {
4663
+ console.log(chalk7.dim(`Current version: ${VERSION}`));
4664
+ console.log(chalk7.dim("Checking npm registry..."));
4665
+ const latest = await fetchLatestVersion();
4666
+ if (!latest) {
4667
+ console.error(chalk7.red("Could not reach the npm registry. Check your network connection."));
4668
+ process.exit(1);
4669
+ }
4670
+ const semver2 = await import('semver');
4671
+ if (!semver2.gt(latest, VERSION)) {
4672
+ console.log(chalk7.green("\u2714") + ` You are on the latest version (${VERSION}).`);
4673
+ return;
4674
+ }
4675
+ console.log(chalk7.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
4676
+ if (opts.check) {
4677
+ console.log(chalk7.dim('Run "vg update" to install.'));
4678
+ return;
4679
+ }
4680
+ const cwd = process.cwd();
4681
+ const globalPm = detectGlobalInstall();
4682
+ const isGlobal = opts.global || globalPm !== null;
4683
+ const pm = opts.pm || (globalPm ?? await detectPackageManager(cwd));
4684
+ let cmd;
4685
+ if (isGlobal) {
4686
+ cmd = getGlobalUpdateCommand(pm, "@vibgrate/cli", latest);
4687
+ console.log(chalk7.dim(`Updating global installation with ${pm}: ${cmd}`));
4688
+ } else {
4689
+ const isDev = await isDevDependency(cwd);
4690
+ let workspaceRoot = false;
4691
+ if (pm === "pnpm" && await detectWorkspaceRoot(cwd)) {
4692
+ console.log(
4693
+ chalk7.yellow("Detected a pnpm workspace root.") + chalk7.dim(" Installing here needs the -w flag and updates the root package.json.")
4694
+ );
4695
+ const proceed = opts.yes || opts.workspaceRoot || await confirmWorkspaceRoot(pm);
4696
+ if (!proceed) {
4697
+ const rootCmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev, { workspaceRoot: true });
4698
+ console.log(chalk7.dim(`Skipped. To update the workspace root, run: ${rootCmd}`));
4699
+ console.log(chalk7.dim('or re-run "vg update --yes" (or --workspace-root) to let vg do it.'));
4700
+ return;
4701
+ }
4702
+ workspaceRoot = true;
4703
+ }
4704
+ cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev, { workspaceRoot });
4705
+ console.log(chalk7.dim(`Using ${pm}: ${cmd}`));
4706
+ }
4707
+ try {
4708
+ execSync(cmd, { cwd, stdio: "inherit" });
4709
+ console.log(chalk7.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
4710
+ } catch {
4711
+ console.error(chalk7.red(`Update failed. Run manually: ${cmd}`));
4712
+ process.exit(1);
4713
+ }
4596
4714
  }
4597
- });
4715
+ );
4598
4716
  var OPENVEX_CONTEXT = "https://openvex.dev/ns/v0.2.0";
4599
4717
  var VEX_STATUSES = ["not_affected", "affected", "fixed", "under_investigation"];
4600
4718
  var VEX_JUSTIFICATIONS = [