@vibgrate/cli 2026.4.30 → 2026.6.5

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,34 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- baselineCommand
4
- } from "./chunk-2VJCLUTR.js";
5
- import {
6
- VERSION,
7
- computeHmac,
8
- dsnCommand,
9
- formatMarkdown,
10
- formatText,
11
- parseDsn,
12
- pushCommand,
13
- scanCommand,
14
- writeDefaultConfig
15
- } from "./chunk-XCIPC2J7.js";
16
2
  import {
17
3
  Semaphore,
4
+ baselineCommand,
18
5
  ensureDir,
19
6
  pathExists,
20
7
  readJsonFile,
21
8
  writeTextFile
22
- } from "./chunk-JQHUH6A3.js";
9
+ } from "./chunk-42GW43JE.js";
10
+ import {
11
+ require_semver
12
+ } from "./chunk-74ZJFYEM.js";
13
+ import {
14
+ __toESM
15
+ } from "./chunk-JSBRDJBE.js";
23
16
 
24
17
  // src/cli.ts
25
- import { Command as Command8 } from "commander";
26
- import chalk8 from "chalk";
18
+ import { Command as Command11 } from "commander";
19
+ import chalk12 from "chalk";
27
20
 
28
21
  // src/commands/init.ts
29
22
  import * as path from "path";
30
23
  import { Command } from "commander";
31
24
  import chalk from "chalk";
25
+ import { writeDefaultConfig } from "@vibgrate/core";
32
26
  var initCommand = new Command("init").description("Initialize vibgrate in a project").argument("[path]", "Path to initialize", ".").option("--baseline", "Create initial baseline after init").option("--yes", "Skip confirmation prompts").action(async (targetPath, opts) => {
33
27
  const rootDir = path.resolve(targetPath);
34
28
  const vibgrateDir = path.join(rootDir, ".vibgrate");
@@ -42,7 +36,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
42
36
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
43
37
  }
44
38
  if (opts.baseline) {
45
- const { runBaseline } = await import("./baseline-QZZXBT74.js");
39
+ const { runBaseline } = await import("./baseline-FPO7HS7Y.js");
46
40
  await runBaseline(rootDir);
47
41
  }
48
42
  console.log("");
@@ -52,15 +46,1044 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
52
46
  console.log("");
53
47
  });
54
48
 
55
- // src/commands/report.ts
49
+ // src/commands/scan.ts
50
+ import * as path3 from "path";
51
+ import { Command as Command3 } from "commander";
52
+ import chalk3 from "chalk";
53
+ import {
54
+ runScan,
55
+ pathExists as pathExists2,
56
+ parseDsn,
57
+ prepareCompressedUpload,
58
+ fetchScanPreflight,
59
+ computeRepoFingerprint,
60
+ detectVcs,
61
+ resolveRepositoryName
62
+ } from "@vibgrate/core";
63
+
64
+ // src/version.ts
65
+ import { createRequire } from "module";
66
+ var require2 = createRequire(import.meta.url);
67
+ var pkg = require2("../package.json");
68
+ var VERSION = pkg.version;
69
+
70
+ // src/commands/dsn.ts
71
+ import * as crypto from "crypto";
56
72
  import * as path2 from "path";
57
73
  import { Command as Command2 } from "commander";
58
74
  import chalk2 from "chalk";
59
- var reportCommand = new Command2("report").description("Generate a drift report from a scan artifact").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--format <format>", "Output format (md|text|json)", "text").action(async (opts) => {
60
- const artifactPath = path2.resolve(opts.in);
75
+ var REGION_HOSTS = {
76
+ us: "us.ingest.vibgrate.com",
77
+ eu: "eu.ingest.vibgrate.com"
78
+ };
79
+ function resolveIngestHost(region, ingest) {
80
+ if (ingest) {
81
+ try {
82
+ return new URL(ingest).host;
83
+ } catch {
84
+ throw new Error(`Invalid ingest URL: ${ingest}`);
85
+ }
86
+ }
87
+ const r = (region ?? "us").toLowerCase();
88
+ const host = REGION_HOSTS[r];
89
+ if (!host) {
90
+ throw new Error(`Unknown region "${r}". Supported: ${Object.keys(REGION_HOSTS).join(", ")}`);
91
+ }
92
+ return host;
93
+ }
94
+ async function provisionDsn(keyId, secret, workspaceId, ingestHost) {
95
+ const url = `https://${ingestHost}/v1/provision`;
96
+ try {
97
+ const response = await fetch(url, {
98
+ method: "POST",
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ "Connection": "close"
102
+ // Prevent keep-alive delays on exit
103
+ },
104
+ body: JSON.stringify({ keyId, secret, workspaceId })
105
+ });
106
+ if (!response.ok) {
107
+ const result = await response.json();
108
+ return { success: false, error: result.error || `HTTP ${response.status}` };
109
+ }
110
+ return { success: true };
111
+ } catch (e) {
112
+ return { success: false, error: e instanceof Error ? e.message : String(e) };
113
+ }
114
+ }
115
+ var dsnCommand = new Command2("dsn").description("Manage DSN tokens");
116
+ dsnCommand.command("create").description("Create a new DSN token").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", "Data residency region (us, eu)", "us").requiredOption("--workspace <id>", 'Workspace ID (use "new" to auto-generate)').option("--write <path>", "Write DSN to file").action(async (opts) => {
117
+ const keyId = crypto.randomBytes(12).toString("hex");
118
+ const secret = crypto.randomBytes(32).toString("hex");
119
+ let ingestHost;
120
+ try {
121
+ ingestHost = resolveIngestHost(opts.region, opts.ingest);
122
+ } catch (e) {
123
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
124
+ process.exit(1);
125
+ }
126
+ let workspaceId = opts.workspace;
127
+ const isNewWorkspace = opts.workspace.toLowerCase() === "new";
128
+ if (isNewWorkspace) {
129
+ workspaceId = crypto.randomBytes(8).toString("hex");
130
+ console.log(chalk2.dim(`Provisioning new workspace ${workspaceId}...`));
131
+ }
132
+ if (isNewWorkspace) {
133
+ const result = await provisionDsn(keyId, secret, workspaceId, ingestHost);
134
+ if (!result.success) {
135
+ console.error(chalk2.red(`Failed to provision DSN: ${result.error}`));
136
+ process.exit(1);
137
+ }
138
+ }
139
+ const dsn = `vibgrate+https://${keyId}:${secret}@${ingestHost}/${workspaceId}`;
140
+ console.log(chalk2.green("\u2714") + " DSN created");
141
+ console.log("");
142
+ console.log(chalk2.bold("DSN:"));
143
+ console.log(` ${dsn}`);
144
+ console.log("");
145
+ console.log(chalk2.bold("Key ID:"));
146
+ console.log(` ${keyId}`);
147
+ if (isNewWorkspace) {
148
+ console.log("");
149
+ console.log(chalk2.bold("Workspace ID:"));
150
+ console.log(` ${workspaceId}`);
151
+ }
152
+ console.log("");
153
+ console.log(chalk2.dim("Set this as VIBGRATE_DSN in your CI environment."));
154
+ if (!isNewWorkspace) {
155
+ console.log(chalk2.dim("The secret must be registered on your Vibgrate ingest API."));
156
+ }
157
+ if (opts.write) {
158
+ const writePath = path2.resolve(opts.write);
159
+ await writeTextFile(writePath, dsn + "\n");
160
+ console.log("");
161
+ console.log(chalk2.green("\u2714") + ` DSN written to ${opts.write}`);
162
+ console.log(chalk2.yellow("\u26A0") + " Add this file to .gitignore!");
163
+ }
164
+ });
165
+
166
+ // src/utils/ingest-id-output.ts
167
+ function emitIngestIdLine(ingestId, options) {
168
+ console.log(`VIBGRATE_INGEST_ID=${ingestId}`);
169
+ if (options?.unchanged) {
170
+ console.log("VIBGRATE_INGEST_UNCHANGED=1");
171
+ }
172
+ }
173
+
174
+ // src/commands/scan.ts
175
+ async function autoPush(artifact, rootDir, opts) {
176
+ const dsn = opts.dsn || process.env.VIBGRATE_DSN;
177
+ if (!dsn) {
178
+ console.error(chalk3.red("No DSN provided for push."));
179
+ console.error(chalk3.dim("Set VIBGRATE_DSN environment variable or use --dsn flag."));
180
+ if (opts.strict) process.exit(1);
181
+ return;
182
+ }
183
+ const parsed = parseDsn(dsn);
184
+ if (!parsed) {
185
+ console.error(chalk3.red("Invalid DSN format."));
186
+ if (opts.strict) process.exit(1);
187
+ return;
188
+ }
189
+ const { body, contentEncoding } = await prepareCompressedUpload(artifact);
190
+ const timestamp = String(Date.now());
191
+ let host = parsed.host;
192
+ if (opts.region) {
193
+ try {
194
+ host = resolveIngestHost(opts.region);
195
+ } catch (e) {
196
+ console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
197
+ if (opts.strict) process.exit(1);
198
+ return;
199
+ }
200
+ }
201
+ const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
202
+ const originalSize = JSON.stringify(artifact).length;
203
+ const compressedSize = body.length;
204
+ const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
205
+ console.log(chalk3.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
206
+ try {
207
+ const response = await fetch(url, {
208
+ method: "POST",
209
+ headers: {
210
+ "Content-Type": "application/json",
211
+ "Content-Encoding": contentEncoding,
212
+ "X-Vibgrate-Timestamp": timestamp,
213
+ "Authorization": `VibgrateDSN ${parsed.keyId}:${parsed.secret}`,
214
+ "Connection": "close"
215
+ // Prevent keep-alive delays on exit
216
+ },
217
+ body
218
+ });
219
+ if (!response.ok) {
220
+ const text = await response.text();
221
+ throw new Error(`HTTP ${response.status}: ${text}`);
222
+ }
223
+ const result = await response.json();
224
+ if (result.unchanged) {
225
+ console.log(
226
+ chalk3.green("\u2714") + ` Repository unchanged since ${result.lastScannedAt ?? "last scan"} \u2014 skipped upload (no credit used).`
227
+ );
228
+ if (result.previousIngestId) {
229
+ emitIngestIdLine(result.previousIngestId, { unchanged: true });
230
+ const dashUrl = `https://dash.vibgrate.com/${parsed.workspaceId}/scan/${result.previousIngestId}`;
231
+ console.log(chalk3.dim(` Previous report: ${dashUrl}`));
232
+ } else if (opts.strict) {
233
+ console.error(chalk3.red("Repository unchanged but no previous ingest id returned."));
234
+ process.exit(1);
235
+ }
236
+ return;
237
+ }
238
+ console.log(chalk3.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
239
+ if (result.ingestId) {
240
+ emitIngestIdLine(result.ingestId);
241
+ const dashUrl = `https://dash.vibgrate.com/${parsed.workspaceId}/scan/${result.ingestId}`;
242
+ const CLEAR_LINE = process.platform === "win32" ? "\x1B[0G\x1B[2K" : "";
243
+ console.log("");
244
+ console.log(CLEAR_LINE + chalk3.dim(" Processing continues in the background. Results available shortly."));
245
+ console.log("");
246
+ console.log(CLEAR_LINE + chalk3.cyan("\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\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"));
247
+ console.log(CLEAR_LINE + chalk3.bold(" \u{1F4CA} View Scan Report"));
248
+ console.log(CLEAR_LINE + " " + chalk3.underline.cyan(dashUrl));
249
+ console.log(CLEAR_LINE + chalk3.cyan("\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\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"));
250
+ console.log("");
251
+ console.log("");
252
+ }
253
+ } catch (e) {
254
+ const msg = e instanceof Error ? e.message : String(e);
255
+ console.error(chalk3.red(`Upload failed: ${msg}`));
256
+ if (opts.strict) process.exit(1);
257
+ }
258
+ }
259
+ function parseNonNegativeNumber(value, label) {
260
+ if (value === void 0) return void 0;
261
+ const parsed = Number(value);
262
+ if (!Number.isFinite(parsed) || parsed < 0) {
263
+ throw new Error(`${label} must be a non-negative number.`);
264
+ }
265
+ return parsed;
266
+ }
267
+ var scanCommand = new Command3("scan").description("Scan a project for upgrade drift").argument("[path]", "Path to scan", ".").option("--out <file>", "Output file path").option("--format <format>", "Output format (text|json|sarif|md)", "text").option("--fail-on <level>", "Fail on warn or error").option("--baseline <file>", "Compare against baseline").option("--changed-only", "Only scan changed files").option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--project-scan-timeout <seconds>", "Per-project scan timeout in seconds (default: 180)").option("--drift-budget <score>", "Fail if drift score is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").action(async (targetPath, opts) => {
268
+ const rootDir = path3.resolve(targetPath);
269
+ if (!await pathExists2(rootDir)) {
270
+ console.error(chalk3.red(`Path does not exist: ${rootDir}`));
271
+ process.exit(1);
272
+ }
273
+ const hasDsn = !!(opts.dsn || process.env.VIBGRATE_DSN);
274
+ const willPush = !opts.offline && (opts.push || hasDsn);
275
+ if (willPush && hasDsn) {
276
+ const dsn = opts.dsn || process.env.VIBGRATE_DSN;
277
+ const parsed = parseDsn(dsn);
278
+ if (parsed) {
279
+ const ingestHost = opts.region ? resolveIngestHost(opts.region) : parsed.host;
280
+ const vcs = await detectVcs(rootDir);
281
+ const fingerprint = await computeRepoFingerprint(rootDir, vcs);
282
+ const repositoryName = await resolveRepositoryName(rootDir);
283
+ try {
284
+ const preflight = await fetchScanPreflight(parsed, ingestHost, {
285
+ repositoryName,
286
+ vcsSha: fingerprint.vcsSha
287
+ });
288
+ if (preflight.status === "error" || !preflight.scans.allowed) {
289
+ console.error(chalk3.red(preflight.error ?? "Scan ingestion not allowed for this workspace."));
290
+ console.error(
291
+ chalk3.dim(
292
+ `Credits: ${preflight.scans.used}/${preflight.scans.limit} (${preflight.plan.label} plan)`
293
+ )
294
+ );
295
+ if (opts.strict) process.exit(1);
296
+ process.exit(1);
297
+ }
298
+ console.log(
299
+ chalk3.dim(
300
+ `Plan: ${preflight.plan.label} \u2014 scan credits ${preflight.scans.used}/${preflight.scans.limit} this month`
301
+ )
302
+ );
303
+ if (preflight.repository?.unchanged) {
304
+ console.log(
305
+ chalk3.green("\u2714") + ` Repository unchanged at ${preflight.repository.lastVcsSha?.slice(0, 7) ?? "same revision"} \u2014 skipping scan.`
306
+ );
307
+ if (preflight.repository.lastIngestId) {
308
+ emitIngestIdLine(preflight.repository.lastIngestId, { unchanged: true });
309
+ const dashUrl = `https://dash.vibgrate.com/${parsed.workspaceId}/scan/${preflight.repository.lastIngestId}`;
310
+ console.log(chalk3.dim(` Latest report: ${dashUrl}`));
311
+ } else if (opts.strict) {
312
+ console.error(chalk3.red("Repository unchanged but no previous ingest id available."));
313
+ process.exit(1);
314
+ }
315
+ return;
316
+ }
317
+ } catch (e) {
318
+ const msg = e instanceof Error ? e.message : String(e);
319
+ console.error(chalk3.yellow(`Preflight check failed: ${msg}`));
320
+ if (opts.strict) process.exit(1);
321
+ }
322
+ }
323
+ }
324
+ const scanOpts = {
325
+ vibgrateVersion: VERSION,
326
+ out: opts.out,
327
+ format: opts.format || "text",
328
+ failOn: opts.failOn,
329
+ baseline: opts.baseline,
330
+ changedOnly: opts.changedOnly,
331
+ concurrency: parseInt(opts.concurrency, 10) || 8,
332
+ push: opts.push,
333
+ dsn: opts.dsn,
334
+ region: opts.region,
335
+ strict: opts.strict,
336
+ uiPurpose: opts.uiPurpose,
337
+ noLocalArtifacts: opts.noLocalArtifacts,
338
+ maxPrivacy: opts.maxPrivacy,
339
+ offline: opts.offline,
340
+ packageManifest: opts.packageManifest,
341
+ driftBudget: parseNonNegativeNumber(opts.driftBudget, "--drift-budget"),
342
+ driftWorseningPercent: parseNonNegativeNumber(opts.driftWorsening, "--drift-worsening"),
343
+ projectScanTimeout: opts.projectScanTimeout ? parseInt(opts.projectScanTimeout, 10) || void 0 : void 0
344
+ };
345
+ const artifact = await runScan(rootDir, scanOpts);
346
+ if (opts.failOn) {
347
+ const hasErrors = artifact.findings.some((f) => f.level === "error");
348
+ const hasWarnings = artifact.findings.some((f) => f.level === "warning");
349
+ if (opts.failOn === "error" && hasErrors) {
350
+ console.error(chalk3.red(`
351
+ Failing: ${artifact.findings.filter((f) => f.level === "error").length} error finding(s) detected.`));
352
+ process.exit(2);
353
+ }
354
+ if (opts.failOn === "warn" && (hasErrors || hasWarnings)) {
355
+ console.error(chalk3.red(`
356
+ Failing: findings detected at warn level or above.`));
357
+ process.exit(2);
358
+ }
359
+ }
360
+ if (scanOpts.driftBudget !== void 0 && artifact.drift.score > scanOpts.driftBudget) {
361
+ console.error(chalk3.red(`
362
+ Failing fitness function: drift score ${artifact.drift.score}/100 exceeds budget ${scanOpts.driftBudget}.`));
363
+ process.exit(2);
364
+ }
365
+ if (scanOpts.driftWorseningPercent !== void 0) {
366
+ if (artifact.delta === void 0) {
367
+ console.error(chalk3.red("\nFailing fitness function: --drift-worsening requires --baseline to compare against previous drift."));
368
+ process.exit(2);
369
+ }
370
+ if (artifact.delta > 0) {
371
+ const baselineScore = artifact.drift.score - artifact.delta;
372
+ const denominator = Math.max(Math.abs(baselineScore), 1e-4);
373
+ const worseningPercent = artifact.delta / denominator * 100;
374
+ if (worseningPercent > scanOpts.driftWorseningPercent) {
375
+ console.error(chalk3.red(`
376
+ Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (threshold ${scanOpts.driftWorseningPercent}%).`));
377
+ process.exit(2);
378
+ }
379
+ }
380
+ }
381
+ if (willPush) {
382
+ await autoPush(artifact, rootDir, scanOpts);
383
+ }
384
+ });
385
+
386
+ // src/commands/report.ts
387
+ import * as path4 from "path";
388
+ import { Command as Command4 } from "commander";
389
+ import chalk5 from "chalk";
390
+
391
+ // src/formatters/text.ts
392
+ import chalk4 from "chalk";
393
+ function formatText(artifact) {
394
+ const lines = [];
395
+ lines.push("");
396
+ lines.push(chalk4.cyan(" \u256D\u2500\u2500\u2500\u256E") + chalk4.greenBright("\u279C"));
397
+ lines.push(chalk4.cyan(" \u256D\u2524") + chalk4.greenBright("\u25C9 \u25C9") + chalk4.cyan("\u251C\u256E") + " " + chalk4.bold.white("V I B G R A T E"));
398
+ lines.push(chalk4.cyan(" \u2570\u2524") + chalk4.dim("\u2500\u2500\u2500") + chalk4.cyan("\u251C\u256F") + " " + chalk4.dim(`Drift Intelligence Engine v${VERSION}`));
399
+ lines.push(chalk4.cyan(" \u2570\u2500\u2500\u2500\u256F"));
400
+ lines.push("");
401
+ lines.push(chalk4.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
402
+ lines.push(chalk4.bold.cyan("\u2551 Vibgrate Drift Report \u2551"));
403
+ lines.push(chalk4.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
404
+ lines.push("");
405
+ for (const project of artifact.projects) {
406
+ lines.push(chalk4.bold(` \u2500\u2500 ${project.name} `) + chalk4.dim(`(${project.type}) ${project.path}`));
407
+ if (project.runtime) {
408
+ const behindStr = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? chalk4.yellow(` (${project.runtimeMajorsBehind} major${project.runtimeMajorsBehind > 1 ? "s" : ""} behind)`) : chalk4.green(" (current)");
409
+ lines.push(` Runtime: ${project.runtime}${behindStr}`);
410
+ }
411
+ if (project.targetFramework) {
412
+ lines.push(` Target: ${project.targetFramework}`);
413
+ }
414
+ if (project.frameworks.length > 0) {
415
+ lines.push(" Frameworks:");
416
+ for (const fw of project.frameworks) {
417
+ const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? chalk4.green("current") : chalk4.yellow(`${fw.majorsBehind} behind`) : chalk4.dim("unknown");
418
+ lines.push(` ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
419
+ }
420
+ }
421
+ const b = project.dependencyAgeBuckets;
422
+ const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
423
+ if (total > 0) {
424
+ lines.push(" Dependencies:");
425
+ lines.push(` ${chalk4.green(`${b.current} current`)} ${chalk4.yellow(`${b.oneBehind} 1-behind`)} ${chalk4.red(`${b.twoPlusBehind} 2+ behind`)} ${chalk4.dim(`${b.unknown} unknown`)}`);
426
+ }
427
+ lines.push("");
428
+ }
429
+ if (artifact.delta !== void 0) {
430
+ const deltaStr = artifact.delta > 0 ? chalk4.green(`+${artifact.delta}`) : artifact.delta < 0 ? chalk4.red(`${artifact.delta}`) : chalk4.dim("0");
431
+ lines.push(chalk4.bold(" Drift Delta: ") + deltaStr + " (vs baseline)");
432
+ lines.push("");
433
+ }
434
+ if (artifact.extended) {
435
+ lines.push(...formatExtended(artifact.extended));
436
+ }
437
+ if (artifact.findings.length > 0) {
438
+ const errors = artifact.findings.filter((f) => f.level === "error");
439
+ const warnings = artifact.findings.filter((f) => f.level === "warning");
440
+ const notes = artifact.findings.filter((f) => f.level === "note");
441
+ const summary = [
442
+ errors.length > 0 ? chalk4.red(`${errors.length} error${errors.length !== 1 ? "s" : ""}`) : "",
443
+ warnings.length > 0 ? chalk4.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}`) : "",
444
+ notes.length > 0 ? chalk4.blue(`${notes.length} note${notes.length !== 1 ? "s" : ""}`) : ""
445
+ ].filter(Boolean).join(chalk4.dim(", "));
446
+ lines.push(chalk4.bold.underline(` Findings`) + chalk4.dim(` (${summary})`));
447
+ for (const f of artifact.findings) {
448
+ const icon = f.level === "error" ? chalk4.red("\u2716") : f.level === "warning" ? chalk4.yellow("\u26A0") : chalk4.blue("\u2139");
449
+ lines.push(` ${icon} ${f.message}`);
450
+ lines.push(chalk4.dim(` ${f.ruleId} in ${f.location}`));
451
+ }
452
+ lines.push("");
453
+ }
454
+ const actions = generatePriorityActions(artifact);
455
+ if (actions.length > 0) {
456
+ lines.push(chalk4.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
457
+ lines.push(chalk4.bold.cyan("\u2551 Top Priority Actions \u2551"));
458
+ lines.push(chalk4.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
459
+ lines.push("");
460
+ for (let i = 0; i < actions.length; i++) {
461
+ const a = actions[i];
462
+ const num = chalk4.bold.cyan(` ${i + 1}.`);
463
+ lines.push(`${num} ${chalk4.bold(a.title)}`);
464
+ lines.push(chalk4.dim(` ${a.explanation}`));
465
+ if (a.impact) lines.push(` Impact: ${chalk4.green(a.impact)}`);
466
+ lines.push("");
467
+ }
468
+ }
469
+ if (artifact.extended?.architecture) {
470
+ lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
471
+ }
472
+ if (artifact.solutions && artifact.solutions.length > 0) {
473
+ lines.push(chalk4.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
474
+ lines.push(chalk4.bold.cyan("\u2551 Solution Drift Summary \u2551"));
475
+ lines.push(chalk4.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
476
+ lines.push("");
477
+ for (const solution of artifact.solutions) {
478
+ const solScore = solution.drift?.score;
479
+ const color = typeof solScore === "number" ? solScore >= 70 ? chalk4.green : solScore >= 40 ? chalk4.yellow : chalk4.red : chalk4.dim;
480
+ lines.push(` \u2022 ${solution.name} (${solution.projectPaths.length} projects) \u2014 ${typeof solScore === "number" ? color(`${solScore}/100`) : chalk4.dim("n/a")}`);
481
+ }
482
+ lines.push("");
483
+ }
484
+ const scoreColor = artifact.drift.score >= 70 ? chalk4.green : artifact.drift.score >= 40 ? chalk4.yellow : chalk4.red;
485
+ lines.push(chalk4.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
486
+ lines.push(chalk4.bold.cyan("\u2551 Drift Score Summary \u2551"));
487
+ lines.push(chalk4.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
488
+ lines.push("");
489
+ lines.push(chalk4.bold(" Drift Score: ") + scoreColor.bold(`${artifact.drift.score}/100`));
490
+ lines.push(chalk4.bold(" Risk Level: ") + riskBadge(artifact.drift.riskLevel));
491
+ lines.push(chalk4.bold(" Projects: ") + `${artifact.projects.length}`);
492
+ if (artifact.vcs) {
493
+ const vcsParts = [artifact.vcs.type];
494
+ if (artifact.vcs.branch) vcsParts.push(artifact.vcs.branch);
495
+ if (artifact.vcs.shortSha) vcsParts.push(chalk4.dim(artifact.vcs.shortSha));
496
+ lines.push(chalk4.bold(" VCS: ") + vcsParts.join(" "));
497
+ }
498
+ lines.push("");
499
+ const m = new Set(artifact.drift.measured ?? ["runtime", "framework", "dependency", "eol"]);
500
+ lines.push(" " + chalk4.bold.underline("Score Breakdown"));
501
+ lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) : chalk4.dim("n/a")}`);
502
+ lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) : chalk4.dim("n/a")}`);
503
+ lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) : chalk4.dim("n/a")}`);
504
+ lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) : chalk4.dim("n/a")}`);
505
+ lines.push("");
506
+ const scannedParts = [`Scanned at ${artifact.timestamp}`];
507
+ if (artifact.durationMs !== void 0) {
508
+ const secs = (artifact.durationMs / 1e3).toFixed(1);
509
+ scannedParts.push(`${secs}s`);
510
+ }
511
+ if (artifact.filesScanned !== void 0) {
512
+ scannedParts.push(`${artifact.filesScanned} file${artifact.filesScanned !== 1 ? "s" : ""} scanned`);
513
+ }
514
+ if (artifact.treeSummary) {
515
+ scannedParts.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files`);
516
+ scannedParts.push(`${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
517
+ }
518
+ lines.push(chalk4.dim(` ${scannedParts.join(" \xB7 ")}`));
519
+ lines.push("");
520
+ return lines.join("\n");
521
+ }
522
+ function riskBadge(level) {
523
+ switch (level) {
524
+ case "low":
525
+ return chalk4.bgGreen.black(" LOW ");
526
+ case "moderate":
527
+ return chalk4.bgYellow.black(" MODERATE ");
528
+ case "high":
529
+ return chalk4.bgRed.white(" HIGH ");
530
+ default:
531
+ return level;
532
+ }
533
+ }
534
+ function scoreBar(score) {
535
+ const width = 20;
536
+ const filled = Math.round(score / 100 * width);
537
+ const empty = width - filled;
538
+ const color = score >= 70 ? chalk4.green : score >= 40 ? chalk4.yellow : chalk4.red;
539
+ return color("\u2588".repeat(filled)) + chalk4.dim("\u2591".repeat(empty)) + ` ${Math.round(score)}`;
540
+ }
541
+ var CATEGORY_LABELS = {
542
+ frontend: "Frontend",
543
+ metaFrameworks: "Meta-frameworks",
544
+ bundlers: "Bundlers",
545
+ css: "CSS / UI",
546
+ backend: "Backend",
547
+ orm: "ORM / Database",
548
+ testing: "Testing",
549
+ lintFormat: "Lint & Format",
550
+ apiMessaging: "API & Messaging",
551
+ observability: "Observability",
552
+ payment: "Payment",
553
+ auth: "Auth",
554
+ email: "Email",
555
+ cloud: "Cloud",
556
+ databases: "Databases",
557
+ messaging: "Messaging",
558
+ crm: "CRM & Comms",
559
+ storage: "Storage",
560
+ search: "Search & AI"
561
+ };
562
+ function formatExtended(ext) {
563
+ const lines = [];
564
+ if (ext.toolingInventory) {
565
+ const inv = ext.toolingInventory;
566
+ const categories = Object.entries(inv).filter(([, items]) => items.length > 0);
567
+ if (categories.length > 0) {
568
+ lines.push(chalk4.bold.underline(" Tech Stack"));
569
+ for (const [cat, items] of categories) {
570
+ const label = CATEGORY_LABELS[cat] ?? cat;
571
+ const names = items.map((i) => chalk4.white(i.name)).join(chalk4.dim(", "));
572
+ lines.push(` ${chalk4.cyan(label)}: ${names}`);
573
+ }
574
+ lines.push("");
575
+ }
576
+ }
577
+ if (ext.serviceDependencies) {
578
+ const svc = ext.serviceDependencies;
579
+ const categories = Object.entries(svc).filter(([, items]) => items.length > 0);
580
+ if (categories.length > 0) {
581
+ lines.push(chalk4.bold.underline(" Services & Integrations"));
582
+ for (const [cat, items] of categories) {
583
+ const label = CATEGORY_LABELS[cat] ?? cat;
584
+ const names = items.map((i) => {
585
+ const ver = i.version ? chalk4.dim(` ${i.version}`) : "";
586
+ return chalk4.white(i.name) + ver;
587
+ }).join(chalk4.dim(", "));
588
+ lines.push(` ${chalk4.cyan(label)}: ${names}`);
589
+ }
590
+ lines.push("");
591
+ }
592
+ }
593
+ if (ext.breakingChangeExposure) {
594
+ const bc = ext.breakingChangeExposure;
595
+ if (bc.deprecatedPackages.length > 0 || bc.legacyPolyfills.length > 0) {
596
+ lines.push(chalk4.bold.underline(" Breaking Change Exposure"));
597
+ const exposureColor = bc.exposureScore >= 40 ? chalk4.red : bc.exposureScore >= 20 ? chalk4.yellow : chalk4.green;
598
+ lines.push(` Exposure Score: ${exposureColor.bold(`${bc.exposureScore}/100`)}`);
599
+ if (bc.deprecatedPackages.length > 0) {
600
+ lines.push(` ${chalk4.red("Deprecated")}: ${bc.deprecatedPackages.map((p) => chalk4.dim(p)).join(", ")}`);
601
+ }
602
+ if (bc.legacyPolyfills.length > 0) {
603
+ lines.push(` ${chalk4.yellow("Polyfills")}: ${bc.legacyPolyfills.map((p) => chalk4.dim(p)).join(", ")}`);
604
+ }
605
+ if (bc.peerConflictsDetected) {
606
+ lines.push(` ${chalk4.red("\u26A0")} Peer dependency conflicts detected`);
607
+ }
608
+ lines.push(` Recommendation: ${chalk4.bold(bc.overallRecommendation)}`);
609
+ const projectsWithPlans = bc.projectIntelligence.filter((p) => p.packages.length > 0).slice(0, 3);
610
+ if (projectsWithPlans.length > 0) {
611
+ lines.push(" Major Upgrade Intelligence:");
612
+ for (const p of projectsWithPlans) {
613
+ lines.push(` - ${p.project} (${p.recommendation})`);
614
+ for (const pkg2 of p.packages.slice(0, 2)) {
615
+ lines.push(` \xB7 ${pkg2.package} ${pkg2.currentVersion ?? "?"} \u2192 ${pkg2.targetVersion ?? "?"} | touched ~${pkg2.usage.touchedPercent}% | ${pkg2.automatable}`);
616
+ }
617
+ }
618
+ }
619
+ lines.push("");
620
+ }
621
+ }
622
+ if (ext.tsModernity && ext.tsModernity.typescriptVersion) {
623
+ const ts = ext.tsModernity;
624
+ lines.push(chalk4.bold.underline(" TypeScript"));
625
+ const parts = [];
626
+ parts.push(`v${ts.typescriptVersion}`);
627
+ if (ts.strict === true) parts.push(chalk4.green("strict \u2714"));
628
+ else if (ts.strict === false) parts.push(chalk4.yellow("strict \u2716"));
629
+ if (ts.moduleType) parts.push(ts.moduleType.toUpperCase());
630
+ if (ts.target) parts.push(`target: ${ts.target}`);
631
+ lines.push(` ${parts.join(chalk4.dim(" \xB7 "))}`);
632
+ lines.push("");
633
+ }
634
+ if (ext.buildDeploy) {
635
+ const bd = ext.buildDeploy;
636
+ const hasSomething = bd.ci.length > 0 || bd.docker.dockerfileCount > 0 || bd.packageManagers.length > 0;
637
+ if (hasSomething) {
638
+ lines.push(chalk4.bold.underline(" Build & Deploy"));
639
+ if (bd.ci.length > 0) lines.push(` CI: ${bd.ci.join(", ")}`);
640
+ if (bd.docker.dockerfileCount > 0) {
641
+ lines.push(` Docker: ${bd.docker.dockerfileCount} Dockerfile${bd.docker.dockerfileCount !== 1 ? "s" : ""} (${bd.docker.baseImages.join(", ")})`);
642
+ }
643
+ if (bd.packageManagers.length > 0) lines.push(` Package Managers: ${bd.packageManagers.join(", ")}`);
644
+ if (bd.monorepoTools.length > 0) lines.push(` Monorepo: ${bd.monorepoTools.join(", ")}`);
645
+ if (bd.iac.length > 0) lines.push(` IaC: ${bd.iac.join(", ")}`);
646
+ lines.push("");
647
+ }
648
+ }
649
+ if (ext.uiPurpose) {
650
+ const up = ext.uiPurpose;
651
+ lines.push(chalk4.bold.underline(" Product Purpose Signals"));
652
+ lines.push(` Frameworks: ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : chalk4.dim("unknown")}`);
653
+ lines.push(` Evidence: ${up.topEvidence.length}${up.capped ? chalk4.dim(` of ${up.evidenceCount} (capped)`) : ""}`);
654
+ const top = up.topEvidence.slice(0, 8);
655
+ if (top.length > 0) {
656
+ lines.push(" Top Signals:");
657
+ for (const item of top) {
658
+ lines.push(` - [${item.kind}] ${item.value} ${chalk4.dim(`(${item.file})`)}`);
659
+ }
660
+ }
661
+ if (up.unknownSignals.length > 0) {
662
+ lines.push(" Unknowns:");
663
+ for (const u of up.unknownSignals.slice(0, 4)) {
664
+ lines.push(` - ${chalk4.yellow(u)}`);
665
+ }
666
+ }
667
+ lines.push("");
668
+ }
669
+ if (ext.securityPosture) {
670
+ const sec = ext.securityPosture;
671
+ lines.push(chalk4.bold.underline(" Security Posture"));
672
+ const checks = [];
673
+ checks.push(sec.lockfilePresent ? chalk4.green("Lockfile \u2714") : chalk4.red("Lockfile \u2716"));
674
+ checks.push(sec.gitignoreCoversEnv ? chalk4.green(".env \u2714") : chalk4.red(".env \u2716"));
675
+ checks.push(sec.gitignoreCoversNodeModules ? chalk4.green("node_modules \u2714") : chalk4.yellow("node_modules \u2716"));
676
+ if (sec.multipleLockfileTypes) checks.push(chalk4.yellow("Multiple lockfiles \u26A0"));
677
+ if (sec.envFilesTracked) checks.push(chalk4.red("Env files tracked \u2716"));
678
+ lines.push(` ${checks.join(chalk4.dim(" \xB7 "))}`);
679
+ lines.push("");
680
+ }
681
+ if (ext.platformMatrix) {
682
+ const pm = ext.platformMatrix;
683
+ if (pm.nativeModules.length > 0 || pm.dockerBaseImages.length > 0) {
684
+ lines.push(chalk4.bold.underline(" Platform"));
685
+ if (pm.nativeModules.length > 0) {
686
+ lines.push(` Native modules: ${pm.nativeModules.map((m) => chalk4.dim(m)).join(", ")}`);
687
+ }
688
+ if (pm.osAssumptions.length > 0) {
689
+ lines.push(` OS assumptions: ${pm.osAssumptions.join(", ")}`);
690
+ }
691
+ lines.push("");
692
+ }
693
+ }
694
+ if (ext.codeQuality) {
695
+ const cq = ext.codeQuality;
696
+ lines.push(chalk4.bold.underline(" Code Quality"));
697
+ lines.push(` Files: ${chalk4.white(`${cq.filesAnalyzed}`)} \xB7 Functions: ${chalk4.white(`${cq.functionsAnalyzed}`)} \xB7 Avg complexity: ${chalk4.white(`${cq.avgCyclomaticComplexity}`)} \xB7 Avg length: ${chalk4.white(`${cq.avgFunctionLength}`)} lines`);
698
+ lines.push(` Max nesting: ${cq.maxNestingDepth} \xB7 Circular deps: ${cq.circularDependencies} \xB7 Dead code: ${cq.deadCodePercent}%`);
699
+ if (cq.godFiles.length > 0) {
700
+ const preview = cq.godFiles.slice(0, 3).map((f) => `${f.path} (${f.lines} lines)`).join(", ");
701
+ lines.push(` ${chalk4.yellow("God files")}: ${preview}`);
702
+ }
703
+ lines.push("");
704
+ }
705
+ if (ext.dependencyGraph) {
706
+ const dg = ext.dependencyGraph;
707
+ if (dg.lockfileType) {
708
+ lines.push(chalk4.bold.underline(" Dependency Graph"));
709
+ lines.push(` ${dg.lockfileType}: ${chalk4.white(`${dg.totalUnique}`)} unique, ${chalk4.white(`${dg.totalInstalled}`)} installed`);
710
+ if (dg.duplicatedPackages.length > 0) {
711
+ lines.push(` ${chalk4.yellow(`${dg.duplicatedPackages.length} duplicated`)} packages`);
712
+ }
713
+ if (dg.phantomDependencies.length > 0) {
714
+ lines.push(` ${chalk4.red(`${dg.phantomDependencies.length} phantom`)} dependencies`);
715
+ }
716
+ lines.push("");
717
+ }
718
+ }
719
+ return lines;
720
+ }
721
+ function formatArchitectureDiagram(arch) {
722
+ const lines = [];
723
+ lines.push(chalk4.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
724
+ lines.push(chalk4.bold.cyan("\u2551 Architecture Layers \u2551"));
725
+ lines.push(chalk4.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
726
+ lines.push("");
727
+ lines.push(chalk4.bold(" Archetype: ") + `${arch.archetype}` + chalk4.dim(` (${Math.round(arch.archetypeConfidence * 100)}% confidence)`));
728
+ lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ? chalk4.dim(` (${arch.unclassified} unclassified)`) : ""));
729
+ lines.push("");
730
+ if (arch.layers.length > 0) {
731
+ for (const layer of arch.layers) {
732
+ const risk = layer.riskLevel === "none" ? chalk4.dim("none") : layer.riskLevel === "low" ? chalk4.green("low") : layer.riskLevel === "moderate" ? chalk4.yellow("moderate") : chalk4.red("high");
733
+ lines.push(` ${chalk4.bold(layer.layer)} ${layer.fileCount} file${layer.fileCount !== 1 ? "s" : ""} drift ${scoreBar(layer.driftScore)} risk ${risk}`);
734
+ }
735
+ lines.push("");
736
+ }
737
+ return lines;
738
+ }
739
+ function generatePriorityActions(artifact) {
740
+ const actions = [];
741
+ const eolProjects = artifact.projects.filter(
742
+ (p) => p.runtimeMajorsBehind !== void 0 && p.runtimeMajorsBehind >= 3
743
+ );
744
+ if (eolProjects.length > 0) {
745
+ const names = eolProjects.map((p) => p.name).join(", ");
746
+ let detail = `End-of-life runtimes no longer receive security patches and block ecosystem upgrades.`;
747
+ const fileLines = [];
748
+ for (const p of eolProjects) {
749
+ fileLines.push(`
750
+ ./${p.path}`);
751
+ fileLines.push(`
752
+ ${p.runtime} \u2192 ${p.runtimeLatest} (${p.runtimeMajorsBehind} major${p.runtimeMajorsBehind > 1 ? "s" : ""} behind)`);
753
+ }
754
+ detail += fileLines.join("");
755
+ actions.push({
756
+ title: `Upgrade EOL runtime${eolProjects.length > 1 ? "s" : ""} in ${names}`,
757
+ explanation: detail,
758
+ impact: `+${Math.min(eolProjects.length * 10, 30)} points (runtime & EOL scores)`,
759
+ severity: 100
760
+ });
761
+ }
762
+ const severeFrameworks = [];
763
+ for (const p of artifact.projects) {
764
+ for (const fw of p.frameworks) {
765
+ if (fw.majorsBehind !== null && fw.majorsBehind >= 3) {
766
+ severeFrameworks.push({ name: fw.name, fw: `${fw.currentVersion} \u2192 ${fw.latestVersion}`, behind: fw.majorsBehind, project: p.name, projectPath: p.path });
767
+ }
768
+ }
769
+ }
770
+ if (severeFrameworks.length > 0) {
771
+ const worst = severeFrameworks.sort((a, b) => b.behind - a.behind)[0];
772
+ const others = severeFrameworks.length > 1 ? ` (+${severeFrameworks.length - 1} more)` : "";
773
+ let detail = `${worst.behind} major versions behind. Major framework drift increases breaking change risk and blocks access to security fixes and performance improvements.`;
774
+ const fileLines = [];
775
+ let shown = 0;
776
+ for (const sf of severeFrameworks) {
777
+ if (shown >= 8) break;
778
+ fileLines.push(`
779
+ ./${sf.projectPath}`);
780
+ fileLines.push(`
781
+ ${sf.name}: ${sf.fw} (${sf.behind} major${sf.behind > 1 ? "s" : ""} behind)`);
782
+ shown++;
783
+ }
784
+ const remaining = severeFrameworks.length - shown;
785
+ detail += fileLines.join("");
786
+ if (remaining > 0) detail += `
787
+ ... and ${remaining} more`;
788
+ actions.push({
789
+ title: `Upgrade ${worst.name} ${worst.fw} in ${worst.project}${others}`,
790
+ explanation: detail,
791
+ impact: `+5\u201315 points`,
792
+ severity: 90
793
+ });
794
+ }
795
+ for (const p of artifact.projects) {
796
+ const b = p.dependencyAgeBuckets;
797
+ const total = b.current + b.oneBehind + b.twoPlusBehind;
798
+ if (total === 0) continue;
799
+ const twoPlusPct = Math.round(b.twoPlusBehind / total * 100);
800
+ if (twoPlusPct >= 40) {
801
+ let detail = `${b.twoPlusBehind} of ${total} dependencies are 2+ majors behind. Run \`npm outdated\` and prioritise packages with known CVEs or breaking API changes.`;
802
+ const worstDeps = p.dependencies.filter((d) => d.majorsBehind !== null && d.majorsBehind >= 2).sort((a, b2) => (b2.majorsBehind ?? 0) - (a.majorsBehind ?? 0));
803
+ if (worstDeps.length > 0) {
804
+ const depLines = [];
805
+ let shown = 0;
806
+ depLines.push(`
807
+ ./${p.path}`);
808
+ for (const dep of worstDeps) {
809
+ if (shown >= 8) break;
810
+ const current = dep.resolvedVersion ?? dep.currentSpec;
811
+ const latest = dep.latestStable ?? "?";
812
+ depLines.push(`
813
+ ${dep.package}: ${current} \u2192 ${latest} (${dep.majorsBehind} major${dep.majorsBehind > 1 ? "s" : ""} behind)`);
814
+ shown++;
815
+ }
816
+ const remaining = worstDeps.length - shown;
817
+ detail += depLines.join("");
818
+ if (remaining > 0) detail += `
819
+ ... and ${remaining} more`;
820
+ }
821
+ actions.push({
822
+ title: `Reduce dependency rot in ${p.name} (${twoPlusPct}% severely outdated)`,
823
+ explanation: detail,
824
+ impact: `+5\u201310 points`,
825
+ severity: 80 + twoPlusPct / 10
826
+ });
827
+ }
828
+ }
829
+ const twoMajorFrameworks = [];
830
+ for (const p of artifact.projects) {
831
+ for (const fw of p.frameworks) {
832
+ if (fw.majorsBehind === 2) {
833
+ twoMajorFrameworks.push({ name: fw.name, project: p.name, projectPath: p.path, fw: `${fw.currentVersion} \u2192 ${fw.latestVersion}` });
834
+ }
835
+ }
836
+ }
837
+ const uniqueTwo = [...new Map(twoMajorFrameworks.map((f) => [f.name, f])).values()];
838
+ if (uniqueTwo.length > 0) {
839
+ const list = uniqueTwo.slice(0, 3).map((f) => `${f.name} (${f.fw})`).join(", ");
840
+ const moreCount = uniqueTwo.length > 3 ? ` +${uniqueTwo.length - 3} more` : "";
841
+ let detail = `These frameworks are 2 major versions behind. Create upgrade tickets and check migration guides \u2014 the gap will widen with each new release.`;
842
+ const fileLines = [];
843
+ let shown = 0;
844
+ for (const tf of twoMajorFrameworks) {
845
+ if (shown >= 8) break;
846
+ fileLines.push(`
847
+ ./${tf.projectPath}`);
848
+ fileLines.push(`
849
+ ${tf.name}: ${tf.fw}`);
850
+ shown++;
851
+ }
852
+ const remaining = twoMajorFrameworks.length - shown;
853
+ detail += fileLines.join("");
854
+ if (remaining > 0) detail += `
855
+ ... and ${remaining} more`;
856
+ actions.push({
857
+ title: `Plan major framework upgrades: ${list}${moreCount}`,
858
+ explanation: detail,
859
+ impact: `+5\u201310 points`,
860
+ severity: 60
861
+ });
862
+ }
863
+ if (artifact.extended?.breakingChangeExposure) {
864
+ const bc = artifact.extended.breakingChangeExposure;
865
+ const total = bc.deprecatedPackages.length + bc.legacyPolyfills.length;
866
+ if (total > 0) {
867
+ const items = [...bc.deprecatedPackages, ...bc.legacyPolyfills].slice(0, 5).join(", ");
868
+ const moreCount = total > 5 ? ` +${total - 5} more` : "";
869
+ let detail = `${total} package${total !== 1 ? "s" : ""} are deprecated or legacy polyfills. These receive no updates and may have known vulnerabilities.`;
870
+ const allPkgNames = /* @__PURE__ */ new Set([...bc.deprecatedPackages, ...bc.legacyPolyfills]);
871
+ const fileLines = [];
872
+ let shown = 0;
873
+ for (const p of artifact.projects) {
874
+ const matches = p.dependencies.filter((d) => allPkgNames.has(d.package));
875
+ if (matches.length === 0) continue;
876
+ if (shown >= 10) break;
877
+ fileLines.push(`
878
+ ./${p.path}`);
879
+ for (const dep of matches) {
880
+ if (shown >= 10) break;
881
+ const ver = dep.resolvedVersion ?? dep.currentSpec;
882
+ const label = bc.deprecatedPackages.includes(dep.package) ? "deprecated" : "polyfill";
883
+ fileLines.push(`
884
+ ${dep.package}: ${ver} (${label})`);
885
+ shown++;
886
+ }
887
+ }
888
+ const remaining = total - shown;
889
+ detail += fileLines.join("");
890
+ if (remaining > 0) detail += `
891
+ ... and ${remaining} more`;
892
+ actions.push({
893
+ title: `Replace deprecated/legacy packages: ${items}${moreCount}`,
894
+ explanation: detail,
895
+ severity: 55
896
+ });
897
+ }
898
+ }
899
+ if (artifact.extended?.dependencyGraph) {
900
+ const dg = artifact.extended.dependencyGraph;
901
+ const phantomCount = dg.phantomDependencies.length;
902
+ if (phantomCount >= 10) {
903
+ let detail = `Packages used in code but not declared in package.json. These rely on transitive installs and can break unpredictably when other packages update.`;
904
+ const details = dg.phantomDependencyDetails;
905
+ if (details && details.length > 0) {
906
+ const byPath = /* @__PURE__ */ new Map();
907
+ for (const d of details) {
908
+ if (!byPath.has(d.sourcePath)) byPath.set(d.sourcePath, []);
909
+ byPath.get(d.sourcePath).push({ package: d.package, spec: d.spec });
910
+ }
911
+ const pathLines = [];
912
+ let shown = 0;
913
+ for (const [srcPath, pkgs] of byPath) {
914
+ if (shown >= 10) break;
915
+ pathLines.push(`
916
+ ./${srcPath}`);
917
+ for (const pkg2 of pkgs) {
918
+ if (shown >= 10) break;
919
+ pathLines.push(`
920
+ ${pkg2.package}: ${pkg2.spec}`);
921
+ shown++;
922
+ }
923
+ }
924
+ const remaining = phantomCount - shown;
925
+ detail += pathLines.join("");
926
+ if (remaining > 0) detail += `
927
+ ... and ${remaining} more`;
928
+ }
929
+ actions.push({
930
+ title: `Fix ${phantomCount} phantom dependencies`,
931
+ explanation: detail,
932
+ severity: 45
933
+ });
934
+ }
935
+ }
936
+ if (artifact.extended?.securityPosture) {
937
+ const sec = artifact.extended.securityPosture;
938
+ if (sec.envFilesTracked || !sec.lockfilePresent) {
939
+ const issues = [];
940
+ if (sec.envFilesTracked) issues.push(".env files are tracked in git");
941
+ if (!sec.lockfilePresent) issues.push("no lockfile found");
942
+ let detail;
943
+ if (sec.envFilesTracked) {
944
+ detail = "Environment files may contain secrets. Add them to .gitignore and rotate any exposed credentials immediately.";
945
+ detail += "\n ./.gitignore";
946
+ detail += "\n Add: .env, .env.*, .env.local";
947
+ } else {
948
+ detail = "Without a lockfile, installs are non-deterministic. Run the install command to generate one and commit it.";
949
+ detail += "\n ./";
950
+ detail += `
951
+ Missing: ${sec.lockfileTypes.length > 0 ? sec.lockfileTypes.join(", ") + " (multiple types detected)" : "package-lock.json, pnpm-lock.yaml, or yarn.lock"}`;
952
+ }
953
+ actions.push({
954
+ title: `Fix security posture: ${issues.join(", ")}`,
955
+ explanation: detail,
956
+ severity: 95
957
+ });
958
+ }
959
+ }
960
+ if (artifact.extended?.dependencyGraph) {
961
+ const dupes = artifact.extended.dependencyGraph.duplicatedPackages;
962
+ const highImpactDupes = dupes.filter((d) => d.versions.length >= 3);
963
+ if (highImpactDupes.length >= 3) {
964
+ const names = highImpactDupes.slice(0, 4).map((d) => `${d.name} (${d.versions.length}v)`).join(", ");
965
+ let detail = `${highImpactDupes.length} packages have 3+ versions installed. Run \`npm dedupe\` to reduce bundle size and install time.`;
966
+ const dupeLines = [];
967
+ let shown = 0;
968
+ for (const d of highImpactDupes) {
969
+ if (shown >= 8) break;
970
+ dupeLines.push(`
971
+ ${d.name}: ${d.versions.join(", ")} (${d.consumers} consumer${d.consumers !== 1 ? "s" : ""})`);
972
+ shown++;
973
+ }
974
+ const remaining = highImpactDupes.length - shown;
975
+ detail += dupeLines.join("");
976
+ if (remaining > 0) detail += `
977
+ ... and ${remaining} more`;
978
+ actions.push({
979
+ title: `Deduplicate heavily-versioned packages`,
980
+ explanation: detail,
981
+ severity: 35
982
+ });
983
+ }
984
+ }
985
+ actions.sort((a, b) => b.severity - a.severity);
986
+ return actions.slice(0, 5);
987
+ }
988
+
989
+ // src/formatters/markdown.ts
990
+ function formatMarkdown(artifact) {
991
+ const lines = [];
992
+ lines.push("# Vibgrate Drift Report");
993
+ lines.push("");
994
+ lines.push(`| Metric | Value |`);
995
+ lines.push(`|--------|-------|`);
996
+ lines.push(`| **Drift Score** | ${artifact.drift.score}/100 |`);
997
+ lines.push(`| **Risk Level** | ${artifact.drift.riskLevel.toUpperCase()} |`);
998
+ lines.push(`| **Projects** | ${artifact.projects.length} |`);
999
+ const scannedMeta = [artifact.timestamp];
1000
+ if (artifact.durationMs !== void 0) scannedMeta.push(`${(artifact.durationMs / 1e3).toFixed(1)}s`);
1001
+ if (artifact.filesScanned !== void 0) scannedMeta.push(`${artifact.filesScanned} files`);
1002
+ if (artifact.treeSummary) scannedMeta.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files \xB7 ${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
1003
+ lines.push(`| **Scanned** | ${scannedMeta.join(" \xB7 ")} |`);
1004
+ if (artifact.vcs) {
1005
+ lines.push(`| **VCS** | ${artifact.vcs.type} |`);
1006
+ if (artifact.vcs.branch) lines.push(`| **Branch** | ${artifact.vcs.branch} |`);
1007
+ if (artifact.vcs.sha) lines.push(`| **Commit** | \`${artifact.vcs.shortSha}\` |`);
1008
+ }
1009
+ lines.push("");
1010
+ lines.push("## Score Breakdown");
1011
+ lines.push("");
1012
+ lines.push(`| Component | Score |`);
1013
+ lines.push(`|-----------|-------|`);
1014
+ lines.push(`| Runtime | ${artifact.drift.components.runtimeScore} |`);
1015
+ lines.push(`| Frameworks | ${artifact.drift.components.frameworkScore} |`);
1016
+ lines.push(`| Dependencies | ${artifact.drift.components.dependencyScore} |`);
1017
+ lines.push(`| EOL Risk | ${artifact.drift.components.eolScore} |`);
1018
+ lines.push("");
1019
+ lines.push("## Projects");
1020
+ lines.push("");
1021
+ for (const project of artifact.projects) {
1022
+ lines.push(`### ${project.name} (${project.type})`);
1023
+ lines.push("");
1024
+ if (project.runtime) {
1025
+ const lag = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? ` \u2014 ${project.runtimeMajorsBehind} major(s) behind` : " \u2014 current";
1026
+ lines.push(`- **Runtime:** ${project.runtime}${lag}`);
1027
+ }
1028
+ if (project.frameworks.length > 0) {
1029
+ lines.push("- **Frameworks:**");
1030
+ for (const fw of project.frameworks) {
1031
+ const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? "current" : `${fw.majorsBehind} behind` : "unknown";
1032
+ lines.push(` - ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
1033
+ }
1034
+ }
1035
+ const b = project.dependencyAgeBuckets;
1036
+ const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
1037
+ if (total > 0) {
1038
+ lines.push(`- **Dependencies:** ${b.current} current, ${b.oneBehind} 1-behind, ${b.twoPlusBehind} 2+ behind, ${b.unknown} unknown`);
1039
+ }
1040
+ lines.push("");
1041
+ }
1042
+ if (artifact.extended?.uiPurpose) {
1043
+ const up = artifact.extended.uiPurpose;
1044
+ lines.push("## Product Purpose Signals");
1045
+ lines.push("");
1046
+ lines.push(`- **Frameworks:** ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : "unknown"}`);
1047
+ lines.push(`- **Evidence Items:** ${up.topEvidence.length}${up.capped ? ` (capped from ${up.evidenceCount})` : ""}`);
1048
+ if (up.topEvidence.length > 0) {
1049
+ lines.push("- **Top Evidence:**");
1050
+ for (const item of up.topEvidence.slice(0, 10)) {
1051
+ lines.push(` - [${item.kind}] ${item.value} (${item.file})`);
1052
+ }
1053
+ }
1054
+ if (up.unknownSignals.length > 0) {
1055
+ lines.push("- **Unknowns:**");
1056
+ for (const u of up.unknownSignals.slice(0, 5)) {
1057
+ lines.push(` - ${u}`);
1058
+ }
1059
+ }
1060
+ lines.push("");
1061
+ }
1062
+ if (artifact.findings.length > 0) {
1063
+ lines.push("## Findings");
1064
+ lines.push("");
1065
+ lines.push(`| Level | Rule | Message | Location |`);
1066
+ lines.push(`|-------|------|---------|----------|`);
1067
+ for (const f of artifact.findings) {
1068
+ const emoji = f.level === "error" ? "\u{1F534}" : f.level === "warning" ? "\u{1F7E1}" : "\u{1F535}";
1069
+ lines.push(`| ${emoji} ${f.level} | ${f.ruleId} | ${f.message} | ${f.location} |`);
1070
+ }
1071
+ lines.push("");
1072
+ }
1073
+ if (artifact.delta !== void 0) {
1074
+ const dir = artifact.delta > 0 ? "\u{1F4C8}" : artifact.delta < 0 ? "\u{1F4C9}" : "\u27A1\uFE0F";
1075
+ lines.push(`## Drift Delta: ${dir} ${artifact.delta > 0 ? "+" : ""}${artifact.delta} vs baseline`);
1076
+ lines.push("");
1077
+ }
1078
+ return lines.join("\n");
1079
+ }
1080
+
1081
+ // src/commands/report.ts
1082
+ var reportCommand = new Command4("report").description("Generate a drift report from a scan artifact").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--format <format>", "Output format (md|text|json)", "text").action(async (opts) => {
1083
+ const artifactPath = path4.resolve(opts.in);
61
1084
  if (!await pathExists(artifactPath)) {
62
- console.error(chalk2.red(`Artifact not found: ${artifactPath}`));
63
- console.error(chalk2.dim('Run "vibgrate scan" first to generate a scan artifact.'));
1085
+ console.error(chalk5.red(`Artifact not found: ${artifactPath}`));
1086
+ console.error(chalk5.dim('Run "vibgrate scan" first to generate a scan artifact.'));
64
1087
  process.exit(1);
65
1088
  }
66
1089
  const artifact = await readJsonFile(artifactPath);
@@ -78,20 +1101,370 @@ var reportCommand = new Command2("report").description("Generate a drift report
78
1101
  }
79
1102
  });
80
1103
 
1104
+ // src/commands/push.ts
1105
+ import * as crypto2 from "crypto";
1106
+ import * as path5 from "path";
1107
+ import { Command as Command5 } from "commander";
1108
+ import chalk6 from "chalk";
1109
+
1110
+ // src/utils/compact-artifact.ts
1111
+ import * as zlib from "zlib";
1112
+ import { promisify } from "util";
1113
+
1114
+ // src/utils/compact-evidence.ts
1115
+ var CATEGORY_PATTERNS = [
1116
+ { category: "pricing", pattern: /price|pricing|billing|subscri|trial|credit|plan|tier|upgrade|premium|pro|enterprise/i },
1117
+ { category: "auth", pattern: /sign[- ]?in|sign[- ]?up|log[- ]?in|log[- ]?out|auth|sso|oauth|password|register|invite|onboard/i },
1118
+ { category: "dashboard", pattern: /dashboard|overview|home|main|summary|stats/i },
1119
+ { category: "settings", pattern: /setting|config|preference|option|profile|account/i },
1120
+ { category: "users", pattern: /user|member|team|role|permission|access|admin|owner/i },
1121
+ { category: "integrations", pattern: /integrat|connect|webhook|api[- ]?key|sync|import|export/i },
1122
+ { category: "reports", pattern: /report|analy|metric|chart|graph|insight|track/i },
1123
+ { category: "workflows", pattern: /workflow|automat|schedule|trigger|action|job|task|pipeline/i },
1124
+ { category: "projects", pattern: /project|workspace|organization|folder|repo/i },
1125
+ { category: "navigation", pattern: /menu|nav|sidebar|header|footer|breadcrumb/i }
1126
+ ];
1127
+ function compactUiPurpose(result, maxSamplesPerCategory = 3) {
1128
+ const evidence = result.topEvidence;
1129
+ const dependencies = evidence.filter((e) => e.kind === "dependency").map((e) => e.value).slice(0, 10);
1130
+ const routes = dedupeRoutes(
1131
+ evidence.filter((e) => e.kind === "route").map((e) => e.value)
1132
+ ).slice(0, 15);
1133
+ const textEvidence = evidence.filter(
1134
+ (e) => e.kind !== "dependency" && e.kind !== "route" && e.kind !== "feature_flag"
1135
+ );
1136
+ const byCategory = /* @__PURE__ */ new Map();
1137
+ const categoryCounts = {};
1138
+ for (const item of textEvidence) {
1139
+ const category = categorize(item.value);
1140
+ if (!byCategory.has(category)) {
1141
+ byCategory.set(category, /* @__PURE__ */ new Set());
1142
+ }
1143
+ const normalized = normalizeValue(item.value);
1144
+ if (normalized.length >= 3) {
1145
+ byCategory.get(category).add(normalized);
1146
+ }
1147
+ }
1148
+ const samples = [];
1149
+ for (const [category, values] of byCategory) {
1150
+ const deduped = dedupeStrings([...values]);
1151
+ categoryCounts[category] = deduped.length;
1152
+ for (const value of deduped.slice(0, maxSamplesPerCategory)) {
1153
+ samples.push({ kind: "text", value, category });
1154
+ }
1155
+ }
1156
+ const featureFlags = evidence.filter((e) => e.kind === "feature_flag");
1157
+ if (featureFlags.length > 0) {
1158
+ categoryCounts["feature_flags"] = featureFlags.length;
1159
+ samples.push({ kind: "feature_flag", value: "feature flags detected", category: "feature_flags" });
1160
+ }
1161
+ return {
1162
+ samples,
1163
+ categoryCounts,
1164
+ originalCount: evidence.length,
1165
+ dependencies,
1166
+ routes,
1167
+ detectedFrameworks: result.detectedFrameworks
1168
+ };
1169
+ }
1170
+ function categorize(value) {
1171
+ for (const { category, pattern } of CATEGORY_PATTERNS) {
1172
+ if (pattern.test(value)) return category;
1173
+ }
1174
+ return "general";
1175
+ }
1176
+ function normalizeValue(value) {
1177
+ return value.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").replace(/\s+/g, " ").trim().slice(0, 60);
1178
+ }
1179
+ function dedupeStrings(values) {
1180
+ const sorted = values.sort((a, b) => b.length - a.length);
1181
+ const kept = [];
1182
+ for (const value of sorted) {
1183
+ const isDupe = kept.some((k) => {
1184
+ const stem = value.slice(0, 6);
1185
+ return k.startsWith(stem) || k.includes(value) || value.includes(k);
1186
+ });
1187
+ if (!isDupe) {
1188
+ kept.push(value);
1189
+ }
1190
+ }
1191
+ return kept;
1192
+ }
1193
+ function dedupeRoutes(routes) {
1194
+ const seen = /* @__PURE__ */ new Set();
1195
+ const result = [];
1196
+ for (const route of routes) {
1197
+ const normalized = route.replace(/:[a-z_]+/gi, ":param").replace(/\[\[*\.*\.*[a-z_]+\]*\]/gi, ":param").replace(/\/+$/, "").toLowerCase();
1198
+ if (!seen.has(normalized)) {
1199
+ seen.add(normalized);
1200
+ result.push(route);
1201
+ }
1202
+ }
1203
+ return result;
1204
+ }
1205
+
1206
+ // src/utils/compact-artifact.ts
1207
+ var gzip2 = promisify(zlib.gzip);
1208
+ var MAX_ITEMS = 50;
1209
+ function extractName(entry) {
1210
+ const match = entry.match(/^(.+?)\s*\(/);
1211
+ return match ? match[1].trim() : entry.trim();
1212
+ }
1213
+ function compactDataStores(result) {
1214
+ return {
1215
+ databaseTechnologies: result.databaseTechnologies.slice(0, 10),
1216
+ connectionStrings: [],
1217
+ // Don't include connection strings in upload
1218
+ connectionPoolSettings: result.connectionPoolSettings.slice(0, MAX_ITEMS),
1219
+ replicationSettings: result.replicationSettings.slice(0, 20),
1220
+ readReplicaSettings: result.readReplicaSettings.slice(0, 20),
1221
+ failoverSettings: result.failoverSettings.slice(0, 20),
1222
+ collationAndEncoding: result.collationAndEncoding.slice(0, 20),
1223
+ queryTimeoutDefaults: result.queryTimeoutDefaults.slice(0, 20),
1224
+ manualIndexes: result.manualIndexes.map(extractName).slice(0, MAX_ITEMS),
1225
+ tables: result.tables.map(extractName).slice(0, MAX_ITEMS),
1226
+ views: result.views.map(extractName).slice(0, MAX_ITEMS),
1227
+ storedProcedures: result.storedProcedures.map(extractName).slice(0, MAX_ITEMS),
1228
+ triggers: result.triggers.map(extractName).slice(0, MAX_ITEMS),
1229
+ rowLevelSecurityPolicies: result.rowLevelSecurityPolicies.slice(0, 20),
1230
+ otherServices: result.otherServices.slice(0, 20)
1231
+ };
1232
+ }
1233
+ function compactApiSurface(result) {
1234
+ const seenProviders = /* @__PURE__ */ new Set();
1235
+ const uniqueIntegrations = result.integrations.filter((i) => {
1236
+ const domain = i.provider.split(":")[0];
1237
+ if (seenProviders.has(domain)) return false;
1238
+ seenProviders.add(domain);
1239
+ return true;
1240
+ }).slice(0, MAX_ITEMS).map((i) => ({
1241
+ provider: i.provider,
1242
+ endpoint: "",
1243
+ // Don't include full endpoints
1244
+ version: i.version,
1245
+ parameters: [],
1246
+ // Don't include params
1247
+ configOptions: [],
1248
+ authHints: [],
1249
+ files: []
1250
+ // Don't include file paths
1251
+ }));
1252
+ return {
1253
+ integrations: uniqueIntegrations,
1254
+ openApiSpecifications: result.openApiSpecifications.slice(0, 10),
1255
+ webhookUrls: result.webhookUrls.slice(0, 20),
1256
+ callbackEndpoints: result.callbackEndpoints.slice(0, 20),
1257
+ apiVersionPins: result.apiVersionPins.slice(0, 20),
1258
+ tokenExpirationPolicies: result.tokenExpirationPolicies.slice(0, 20),
1259
+ rateLimitOverrides: result.rateLimitOverrides.slice(0, 20),
1260
+ customHeaders: result.customHeaders.slice(0, 20),
1261
+ corsPolicies: result.corsPolicies.slice(0, 20),
1262
+ oauthScopes: result.oauthScopes.slice(0, 20),
1263
+ apiTokens: []
1264
+ // Don't include token references
1265
+ };
1266
+ }
1267
+ function compactAssetBranding(result) {
1268
+ return {
1269
+ faviconFiles: result.faviconFiles.slice(0, 1),
1270
+ productLogos: []
1271
+ // Don't include logos
1272
+ };
1273
+ }
1274
+ function prepareArtifactForUpload(artifact) {
1275
+ const compacted = { ...artifact };
1276
+ if (compacted.extended) {
1277
+ const ext = { ...compacted.extended };
1278
+ if (ext.dataStores) {
1279
+ ext.dataStores = compactDataStores(ext.dataStores);
1280
+ }
1281
+ if (ext.apiSurface) {
1282
+ ext.apiSurface = compactApiSurface(ext.apiSurface);
1283
+ }
1284
+ if (ext.assetBranding) {
1285
+ ext.assetBranding = compactAssetBranding(ext.assetBranding);
1286
+ }
1287
+ if (ext.uiPurpose) {
1288
+ const compactedUi = compactUiPurpose(ext.uiPurpose);
1289
+ ext.uiPurpose = {
1290
+ enabled: ext.uiPurpose.enabled,
1291
+ detectedFrameworks: compactedUi.detectedFrameworks,
1292
+ evidenceCount: compactedUi.originalCount,
1293
+ capped: ext.uiPurpose.capped,
1294
+ topEvidence: [],
1295
+ // Clear full evidence
1296
+ unknownSignals: [],
1297
+ // Add compacted data under extended properties
1298
+ ...{ compacted: compactedUi }
1299
+ };
1300
+ }
1301
+ if (ext.runtimeConfiguration) {
1302
+ ext.runtimeConfiguration = {
1303
+ ...ext.runtimeConfiguration,
1304
+ environmentVariables: ext.runtimeConfiguration.environmentVariables.slice(0, 100),
1305
+ hiddenConfigFiles: ext.runtimeConfiguration.hiddenConfigFiles.slice(0, MAX_ITEMS),
1306
+ startupArguments: ext.runtimeConfiguration.startupArguments.slice(0, 100)
1307
+ };
1308
+ }
1309
+ if (ext.operationalResilience) {
1310
+ const ops = ext.operationalResilience;
1311
+ ext.operationalResilience = {
1312
+ implicitTimeouts: ops.implicitTimeouts.slice(0, 30),
1313
+ defaultPaginationSize: ops.defaultPaginationSize.slice(0, 30),
1314
+ implicitRetryLogic: ops.implicitRetryLogic.slice(0, 30),
1315
+ defaultLocale: ops.defaultLocale.slice(0, 20),
1316
+ defaultCurrency: ops.defaultCurrency.slice(0, 20),
1317
+ implicitTimezone: ops.implicitTimezone.slice(0, 20),
1318
+ defaultCharacterEncoding: ops.defaultCharacterEncoding.slice(0, 20),
1319
+ sessionStores: ops.sessionStores.slice(0, 20),
1320
+ distributedLocks: ops.distributedLocks.slice(0, 20),
1321
+ jobSchedulers: ops.jobSchedulers.slice(0, 30),
1322
+ idempotencyKeys: ops.idempotencyKeys.slice(0, 20),
1323
+ rateLimitingCounters: ops.rateLimitingCounters.slice(0, 20),
1324
+ circuitBreakerState: ops.circuitBreakerState.slice(0, 20),
1325
+ abTestToggles: ops.abTestToggles.slice(0, 20),
1326
+ regionalEnablementRules: ops.regionalEnablementRules.slice(0, 20),
1327
+ betaAccessGroups: ops.betaAccessGroups.slice(0, 20),
1328
+ licensingEnforcementLogic: ops.licensingEnforcementLogic.slice(0, 20),
1329
+ killSwitches: ops.killSwitches.slice(0, 20),
1330
+ connectorRetryLogic: ops.connectorRetryLogic.slice(0, 20),
1331
+ apiPollingIntervals: ops.apiPollingIntervals.slice(0, 20),
1332
+ fieldMappings: ops.fieldMappings.slice(0, 20),
1333
+ schemaRegistryRules: ops.schemaRegistryRules.slice(0, 20),
1334
+ deadLetterQueueBehavior: ops.deadLetterQueueBehavior.slice(0, 20),
1335
+ dataMaskingRules: ops.dataMaskingRules.slice(0, 20),
1336
+ transformationLogic: ops.transformationLogic.slice(0, 20),
1337
+ timezoneHandling: ops.timezoneHandling.slice(0, 20),
1338
+ encryptionSettings: ops.encryptionSettings.slice(0, 30),
1339
+ hardcodedSecretSignals: ops.hardcodedSecretSignals.slice(0, 20)
1340
+ };
1341
+ }
1342
+ if (ext.dependencyGraph) {
1343
+ ext.dependencyGraph = {
1344
+ ...ext.dependencyGraph,
1345
+ phantomDependencies: ext.dependencyGraph.phantomDependencies.slice(0, MAX_ITEMS),
1346
+ phantomDependencyDetails: ext.dependencyGraph.phantomDependencyDetails?.slice(0, MAX_ITEMS),
1347
+ duplicatedPackages: ext.dependencyGraph.duplicatedPackages.slice(0, MAX_ITEMS)
1348
+ };
1349
+ }
1350
+ compacted.extended = ext;
1351
+ }
1352
+ return compacted;
1353
+ }
1354
+ async function compressArtifact(artifact) {
1355
+ const json = JSON.stringify(artifact);
1356
+ return gzip2(json, { level: 9 });
1357
+ }
1358
+ async function prepareCompressedUpload2(artifact) {
1359
+ const compacted = prepareArtifactForUpload(artifact);
1360
+ const compressed = await compressArtifact(compacted);
1361
+ return { body: compressed, contentEncoding: "gzip" };
1362
+ }
1363
+
1364
+ // src/commands/push.ts
1365
+ function parseDsn2(dsn) {
1366
+ const cleaned = dsn.replace(/[\x00-\x1F\x7F\uFEFF\u200B-\u200D\u2060]/g, "").trim();
1367
+ const match = cleaned.match(/^vibgrate\+(https?):?\/\/([^:]+):([^@]+)@([^/]+)\/(.+)$/);
1368
+ if (!match) return null;
1369
+ return {
1370
+ scheme: match[1],
1371
+ keyId: match[2],
1372
+ secret: match[3],
1373
+ host: match[4],
1374
+ workspaceId: match[5]
1375
+ };
1376
+ }
1377
+ function computeHmac(body, secret) {
1378
+ return crypto2.createHmac("sha256", secret).update(body).digest("base64");
1379
+ }
1380
+ var pushCommand = new Command5("push").description("Push scan results to Vibgrate API").option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region (us, eu)").option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
1381
+ const dsn = opts.dsn || process.env.VIBGRATE_DSN;
1382
+ if (!dsn) {
1383
+ console.error(chalk6.red("No DSN provided."));
1384
+ console.error(chalk6.dim("Set VIBGRATE_DSN environment variable or use --dsn flag."));
1385
+ if (opts.strict) process.exit(1);
1386
+ return;
1387
+ }
1388
+ const parsed = parseDsn2(dsn);
1389
+ if (!parsed) {
1390
+ console.error(chalk6.red("Invalid DSN format."));
1391
+ console.error(chalk6.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>"));
1392
+ if (opts.strict) process.exit(1);
1393
+ return;
1394
+ }
1395
+ const filePath = path5.resolve(opts.file);
1396
+ if (!await pathExists(filePath)) {
1397
+ console.error(chalk6.red(`Scan artifact not found: ${filePath}`));
1398
+ console.error(chalk6.dim('Run "vibgrate scan" first.'));
1399
+ if (opts.strict) process.exit(1);
1400
+ return;
1401
+ }
1402
+ const artifact = await readJsonFile(filePath);
1403
+ const { body, contentEncoding } = await prepareCompressedUpload2(artifact);
1404
+ const timestamp = String(Date.now());
1405
+ let host = parsed.host;
1406
+ if (opts.region) {
1407
+ try {
1408
+ host = resolveIngestHost(opts.region);
1409
+ } catch (e) {
1410
+ console.error(chalk6.red(e instanceof Error ? e.message : String(e)));
1411
+ if (opts.strict) process.exit(1);
1412
+ return;
1413
+ }
1414
+ }
1415
+ const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
1416
+ const originalSize = JSON.stringify(artifact).length;
1417
+ const compressedSize = body.length;
1418
+ const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
1419
+ console.log(chalk6.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
1420
+ try {
1421
+ const response = await fetch(url, {
1422
+ method: "POST",
1423
+ headers: {
1424
+ "Content-Type": "application/json",
1425
+ "Content-Encoding": contentEncoding,
1426
+ "X-Vibgrate-Timestamp": timestamp,
1427
+ "Authorization": `VibgrateDSN ${parsed.keyId}:${parsed.secret}`,
1428
+ "Connection": "close"
1429
+ // Prevent keep-alive delays on exit
1430
+ },
1431
+ body
1432
+ });
1433
+ if (!response.ok) {
1434
+ const text = await response.text();
1435
+ throw new Error(`HTTP ${response.status}: ${text}`);
1436
+ }
1437
+ const result = await response.json();
1438
+ console.log(chalk6.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
1439
+ console.log();
1440
+ console.log(chalk6.dim("Processing continues in the background. Results available shortly."));
1441
+ console.log();
1442
+ if (result.ingestId) {
1443
+ const dashHost = host.includes("eu.") ? "dash.vibgrate.eu" : "dash.vibgrate.com";
1444
+ const reportUrl = `https://${dashHost}/${parsed.workspaceId}/scan/${result.ingestId}`;
1445
+ console.log(chalk6.dim("View report: ") + chalk6.underline(reportUrl));
1446
+ }
1447
+ } catch (e) {
1448
+ const msg = e instanceof Error ? e.message : String(e);
1449
+ console.error(chalk6.red(`Upload failed: ${msg}`));
1450
+ if (opts.strict) process.exit(1);
1451
+ }
1452
+ });
1453
+
81
1454
  // src/commands/update.ts
82
1455
  import { execSync } from "child_process";
83
- import * as path4 from "path";
84
- import { Command as Command3 } from "commander";
85
- import chalk3 from "chalk";
1456
+ import * as path7 from "path";
1457
+ import { Command as Command6 } from "commander";
1458
+ import chalk7 from "chalk";
86
1459
 
87
1460
  // src/utils/update-check.ts
1461
+ var import_semver = __toESM(require_semver(), 1);
88
1462
  import * as fs from "fs/promises";
89
- import * as path3 from "path";
1463
+ import * as path6 from "path";
90
1464
  import * as os from "os";
91
- import semver from "semver";
92
1465
  var REGISTRY_URL = "https://registry.npmjs.org/@vibgrate%2fcli/latest";
93
- var CACHE_DIR = path3.join(os.homedir(), ".vibgrate");
94
- var CACHE_FILE = path3.join(CACHE_DIR, "update-check.json");
1466
+ var CACHE_DIR = path6.join(os.homedir(), ".vibgrate");
1467
+ var CACHE_FILE = path6.join(CACHE_DIR, "update-check.json");
95
1468
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1e3;
96
1469
  async function checkForUpdate() {
97
1470
  try {
@@ -100,7 +1473,7 @@ async function checkForUpdate() {
100
1473
  return {
101
1474
  current: VERSION,
102
1475
  latest: cached.latest,
103
- updateAvailable: semver.gt(cached.latest, VERSION)
1476
+ updateAvailable: import_semver.default.gt(cached.latest, VERSION)
104
1477
  };
105
1478
  }
106
1479
  const controller = new AbortController();
@@ -118,12 +1491,12 @@ async function checkForUpdate() {
118
1491
  if (!response.ok) return null;
119
1492
  const data = await response.json();
120
1493
  const latest = data.version;
121
- if (!latest || !semver.valid(latest)) return null;
1494
+ if (!latest || !import_semver.default.valid(latest)) return null;
122
1495
  await writeCache({ latest, checkedAt: Date.now() });
123
1496
  return {
124
1497
  current: VERSION,
125
1498
  latest,
126
- updateAvailable: semver.gt(latest, VERSION)
1499
+ updateAvailable: import_semver.default.gt(latest, VERSION)
127
1500
  };
128
1501
  } catch {
129
1502
  return null;
@@ -146,7 +1519,7 @@ async function fetchLatestVersion() {
146
1519
  if (!response.ok) return null;
147
1520
  const data = await response.json();
148
1521
  const latest = data.version;
149
- if (!latest || !semver.valid(latest)) return null;
1522
+ if (!latest || !import_semver.default.valid(latest)) return null;
150
1523
  await writeCache({ latest, checkedAt: Date.now() });
151
1524
  return latest;
152
1525
  } catch {
@@ -184,8 +1557,8 @@ function detectGlobalInstall() {
184
1557
  }
185
1558
  return null;
186
1559
  }
187
- function getGlobalUpdateCommand(pm, pkg, version) {
188
- const spec = `${pkg}@${version}`;
1560
+ function getGlobalUpdateCommand(pm, pkg2, version) {
1561
+ const spec = `${pkg2}@${version}`;
189
1562
  switch (pm) {
190
1563
  case "pnpm":
191
1564
  return `pnpm add -g ${spec}`;
@@ -199,13 +1572,13 @@ function getGlobalUpdateCommand(pm, pkg, version) {
199
1572
  }
200
1573
  }
201
1574
  async function detectPackageManager(cwd) {
202
- if (await pathExists(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
203
- if (await pathExists(path4.join(cwd, "bun.lockb"))) return "bun";
204
- if (await pathExists(path4.join(cwd, "yarn.lock"))) return "yarn";
1575
+ if (await pathExists(path7.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1576
+ if (await pathExists(path7.join(cwd, "bun.lockb"))) return "bun";
1577
+ if (await pathExists(path7.join(cwd, "yarn.lock"))) return "yarn";
205
1578
  return "npm";
206
1579
  }
207
- function getInstallCommand(pm, pkg, version, isDev) {
208
- const spec = `${pkg}@${version}`;
1580
+ function getInstallCommand(pm, pkg2, version, isDev) {
1581
+ const spec = `${pkg2}@${version}`;
209
1582
  switch (pm) {
210
1583
  case "pnpm":
211
1584
  return isDev ? `pnpm add -D ${spec}` : `pnpm add ${spec}`;
@@ -220,30 +1593,30 @@ function getInstallCommand(pm, pkg, version, isDev) {
220
1593
  }
221
1594
  async function isDevDependency(cwd) {
222
1595
  try {
223
- const pkgPath = path4.join(cwd, "package.json");
1596
+ const pkgPath = path7.join(cwd, "package.json");
224
1597
  const raw = await (await import("fs/promises")).readFile(pkgPath, "utf-8");
225
- const pkg = JSON.parse(raw);
226
- return Boolean(pkg.devDependencies?.["@vibgrate/cli"]);
1598
+ const pkg2 = JSON.parse(raw);
1599
+ return Boolean(pkg2.devDependencies?.["@vibgrate/cli"]);
227
1600
  } catch {
228
1601
  return true;
229
1602
  }
230
1603
  }
231
- var updateCommand = new Command3("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) => {
232
- console.log(chalk3.dim(`Current version: ${VERSION}`));
233
- console.log(chalk3.dim("Checking npm registry..."));
1604
+ var updateCommand = new Command6("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) => {
1605
+ console.log(chalk7.dim(`Current version: ${VERSION}`));
1606
+ console.log(chalk7.dim("Checking npm registry..."));
234
1607
  const latest = await fetchLatestVersion();
235
1608
  if (!latest) {
236
- console.error(chalk3.red("Could not reach the npm registry. Check your network connection."));
1609
+ console.error(chalk7.red("Could not reach the npm registry. Check your network connection."));
237
1610
  process.exit(1);
238
1611
  }
239
- const semver2 = await import("semver");
1612
+ const semver2 = await import("./semver-JBJZTHUX.js");
240
1613
  if (!semver2.gt(latest, VERSION)) {
241
- console.log(chalk3.green("\u2714") + ` You are on the latest version (${VERSION}).`);
1614
+ console.log(chalk7.green("\u2714") + ` You are on the latest version (${VERSION}).`);
242
1615
  return;
243
1616
  }
244
- console.log(chalk3.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
1617
+ console.log(chalk7.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
245
1618
  if (opts.check) {
246
- console.log(chalk3.dim('Run "vibgrate update" to install.'));
1619
+ console.log(chalk7.dim('Run "vibgrate update" to install.'));
247
1620
  return;
248
1621
  }
249
1622
  const cwd = process.cwd();
@@ -253,26 +1626,26 @@ var updateCommand = new Command3("update").description("Update vibgrate to the l
253
1626
  let cmd;
254
1627
  if (isGlobal) {
255
1628
  cmd = getGlobalUpdateCommand(pm, "@vibgrate/cli", latest);
256
- console.log(chalk3.dim(`Updating global installation with ${pm}: ${cmd}`));
1629
+ console.log(chalk7.dim(`Updating global installation with ${pm}: ${cmd}`));
257
1630
  } else {
258
1631
  const isDev = await isDevDependency(cwd);
259
1632
  cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev);
260
- console.log(chalk3.dim(`Using ${pm}: ${cmd}`));
1633
+ console.log(chalk7.dim(`Using ${pm}: ${cmd}`));
261
1634
  }
262
1635
  try {
263
1636
  execSync(cmd, { cwd, stdio: "inherit" });
264
- console.log(chalk3.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
1637
+ console.log(chalk7.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
265
1638
  } catch {
266
- console.error(chalk3.red(`Update failed. Run manually: ${cmd}`));
1639
+ console.error(chalk7.red(`Update failed. Run manually: ${cmd}`));
267
1640
  process.exit(1);
268
1641
  }
269
1642
  });
270
1643
 
271
1644
  // src/commands/sbom.ts
272
- import * as path5 from "path";
1645
+ import * as path8 from "path";
273
1646
  import { randomUUID } from "crypto";
274
- import { Command as Command4 } from "commander";
275
- import chalk4 from "chalk";
1647
+ import { Command as Command7 } from "commander";
1648
+ import chalk8 from "chalk";
276
1649
  function flattenDependencies(artifact) {
277
1650
  const rows = [];
278
1651
  for (const project of artifact.projects) {
@@ -410,50 +1783,50 @@ function formatDeltaText(base, current) {
410
1783
  return lines.join("\n");
411
1784
  }
412
1785
  async function readArtifactOrExit(filePath) {
413
- const absolutePath = path5.resolve(filePath);
1786
+ const absolutePath = path8.resolve(filePath);
414
1787
  if (!await pathExists(absolutePath)) {
415
- console.error(chalk4.red(`Artifact not found: ${absolutePath}`));
1788
+ console.error(chalk8.red(`Artifact not found: ${absolutePath}`));
416
1789
  process.exit(1);
417
1790
  }
418
1791
  return readJsonFile(absolutePath);
419
1792
  }
420
- var exportCommand = new Command4("export").description("Export scan artifact as SBOM").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--out <file>", "Output SBOM file").option("--format <format>", "SBOM format (cyclonedx|spdx)", "cyclonedx").action(async (opts) => {
1793
+ var exportCommand = new Command7("export").description("Export scan artifact as SBOM").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--out <file>", "Output SBOM file").option("--format <format>", "SBOM format (cyclonedx|spdx)", "cyclonedx").action(async (opts) => {
421
1794
  const artifact = await readArtifactOrExit(opts.in);
422
1795
  const format = opts.format.toLowerCase();
423
1796
  if (format !== "cyclonedx" && format !== "spdx") {
424
- console.error(chalk4.red("Invalid SBOM format. Use cyclonedx or spdx."));
1797
+ console.error(chalk8.red("Invalid SBOM format. Use cyclonedx or spdx."));
425
1798
  process.exit(1);
426
1799
  }
427
1800
  const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
428
1801
  const body = JSON.stringify(sbom, null, 2);
429
1802
  if (opts.out) {
430
- await writeTextFile(path5.resolve(opts.out), body);
431
- console.log(chalk4.green("\u2714") + ` SBOM written to ${opts.out}`);
1803
+ await writeTextFile(path8.resolve(opts.out), body);
1804
+ console.log(chalk8.green("\u2714") + ` SBOM written to ${opts.out}`);
432
1805
  } else {
433
1806
  console.log(body);
434
1807
  }
435
1808
  });
436
- var deltaCommand = new Command4("delta").description("Show SBOM delta between two scan artifacts").requiredOption("--from <file>", "Baseline scan artifact path").requiredOption("--to <file>", "Current scan artifact path").option("--out <file>", "Write report to file").action(async (opts) => {
1809
+ var deltaCommand = new Command7("delta").description("Show SBOM delta between two scan artifacts").requiredOption("--from <file>", "Baseline scan artifact path").requiredOption("--to <file>", "Current scan artifact path").option("--out <file>", "Write report to file").action(async (opts) => {
437
1810
  const base = await readArtifactOrExit(opts.from);
438
1811
  const current = await readArtifactOrExit(opts.to);
439
1812
  const report = formatDeltaText(base, current);
440
1813
  if (opts.out) {
441
- await writeTextFile(path5.resolve(opts.out), report);
442
- console.log(chalk4.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
1814
+ await writeTextFile(path8.resolve(opts.out), report);
1815
+ console.log(chalk8.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
443
1816
  } else {
444
1817
  console.log(report);
445
1818
  }
446
1819
  });
447
- var sbomCommand = new Command4("sbom").description("SBOM export and delta reports for dependency drift tracking").addCommand(exportCommand).addCommand(deltaCommand);
1820
+ var sbomCommand = new Command7("sbom").description("SBOM export and delta reports for dependency drift tracking").addCommand(exportCommand).addCommand(deltaCommand);
448
1821
 
449
1822
  // src/commands/extract.ts
450
- import * as path6 from "path";
1823
+ import * as path9 from "path";
451
1824
  import * as os2 from "os";
452
1825
  import * as fs2 from "fs/promises";
453
1826
  import { existsSync } from "fs";
454
1827
  import { spawn, spawnSync } from "child_process";
455
- import { Command as Command5 } from "commander";
456
- import chalk5 from "chalk";
1828
+ import { Command as Command8 } from "commander";
1829
+ import chalk9 from "chalk";
457
1830
  var EXIT_SUCCESS = 0;
458
1831
  var EXIT_SCHEMA_FAILURE = 1;
459
1832
  var EXIT_PARSE_FAILURE = 2;
@@ -543,8 +1916,8 @@ async function detectLanguages(rootDir, includeTests) {
543
1916
  }
544
1917
  for (const entry of entries) {
545
1918
  if (entry.name.startsWith(".")) continue;
546
- const absPath = path6.join(dir, entry.name);
547
- const relPath = path6.join(relBase, entry.name);
1919
+ const absPath = path9.join(dir, entry.name);
1920
+ const relPath = path9.join(relBase, entry.name);
548
1921
  if (entry.isDirectory()) {
549
1922
  if (!SKIP_DIRS.has(entry.name)) {
550
1923
  await walk(absPath, relPath);
@@ -553,7 +1926,7 @@ async function detectLanguages(rootDir, includeTests) {
553
1926
  }
554
1927
  if (!entry.isFile()) continue;
555
1928
  if (!includeTests && isTestPath(relPath)) continue;
556
- const ext = path6.extname(entry.name).toLowerCase();
1929
+ const ext = path9.extname(entry.name).toLowerCase();
557
1930
  for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
558
1931
  if (exts.has(ext)) {
559
1932
  counts.set(lang, (counts.get(lang) ?? 0) + 1);
@@ -629,10 +2002,10 @@ function splitHcsOutput(lines) {
629
2002
  return { preamble, facts };
630
2003
  }
631
2004
  function resolveHcsWorkerBin() {
632
- const base = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
633
- const bundledPath = path6.resolve(base, "..", "dist", "hcs-worker.js");
2005
+ const base = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
2006
+ const bundledPath = path9.resolve(base, "..", "dist", "hcs-worker.js");
634
2007
  if (existsSync(bundledPath)) return bundledPath;
635
- const siblingPath = path6.resolve(base, "hcs-worker.js");
2008
+ const siblingPath = path9.resolve(base, "hcs-worker.js");
636
2009
  if (existsSync(siblingPath)) return siblingPath;
637
2010
  try {
638
2011
  const resolved = import.meta.resolve("@vibgrate/hcs-node-worker");
@@ -640,9 +2013,9 @@ function resolveHcsWorkerBin() {
640
2013
  if (existsSync(resolvedPath)) return resolvedPath;
641
2014
  } catch {
642
2015
  }
643
- const monorepoPath = path6.resolve(base, "..", "..", "..", "vibgrate-hcs", "node", "dist", "main.js");
2016
+ const monorepoPath = path9.resolve(base, "..", "..", "..", "vibgrate-hcs", "node", "dist", "main.js");
644
2017
  if (existsSync(monorepoPath)) return monorepoPath;
645
- const devPath = path6.resolve(base, "..", "..", "..", "vibgrate-hcs", "node", "src", "main.ts");
2018
+ const devPath = path9.resolve(base, "..", "..", "..", "vibgrate-hcs", "node", "src", "main.ts");
646
2019
  if (existsSync(devPath)) return devPath;
647
2020
  throw new Error(
648
2021
  'Cannot locate HCS worker. Run "pnpm build" in the CLI package to bundle the worker into dist/.'
@@ -745,7 +2118,7 @@ async function runNodeWorker(rootDir, language, opts) {
745
2118
  args.push("--cobol-copybook-paths", opts.copybookPaths);
746
2119
  }
747
2120
  }
748
- return new Promise((resolve6) => {
2121
+ return new Promise((resolve9) => {
749
2122
  const facts = [];
750
2123
  const errors = [];
751
2124
  let stdoutBuf = "";
@@ -782,7 +2155,7 @@ async function runNodeWorker(rootDir, language, opts) {
782
2155
  continue;
783
2156
  }
784
2157
  if (opts.verbose) {
785
- process.stderr.write(chalk5.dim(`[${language}] ${trimmed}
2158
+ process.stderr.write(chalk9.dim(`[${language}] ${trimmed}
786
2159
  `));
787
2160
  }
788
2161
  if (trimmed.startsWith("[error]")) {
@@ -796,15 +2169,15 @@ async function runNodeWorker(rootDir, language, opts) {
796
2169
  facts.push(stdoutBuf.trim());
797
2170
  }
798
2171
  if (killed) {
799
- resolve6({ language, facts, errors: ["Worker killed: timeout exceeded"], exitCode: EXIT_TIMEOUT });
2172
+ resolve9({ language, facts, errors: ["Worker killed: timeout exceeded"], exitCode: EXIT_TIMEOUT });
800
2173
  } else {
801
- resolve6({ language, facts, errors, exitCode: code ?? 0 });
2174
+ resolve9({ language, facts, errors, exitCode: code ?? 0 });
802
2175
  }
803
2176
  });
804
2177
  child.on("error", (err) => {
805
2178
  clearTimeout(timer);
806
2179
  errors.push(`Failed to spawn worker: ${err.message}`);
807
- resolve6({ language, facts, errors, exitCode: EXIT_PARSE_FAILURE });
2180
+ resolve9({ language, facts, errors, exitCode: EXIT_PARSE_FAILURE });
808
2181
  });
809
2182
  });
810
2183
  }
@@ -824,7 +2197,7 @@ function resolveDotnetPublishedWorker(workersDir) {
824
2197
  };
825
2198
  const candidates = candidatesByPlatform[process.platform] ?? [];
826
2199
  for (const filename of candidates) {
827
- const fullPath = path6.join(workersDir, filename);
2200
+ const fullPath = path9.join(workersDir, filename);
828
2201
  if (existsSync(fullPath)) {
829
2202
  return fullPath;
830
2203
  }
@@ -832,30 +2205,30 @@ function resolveDotnetPublishedWorker(workersDir) {
832
2205
  return null;
833
2206
  }
834
2207
  function resolveNativeWorker(language, projectDir) {
835
- const base = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
836
- const hcsFromBundle = path6.resolve(base, "..", "..", "vibgrate-hcs");
837
- const hcsFromSrc = path6.resolve(base, "..", "..", "..", "vibgrate-hcs");
2208
+ const base = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
2209
+ const hcsFromBundle = path9.resolve(base, "..", "..", "vibgrate-hcs");
2210
+ const hcsFromSrc = path9.resolve(base, "..", "..", "..", "vibgrate-hcs");
838
2211
  const hcsRoot = existsSync(hcsFromBundle) ? hcsFromBundle : hcsFromSrc;
839
- const workersDir = path6.resolve(base, "workers");
2212
+ const workersDir = path9.resolve(base, "workers");
840
2213
  switch (language) {
841
2214
  case "go": {
842
- const bin = path6.join(workersDir, process.platform === "win32" ? "vibgrate-hcs-go.exe" : "vibgrate-hcs-go");
2215
+ const bin = path9.join(workersDir, process.platform === "win32" ? "vibgrate-hcs-go.exe" : "vibgrate-hcs-go");
843
2216
  if (existsSync(bin)) {
844
2217
  return { cmd: bin, args: ["--project", projectDir, "--output", "ndjson"] };
845
2218
  }
846
- const src = path6.join(hcsRoot, "go");
847
- if (existsSync(path6.join(src, "main.go"))) {
2219
+ const src = path9.join(hcsRoot, "go");
2220
+ if (existsSync(path9.join(src, "main.go"))) {
848
2221
  return { cmd: "go", args: ["run", ".", "--project", projectDir, "--output", "ndjson"], cwd: src };
849
2222
  }
850
2223
  return null;
851
2224
  }
852
2225
  case "python": {
853
- const bin = path6.join(workersDir, "vibgrate-hcs-python");
2226
+ const bin = path9.join(workersDir, "vibgrate-hcs-python");
854
2227
  if (existsSync(bin)) {
855
2228
  return { cmd: bin, args: ["--project", projectDir, "--output", "ndjson"] };
856
2229
  }
857
- const src = path6.join(hcsRoot, "python");
858
- if (existsSync(path6.join(src, "pyproject.toml"))) {
2230
+ const src = path9.join(hcsRoot, "python");
2231
+ if (existsSync(path9.join(src, "pyproject.toml"))) {
859
2232
  return {
860
2233
  cmd: "python3",
861
2234
  args: ["-m", "vibgrate_hcs_python.main", "--project", projectDir, "--output", "ndjson"],
@@ -865,13 +2238,13 @@ function resolveNativeWorker(language, projectDir) {
865
2238
  return null;
866
2239
  }
867
2240
  case "java": {
868
- const jar = path6.join(workersDir, "vibgrate-hcs-jvm.jar");
2241
+ const jar = path9.join(workersDir, "vibgrate-hcs-jvm.jar");
869
2242
  if (existsSync(jar)) {
870
2243
  return { cmd: "java", args: ["-jar", jar, "--project", projectDir, "--output", "ndjson"] };
871
2244
  }
872
- const src = path6.join(hcsRoot, "jvm");
873
- if (existsSync(path6.join(src, "build.gradle.kts"))) {
874
- const gradlew = path6.join(src, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2245
+ const src = path9.join(hcsRoot, "jvm");
2246
+ if (existsSync(path9.join(src, "build.gradle.kts"))) {
2247
+ const gradlew = path9.join(src, process.platform === "win32" ? "gradlew.bat" : "gradlew");
875
2248
  const launcher = existsSync(gradlew) ? gradlew : "gradle";
876
2249
  return {
877
2250
  cmd: launcher,
@@ -887,11 +2260,11 @@ function resolveNativeWorker(language, projectDir) {
887
2260
  if (publishedWorker) {
888
2261
  return { cmd: publishedWorker, args: ["--project", projectDir, "--output", "ndjson"] };
889
2262
  }
890
- const dll = path6.join(workersDir, "VibgrateHcsWorker.dll");
2263
+ const dll = path9.join(workersDir, "VibgrateHcsWorker.dll");
891
2264
  if (existsSync(dll)) {
892
2265
  return { cmd: "dotnet", args: [dll, "--project", projectDir, "--output", "ndjson"] };
893
2266
  }
894
- const csproj = path6.join(hcsRoot, "dotnet", "src", "VibgrateHcsWorker", "VibgrateHcsWorker.csproj");
2267
+ const csproj = path9.join(hcsRoot, "dotnet", "src", "VibgrateHcsWorker", "VibgrateHcsWorker.csproj");
895
2268
  if (existsSync(csproj)) {
896
2269
  return {
897
2270
  cmd: "dotnet",
@@ -917,19 +2290,19 @@ async function runNativeWorker(rootDir, language, opts) {
917
2290
  exitCode: EXIT_PARSE_FAILURE
918
2291
  };
919
2292
  }
920
- return new Promise((resolve6) => {
2293
+ return new Promise((resolve9) => {
921
2294
  const facts = [];
922
2295
  const errors = [];
923
2296
  let stdoutBuf = "";
924
2297
  let killed = false;
925
2298
  if (opts.verbose) {
926
- process.stderr.write(chalk5.dim(`[${language}] Spawning: ${spec.cmd} ${spec.args.join(" ")}
2299
+ process.stderr.write(chalk9.dim(`[${language}] Spawning: ${spec.cmd} ${spec.args.join(" ")}
927
2300
  `));
928
2301
  }
929
2302
  const runtimeReq = NATIVE_RUNTIME_REQUIREMENTS[language];
930
2303
  const usesPathCommand = runtimeReq && spec.cmd === runtimeReq.command;
931
2304
  if (usesPathCommand && !commandExistsOnPath(spec.cmd)) {
932
- resolve6({
2305
+ resolve9({
933
2306
  language,
934
2307
  facts: [],
935
2308
  errors: [
@@ -964,7 +2337,7 @@ async function runNativeWorker(rootDir, language, opts) {
964
2337
  const trimmed = line.trim();
965
2338
  if (!trimmed) continue;
966
2339
  if (opts.verbose) {
967
- process.stderr.write(chalk5.dim(`[${language}] ${trimmed}
2340
+ process.stderr.write(chalk9.dim(`[${language}] ${trimmed}
968
2341
  `));
969
2342
  }
970
2343
  if (trimmed.startsWith("[error]")) {
@@ -976,9 +2349,9 @@ async function runNativeWorker(rootDir, language, opts) {
976
2349
  clearTimeout(timer);
977
2350
  if (stdoutBuf.trim()) facts.push(stdoutBuf.trim());
978
2351
  if (killed) {
979
- resolve6({ language, facts, errors: ["Worker killed: timeout exceeded"], exitCode: EXIT_TIMEOUT });
2352
+ resolve9({ language, facts, errors: ["Worker killed: timeout exceeded"], exitCode: EXIT_TIMEOUT });
980
2353
  } else {
981
- resolve6({ language, facts, errors, exitCode: code ?? 0 });
2354
+ resolve9({ language, facts, errors, exitCode: code ?? 0 });
982
2355
  }
983
2356
  });
984
2357
  child.on("error", (err) => {
@@ -987,21 +2360,21 @@ async function runNativeWorker(rootDir, language, opts) {
987
2360
  errors.push(
988
2361
  isNotFound ? `[error] '${spec.cmd}' not found. ${buildNativeInstallHint(language)}` : `[error] Failed to spawn native worker: ${err.message}`
989
2362
  );
990
- resolve6({ language, facts, errors, exitCode: EXIT_PARSE_FAILURE });
2363
+ resolve9({ language, facts, errors, exitCode: EXIT_PARSE_FAILURE });
991
2364
  });
992
2365
  });
993
2366
  }
994
2367
  async function pushFacts(facts, dsn, verbose) {
995
- const parsed = parseDsn(dsn);
2368
+ const parsed = parseDsn2(dsn);
996
2369
  if (!parsed) {
997
- process.stderr.write(chalk5.red("Invalid DSN format.\n"));
998
- process.stderr.write(chalk5.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
2370
+ process.stderr.write(chalk9.red("Invalid DSN format.\n"));
2371
+ process.stderr.write(chalk9.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
999
2372
  return false;
1000
2373
  }
1001
2374
  const body = facts.join("\n") + "\n";
1002
2375
  const url = `${parsed.scheme}://${parsed.host}/v1/ingest/hcs`;
1003
2376
  if (verbose) {
1004
- process.stderr.write(chalk5.dim(`Pushing ${facts.length} facts to ${parsed.host}...
2377
+ process.stderr.write(chalk9.dim(`Pushing ${facts.length} facts to ${parsed.host}...
1005
2378
  `));
1006
2379
  }
1007
2380
  try {
@@ -1019,17 +2392,17 @@ async function pushFacts(facts, dsn, verbose) {
1019
2392
  });
1020
2393
  if (!response.ok) {
1021
2394
  const text = await response.text().catch(() => "");
1022
- process.stderr.write(chalk5.red(`Push failed: HTTP ${response.status} ${text}
2395
+ process.stderr.write(chalk9.red(`Push failed: HTTP ${response.status} ${text}
1023
2396
  `));
1024
2397
  return false;
1025
2398
  }
1026
2399
  if (verbose) {
1027
- process.stderr.write(chalk5.green(`\u2714 Pushed ${facts.length} facts successfully
2400
+ process.stderr.write(chalk9.green(`\u2714 Pushed ${facts.length} facts successfully
1028
2401
  `));
1029
2402
  }
1030
2403
  return true;
1031
2404
  } catch (err) {
1032
- process.stderr.write(chalk5.red(`Push failed: ${err instanceof Error ? err.message : String(err)}
2405
+ process.stderr.write(chalk9.red(`Push failed: ${err instanceof Error ? err.message : String(err)}
1033
2406
  `));
1034
2407
  return false;
1035
2408
  }
@@ -1096,21 +2469,21 @@ var ProgressTracker = class {
1096
2469
  const parts = [];
1097
2470
  for (const [lang, st] of this.langStatus) {
1098
2471
  if (st.done) {
1099
- parts.push(chalk5.green(`${lang} \u2713`));
2472
+ parts.push(chalk9.green(`${lang} \u2713`));
1100
2473
  } else if (st.phase === "waiting") {
1101
- parts.push(chalk5.dim(`${lang} \xB7`));
2474
+ parts.push(chalk9.dim(`${lang} \xB7`));
1102
2475
  } else if (st.phase === "discovering") {
1103
- parts.push(chalk5.yellow(`${lang} \u2026`));
2476
+ parts.push(chalk9.yellow(`${lang} \u2026`));
1104
2477
  } else if (st.phase === "scanning" && st.fileCount > 0) {
1105
2478
  const pct = Math.round(st.fileIndex / st.fileCount * 100);
1106
- parts.push(chalk5.cyan(`${lang} ${st.fileIndex}/${st.fileCount} ${pct}%`));
2479
+ parts.push(chalk9.cyan(`${lang} ${st.fileIndex}/${st.fileCount} ${pct}%`));
1107
2480
  } else if (st.phase === "extracting") {
1108
- parts.push(chalk5.cyan(`${lang} extracting`));
2481
+ parts.push(chalk9.cyan(`${lang} extracting`));
1109
2482
  } else {
1110
- parts.push(chalk5.dim(`${lang} ${st.phase}`));
2483
+ parts.push(chalk9.dim(`${lang} ${st.phase}`));
1111
2484
  }
1112
2485
  }
1113
- const line = ` ${parts.join(" ")} ${chalk5.dim(`${this.totalFacts} facts`)}`;
2486
+ const line = ` ${parts.join(" ")} ${chalk9.dim(`${this.totalFacts} facts`)}`;
1114
2487
  const padding = Math.max(0, this.lastLineLen - line.length);
1115
2488
  process.stderr.write(`\r${line}${" ".repeat(padding)}`);
1116
2489
  this.lastLineLen = line.length;
@@ -1122,10 +2495,10 @@ var ProgressTracker = class {
1122
2495
  }
1123
2496
  }
1124
2497
  };
1125
- var extractCommand = new Command5("extract").description("Analyze source code and emit validated HCS facts (NDJSON)").argument("[path]", "Path to source directory", ".").option("-o, --out <file>", "Write NDJSON to file (default: stdout)").option("--language <langs>", "Comma-separated languages to analyze (default: auto-detect)").option("--include-tests", "Include test files in analysis").option("--push", "Stream validated facts to dashboard API").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--concurrency <n>", "Number of parallel file workers").option("--timeout-mins <mins>", "Total analysis timeout in minutes", "60").option("--feedback <file>", "Load NDJSON diff artifact for refinement").option("--verbose", "Print worker stderr and summary statistics").action(async (targetPath, opts) => {
1126
- const rootDir = path6.resolve(targetPath);
2498
+ var extractCommand = new Command8("extract").description("Analyze source code and emit validated HCS facts (NDJSON)").argument("[path]", "Path to source directory", ".").option("-o, --out <file>", "Write NDJSON to file (default: stdout)").option("--language <langs>", "Comma-separated languages to analyze (default: auto-detect)").option("--include-tests", "Include test files in analysis").option("--push", "Stream validated facts to dashboard API").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--concurrency <n>", "Number of parallel file workers").option("--timeout-mins <mins>", "Total analysis timeout in minutes", "60").option("--feedback <file>", "Load NDJSON diff artifact for refinement").option("--verbose", "Print worker stderr and summary statistics").action(async (targetPath, opts) => {
2499
+ const rootDir = path9.resolve(targetPath);
1127
2500
  if (!await pathExists(rootDir)) {
1128
- process.stderr.write(chalk5.red(`Path does not exist: ${rootDir}
2501
+ process.stderr.write(chalk9.red(`Path does not exist: ${rootDir}
1129
2502
  `));
1130
2503
  process.exit(EXIT_USAGE_ERROR);
1131
2504
  }
@@ -1133,47 +2506,47 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1133
2506
  try {
1134
2507
  stat3 = await fs2.stat(rootDir);
1135
2508
  } catch {
1136
- process.stderr.write(chalk5.red(`Cannot read path: ${rootDir}
2509
+ process.stderr.write(chalk9.red(`Cannot read path: ${rootDir}
1137
2510
  `));
1138
2511
  process.exit(EXIT_USAGE_ERROR);
1139
2512
  }
1140
2513
  if (!stat3.isDirectory()) {
1141
- process.stderr.write(chalk5.red(`Path must be a directory: ${rootDir}
2514
+ process.stderr.write(chalk9.red(`Path must be a directory: ${rootDir}
1142
2515
  `));
1143
2516
  process.exit(EXIT_USAGE_ERROR);
1144
2517
  }
1145
2518
  const dsn = opts.dsn || process.env.VIBGRATE_DSN;
1146
2519
  if (opts.push && !dsn) {
1147
- process.stderr.write(chalk5.red("--push requires --dsn or VIBGRATE_DSN environment variable\n"));
2520
+ process.stderr.write(chalk9.red("--push requires --dsn or VIBGRATE_DSN environment variable\n"));
1148
2521
  process.exit(EXIT_USAGE_ERROR);
1149
2522
  }
1150
- if (dsn && !parseDsn(dsn)) {
1151
- process.stderr.write(chalk5.red("Invalid DSN format.\n"));
1152
- process.stderr.write(chalk5.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
2523
+ if (dsn && !parseDsn2(dsn)) {
2524
+ process.stderr.write(chalk9.red("Invalid DSN format.\n"));
2525
+ process.stderr.write(chalk9.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
1153
2526
  process.exit(EXIT_USAGE_ERROR);
1154
2527
  }
1155
2528
  const concurrency = opts.concurrency ? parseInt(opts.concurrency, 10) : os2.cpus().length;
1156
2529
  if (isNaN(concurrency) || concurrency < 1) {
1157
- process.stderr.write(chalk5.red("--concurrency must be >= 1\n"));
2530
+ process.stderr.write(chalk9.red("--concurrency must be >= 1\n"));
1158
2531
  process.exit(EXIT_USAGE_ERROR);
1159
2532
  }
1160
2533
  const timeoutMins = parseInt(opts.timeoutMins, 10);
1161
2534
  if (isNaN(timeoutMins) || timeoutMins < 1) {
1162
- process.stderr.write(chalk5.red("--timeout-mins must be >= 1\n"));
2535
+ process.stderr.write(chalk9.red("--timeout-mins must be >= 1\n"));
1163
2536
  process.exit(EXIT_USAGE_ERROR);
1164
2537
  }
1165
2538
  const timeoutMs = timeoutMins * 60 * 1e3;
1166
2539
  if (opts.out) {
1167
- const outDir = path6.dirname(path6.resolve(opts.out));
2540
+ const outDir = path9.dirname(path9.resolve(opts.out));
1168
2541
  if (!await pathExists(outDir)) {
1169
- process.stderr.write(chalk5.red(`Output directory does not exist: ${outDir}
2542
+ process.stderr.write(chalk9.red(`Output directory does not exist: ${outDir}
1170
2543
  `));
1171
2544
  process.exit(EXIT_USAGE_ERROR);
1172
2545
  }
1173
2546
  try {
1174
- const outStat = await fs2.stat(path6.resolve(opts.out));
2547
+ const outStat = await fs2.stat(path9.resolve(opts.out));
1175
2548
  if (outStat.isDirectory()) {
1176
- process.stderr.write(chalk5.red(`--out cannot be a directory: ${opts.out}
2549
+ process.stderr.write(chalk9.red(`--out cannot be a directory: ${opts.out}
1177
2550
  `));
1178
2551
  process.exit(EXIT_USAGE_ERROR);
1179
2552
  }
@@ -1182,16 +2555,16 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1182
2555
  }
1183
2556
  let feedbackLines;
1184
2557
  if (opts.feedback) {
1185
- const feedbackPath = path6.resolve(opts.feedback);
2558
+ const feedbackPath = path9.resolve(opts.feedback);
1186
2559
  if (!await pathExists(feedbackPath)) {
1187
- process.stderr.write(chalk5.red(`Feedback file not found: ${feedbackPath}
2560
+ process.stderr.write(chalk9.red(`Feedback file not found: ${feedbackPath}
1188
2561
  `));
1189
2562
  process.exit(EXIT_USAGE_ERROR);
1190
2563
  }
1191
2564
  try {
1192
2565
  feedbackLines = await loadFeedback(feedbackPath);
1193
2566
  } catch (err) {
1194
- process.stderr.write(chalk5.red(`Invalid feedback file: ${err instanceof Error ? err.message : String(err)}
2567
+ process.stderr.write(chalk9.red(`Invalid feedback file: ${err instanceof Error ? err.message : String(err)}
1195
2568
  `));
1196
2569
  process.exit(EXIT_USAGE_ERROR);
1197
2570
  }
@@ -1201,9 +2574,9 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1201
2574
  targetLanguages = opts.language.split(",").map((l) => normalizeLanguageToken(l)).filter(Boolean);
1202
2575
  for (const lang of targetLanguages) {
1203
2576
  if (!SUPPORTED_LANGUAGES.has(lang)) {
1204
- process.stderr.write(chalk5.red(`Unknown language: "${lang}"
2577
+ process.stderr.write(chalk9.red(`Unknown language: "${lang}"
1205
2578
  `));
1206
- process.stderr.write(chalk5.dim(`Supported: ${[...SUPPORTED_LANGUAGES].sort().join(", ")}
2579
+ process.stderr.write(chalk9.dim(`Supported: ${[...SUPPORTED_LANGUAGES].sort().join(", ")}
1207
2580
  `));
1208
2581
  process.exit(EXIT_USAGE_ERROR);
1209
2582
  }
@@ -1211,14 +2584,14 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1211
2584
  } else {
1212
2585
  const detected = await detectLanguages(rootDir, opts.includeTests ?? false);
1213
2586
  if (detected.length === 0) {
1214
- process.stderr.write(chalk5.yellow("No supported source files detected.\n"));
2587
+ process.stderr.write(chalk9.yellow("No supported source files detected.\n"));
1215
2588
  process.exit(EXIT_SUCCESS);
1216
2589
  }
1217
2590
  targetLanguages = detected.map((d) => d.language);
1218
2591
  if (opts.verbose) {
1219
- process.stderr.write(chalk5.dim("Detected languages:\n"));
2592
+ process.stderr.write(chalk9.dim("Detected languages:\n"));
1220
2593
  for (const d of detected) {
1221
- process.stderr.write(chalk5.dim(` ${d.language}: ${d.fileCount} files
2594
+ process.stderr.write(chalk9.dim(` ${d.language}: ${d.fileCount} files
1222
2595
  `));
1223
2596
  }
1224
2597
  }
@@ -1226,20 +2599,20 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1226
2599
  const runnableLanguages = targetLanguages.filter((l) => ALL_WORKER_LANGS.has(l));
1227
2600
  const unknownWorkerLangs = targetLanguages.filter((l) => !ALL_WORKER_LANGS.has(l));
1228
2601
  if (unknownWorkerLangs.length > 0 && opts.verbose) {
1229
- process.stderr.write(chalk5.dim(
2602
+ process.stderr.write(chalk9.dim(
1230
2603
  `No worker registered for: ${unknownWorkerLangs.join(", ")} \u2014 skipping.
1231
2604
  `
1232
2605
  ));
1233
2606
  }
1234
2607
  if (runnableLanguages.length === 0) {
1235
- process.stderr.write(chalk5.yellow("No languages with available HCS workers found.\n"));
2608
+ process.stderr.write(chalk9.yellow("No languages with available HCS workers found.\n"));
1236
2609
  process.exit(EXIT_SUCCESS);
1237
2610
  }
1238
2611
  const startTime = Date.now();
1239
2612
  const globalDeadline = startTime + timeoutMs;
1240
2613
  process.stderr.write(
1241
- chalk5.bold(`Extracting HCS facts from ${rootDir}
1242
- `) + chalk5.dim(`Languages: ${runnableLanguages.join(", ")} Concurrency: ${concurrency} Timeout: ${timeoutMins}m
2614
+ chalk9.bold(`Extracting HCS facts from ${rootDir}
2615
+ `) + chalk9.dim(`Languages: ${runnableLanguages.join(", ")} Concurrency: ${concurrency} Timeout: ${timeoutMins}m
1243
2616
  `)
1244
2617
  );
1245
2618
  const progress = new ProgressTracker(runnableLanguages);
@@ -1279,7 +2652,7 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1279
2652
  hasSchemaFailure = true;
1280
2653
  allErrors.push(`[${language}] Schema validation: ${validation.error}`);
1281
2654
  if (opts.verbose) {
1282
- process.stderr.write(chalk5.red(`[${language}] Invalid fact: ${validation.error}
2655
+ process.stderr.write(chalk9.red(`[${language}] Invalid fact: ${validation.error}
1283
2656
  `));
1284
2657
  }
1285
2658
  }
@@ -1307,9 +2680,9 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1307
2680
  const allLines = [...allPreamble, ...allFacts];
1308
2681
  const ndjsonOutput = allLines.join("\n") + (allLines.length > 0 ? "\n" : "");
1309
2682
  if (opts.out) {
1310
- const outPath = path6.resolve(opts.out);
2683
+ const outPath = path9.resolve(opts.out);
1311
2684
  await fs2.writeFile(outPath, ndjsonOutput, "utf-8");
1312
- process.stderr.write(chalk5.green(`\u2714 Wrote ${allFacts.length} facts to ${outPath}
2685
+ process.stderr.write(chalk9.green(`\u2714 Wrote ${allFacts.length} facts to ${outPath}
1313
2686
  `));
1314
2687
  } else {
1315
2688
  process.stdout.write(ndjsonOutput);
@@ -1321,48 +2694,48 @@ var extractCommand = new Command5("extract").description("Analyze source code an
1321
2694
  }
1322
2695
  }
1323
2696
  if (opts.verbose) {
1324
- process.stderr.write(chalk5.dim("\n\u2500\u2500 Summary \u2500\u2500\n"));
1325
- process.stderr.write(chalk5.dim(` Facts emitted : ${allFacts.length}
2697
+ process.stderr.write(chalk9.dim("\n\u2500\u2500 Summary \u2500\u2500\n"));
2698
+ process.stderr.write(chalk9.dim(` Facts emitted : ${allFacts.length}
1326
2699
  `));
1327
- process.stderr.write(chalk5.dim(` Languages : ${runnableLanguages.join(", ")}
2700
+ process.stderr.write(chalk9.dim(` Languages : ${runnableLanguages.join(", ")}
1328
2701
  `));
1329
- process.stderr.write(chalk5.dim(` Elapsed : ${elapsed}s
2702
+ process.stderr.write(chalk9.dim(` Elapsed : ${elapsed}s
1330
2703
  `));
1331
2704
  if (allErrors.length > 0) {
1332
- process.stderr.write(chalk5.dim(` Errors : ${allErrors.length}
2705
+ process.stderr.write(chalk9.dim(` Errors : ${allErrors.length}
1333
2706
  `));
1334
2707
  for (const err of allErrors.slice(0, 10)) {
1335
- process.stderr.write(chalk5.dim(` ${err}
2708
+ process.stderr.write(chalk9.dim(` ${err}
1336
2709
  `));
1337
2710
  }
1338
2711
  if (allErrors.length > 10) {
1339
- process.stderr.write(chalk5.dim(` ... and ${allErrors.length - 10} more
2712
+ process.stderr.write(chalk9.dim(` ... and ${allErrors.length - 10} more
1340
2713
  `));
1341
2714
  }
1342
2715
  }
1343
- process.stderr.write(chalk5.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n"));
2716
+ process.stderr.write(chalk9.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n"));
1344
2717
  }
1345
2718
  if (hasTimeout) {
1346
- process.stderr.write(chalk5.red(`Timeout exceeded (${timeoutMins} minutes)
2719
+ process.stderr.write(chalk9.red(`Timeout exceeded (${timeoutMins} minutes)
1347
2720
  `));
1348
2721
  process.exit(EXIT_TIMEOUT);
1349
2722
  }
1350
2723
  if (hasSchemaFailure) {
1351
- process.stderr.write(chalk5.red("Fact schema validation failures detected\n"));
2724
+ process.stderr.write(chalk9.red("Fact schema validation failures detected\n"));
1352
2725
  process.exit(EXIT_SCHEMA_FAILURE);
1353
2726
  }
1354
2727
  if (hasParseFailure) {
1355
- process.stderr.write(chalk5.red("Parsing failures detected\n"));
2728
+ process.stderr.write(chalk9.red("Parsing failures detected\n"));
1356
2729
  process.exit(EXIT_PARSE_FAILURE);
1357
2730
  }
1358
2731
  });
1359
2732
 
1360
2733
  // src/commands/diff.ts
1361
- import * as path7 from "path";
2734
+ import * as path10 from "path";
1362
2735
  import * as fs3 from "fs/promises";
1363
- import * as crypto from "crypto";
1364
- import { Command as Command6 } from "commander";
1365
- import chalk6 from "chalk";
2736
+ import * as crypto3 from "crypto";
2737
+ import { Command as Command9 } from "commander";
2738
+ import chalk10 from "chalk";
1366
2739
  var EXIT_INVALID_PATH = 2;
1367
2740
  var EXIT_USAGE_ERROR2 = 5;
1368
2741
  var VALID_FORMATS = /* @__PURE__ */ new Set(["ndjson", "json", "patch"]);
@@ -1384,8 +2757,8 @@ async function discoverFiles(root, includeGlobs, excludeGlobs) {
1384
2757
  }
1385
2758
  for (const entry of entries) {
1386
2759
  if (entry.name.startsWith(".") && SKIP_DIRS2.has(entry.name)) continue;
1387
- const absPath = path7.join(dir, entry.name);
1388
- const relPath = path7.join(relBase, entry.name);
2760
+ const absPath = path10.join(dir, entry.name);
2761
+ const relPath = path10.join(relBase, entry.name);
1389
2762
  if (entry.isDirectory()) {
1390
2763
  if (!SKIP_DIRS2.has(entry.name)) {
1391
2764
  await walk(absPath, relPath);
@@ -1556,7 +2929,7 @@ function lcsRatio(a, b) {
1556
2929
  return 2 * lcsLen / (n + m);
1557
2930
  }
1558
2931
  function generateDeltaId(kind, filePath) {
1559
- const hash = crypto.createHash("sha256").update(`${kind}:${filePath}`).digest("hex").substring(0, 16);
2932
+ const hash = crypto3.createHash("sha256").update(`${kind}:${filePath}`).digest("hex").substring(0, 16);
1560
2933
  return `hcs:delta:${hash}`;
1561
2934
  }
1562
2935
  function formatNdjson(deltas) {
@@ -1568,8 +2941,8 @@ function formatJson(deltas) {
1568
2941
  function formatPatch(deltas, originalPath, generatedPath) {
1569
2942
  const lines = [];
1570
2943
  for (const delta of deltas) {
1571
- const aFile = delta.originalPath ? path7.join("a", delta.originalPath) : "/dev/null";
1572
- const bFile = delta.generatedPath ? path7.join("b", delta.generatedPath) : "/dev/null";
2944
+ const aFile = delta.originalPath ? path10.join("a", delta.originalPath) : "/dev/null";
2945
+ const bFile = delta.generatedPath ? path10.join("b", delta.generatedPath) : "/dev/null";
1573
2946
  if (delta.changeKind === "renamed" && delta.originalPath && delta.generatedPath) {
1574
2947
  lines.push(`diff --vibgrate ${aFile} ${bFile}`);
1575
2948
  lines.push(`similarity index ${Math.round((delta.similarity ?? 0) * 100)}%`);
@@ -1590,12 +2963,12 @@ function formatPatch(deltas, originalPath, generatedPath) {
1590
2963
  }
1591
2964
  return lines.join("\n");
1592
2965
  }
1593
- var diffCommand = new Command6("diff").description("Compare two directory trees and emit structured delta facts").argument("<original_path>", "Original source directory").argument("<generated_path>", "Generated source directory").option("-o, --out <file>", "Output file path", "vibgrate.diff.ndjson").option("--format <fmt>", "Output format (ndjson|json|patch)", "ndjson").option("--ignore-whitespace", "Ignore whitespace-only changes").option("--context <n>", "Context lines for patch output", "3").option("--include <globs>", "Only include matching files (comma-separated)").option("--exclude <globs>", "Exclude matching files (comma-separated)").option("--stats", "Include insertions/deletions summary per file").option("--verbose", "Print file match decisions and rename detection").action(async (originalPath, generatedPath, opts) => {
1594
- const origDir = path7.resolve(originalPath);
1595
- const genDir = path7.resolve(generatedPath);
2966
+ var diffCommand = new Command9("diff").description("Compare two directory trees and emit structured delta facts").argument("<original_path>", "Original source directory").argument("<generated_path>", "Generated source directory").option("-o, --out <file>", "Output file path", "vibgrate.diff.ndjson").option("--format <fmt>", "Output format (ndjson|json|patch)", "ndjson").option("--ignore-whitespace", "Ignore whitespace-only changes").option("--context <n>", "Context lines for patch output", "3").option("--include <globs>", "Only include matching files (comma-separated)").option("--exclude <globs>", "Exclude matching files (comma-separated)").option("--stats", "Include insertions/deletions summary per file").option("--verbose", "Print file match decisions and rename detection").action(async (originalPath, generatedPath, opts) => {
2967
+ const origDir = path10.resolve(originalPath);
2968
+ const genDir = path10.resolve(generatedPath);
1596
2969
  for (const [label, dir] of [["original_path", origDir], ["generated_path", genDir]]) {
1597
2970
  if (!await pathExists(dir)) {
1598
- process.stderr.write(chalk6.red(`${label} does not exist: ${dir}
2971
+ process.stderr.write(chalk10.red(`${label} does not exist: ${dir}
1599
2972
  `));
1600
2973
  process.exit(EXIT_INVALID_PATH);
1601
2974
  }
@@ -1603,25 +2976,25 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1603
2976
  try {
1604
2977
  stat3 = await fs3.stat(dir);
1605
2978
  } catch {
1606
- process.stderr.write(chalk6.red(`Cannot read ${label}: ${dir}
2979
+ process.stderr.write(chalk10.red(`Cannot read ${label}: ${dir}
1607
2980
  `));
1608
2981
  process.exit(EXIT_INVALID_PATH);
1609
2982
  }
1610
2983
  if (!stat3.isDirectory()) {
1611
- process.stderr.write(chalk6.red(`${label} must be a directory: ${dir}
2984
+ process.stderr.write(chalk10.red(`${label} must be a directory: ${dir}
1612
2985
  `));
1613
2986
  process.exit(EXIT_INVALID_PATH);
1614
2987
  }
1615
2988
  }
1616
2989
  const format = opts.format.toLowerCase();
1617
2990
  if (!VALID_FORMATS.has(format)) {
1618
- process.stderr.write(chalk6.red(`Invalid format: "${opts.format}". Allowed: ndjson, json, patch
2991
+ process.stderr.write(chalk10.red(`Invalid format: "${opts.format}". Allowed: ndjson, json, patch
1619
2992
  `));
1620
2993
  process.exit(EXIT_USAGE_ERROR2);
1621
2994
  }
1622
2995
  const contextLines = parseInt(opts.context, 10);
1623
2996
  if (isNaN(contextLines) || contextLines < 0) {
1624
- process.stderr.write(chalk6.red("--context must be >= 0\n"));
2997
+ process.stderr.write(chalk10.red("--context must be >= 0\n"));
1625
2998
  process.exit(EXIT_USAGE_ERROR2);
1626
2999
  }
1627
3000
  const includeGlobs = opts.include ? opts.include.split(",").map((g) => g.trim()).filter(Boolean) : void 0;
@@ -1633,9 +3006,9 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1633
3006
  const origMap = new Map(origFiles.map((f) => [f.relPath, f]));
1634
3007
  const genMap = new Map(genFiles.map((f) => [f.relPath, f]));
1635
3008
  if (opts.verbose) {
1636
- process.stderr.write(chalk6.dim(`Original: ${origFiles.length} files
3009
+ process.stderr.write(chalk10.dim(`Original: ${origFiles.length} files
1637
3010
  `));
1638
- process.stderr.write(chalk6.dim(`Generated: ${genFiles.length} files
3011
+ process.stderr.write(chalk10.dim(`Generated: ${genFiles.length} files
1639
3012
  `));
1640
3013
  }
1641
3014
  const deltas = [];
@@ -1669,7 +3042,7 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1669
3042
  }
1670
3043
  deltas.push(delta);
1671
3044
  if (opts.verbose) {
1672
- process.stderr.write(chalk6.dim(` modified: ${relPath} (+${diffResult.insertions} -${diffResult.deletions})
3045
+ process.stderr.write(chalk10.dim(` modified: ${relPath} (+${diffResult.insertions} -${diffResult.deletions})
1673
3046
  `));
1674
3047
  }
1675
3048
  }
@@ -1700,7 +3073,7 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1700
3073
  const pairs = [];
1701
3074
  for (const [origPath, origContent] of origContents) {
1702
3075
  for (const [genPath, genContent] of genContents) {
1703
- if (path7.extname(origPath) !== path7.extname(genPath)) continue;
3076
+ if (path10.extname(origPath) !== path10.extname(genPath)) continue;
1704
3077
  const similarity = computeSimilarity(origContent, genContent);
1705
3078
  if (similarity >= RENAME_THRESHOLD) {
1706
3079
  pairs.push({ origPath, genPath, similarity });
@@ -1733,7 +3106,7 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1733
3106
  }
1734
3107
  deltas.push(delta);
1735
3108
  if (opts.verbose) {
1736
- process.stderr.write(chalk6.dim(
3109
+ process.stderr.write(chalk10.dim(
1737
3110
  ` renamed: ${pair.origPath} \u2192 ${pair.genPath} (${(pair.similarity * 100).toFixed(0)}% similar)
1738
3111
  `
1739
3112
  ));
@@ -1758,7 +3131,7 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1758
3131
  }
1759
3132
  deltas.push(delta);
1760
3133
  if (opts.verbose) {
1761
- process.stderr.write(chalk6.dim(` removed: ${file.relPath}
3134
+ process.stderr.write(chalk10.dim(` removed: ${file.relPath}
1762
3135
  `));
1763
3136
  }
1764
3137
  }
@@ -1780,7 +3153,7 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1780
3153
  }
1781
3154
  deltas.push(delta);
1782
3155
  if (opts.verbose) {
1783
- process.stderr.write(chalk6.dim(` added: ${file.relPath}
3156
+ process.stderr.write(chalk10.dim(` added: ${file.relPath}
1784
3157
  `));
1785
3158
  }
1786
3159
  }
@@ -1801,16 +3174,16 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1801
3174
  output = formatPatch(deltas, originalPath, generatedPath);
1802
3175
  break;
1803
3176
  }
1804
- const outPath = path7.resolve(opts.out);
3177
+ const outPath = path10.resolve(opts.out);
1805
3178
  await fs3.writeFile(outPath, output, "utf-8");
1806
3179
  const added = deltas.filter((d) => d.changeKind === "added").length;
1807
3180
  const removed = deltas.filter((d) => d.changeKind === "removed").length;
1808
3181
  const modified = deltas.filter((d) => d.changeKind === "modified").length;
1809
3182
  const renamed = deltas.filter((d) => d.changeKind === "renamed").length;
1810
3183
  process.stderr.write(
1811
- chalk6.green(`\u2714 `) + `${deltas.length} changes: ` + chalk6.green(`+${added} added`) + ", " + chalk6.red(`-${removed} removed`) + ", " + chalk6.yellow(`~${modified} modified`) + ", " + chalk6.blue(`\u2192${renamed} renamed`) + "\n"
3184
+ chalk10.green(`\u2714 `) + `${deltas.length} changes: ` + chalk10.green(`+${added} added`) + ", " + chalk10.red(`-${removed} removed`) + ", " + chalk10.yellow(`~${modified} modified`) + ", " + chalk10.blue(`\u2192${renamed} renamed`) + "\n"
1812
3185
  );
1813
- process.stderr.write(chalk6.dim(` Written to ${outPath}
3186
+ process.stderr.write(chalk10.dim(` Written to ${outPath}
1814
3187
  `));
1815
3188
  if (opts.verbose && opts.stats) {
1816
3189
  let totalIns = 0;
@@ -1821,193 +3194,193 @@ var diffCommand = new Command6("diff").description("Compare two directory trees
1821
3194
  totalDel += d.stats.deletions;
1822
3195
  }
1823
3196
  }
1824
- process.stderr.write(chalk6.dim(` Total: +${totalIns} insertions, -${totalDel} deletions
3197
+ process.stderr.write(chalk10.dim(` Total: +${totalIns} insertions, -${totalDel} deletions
1825
3198
  `));
1826
3199
  }
1827
3200
  });
1828
3201
 
1829
3202
  // src/commands/help.ts
1830
- import { Command as Command7 } from "commander";
1831
- import chalk7 from "chalk";
3203
+ import { Command as Command10 } from "commander";
3204
+ import chalk11 from "chalk";
1832
3205
  var HELP_URL = "https://vibgrate.com/help";
1833
3206
  function printFooter() {
1834
3207
  console.log("");
1835
- console.log(chalk7.dim(`See ${HELP_URL} for more guidance`));
3208
+ console.log(chalk11.dim(`See ${HELP_URL} for more guidance`));
1836
3209
  }
1837
3210
  var detailedHelp = {
1838
3211
  scan: () => {
1839
3212
  console.log("");
1840
- console.log(chalk7.bold.underline("vibgrate scan") + chalk7.dim(" \u2014 Scan a project for upgrade drift"));
3213
+ console.log(chalk11.bold.underline("vibgrate scan") + chalk11.dim(" \u2014 Scan a project for upgrade drift"));
1841
3214
  console.log("");
1842
- console.log(chalk7.bold("Usage:"));
3215
+ console.log(chalk11.bold("Usage:"));
1843
3216
  console.log(" vibgrate scan [path] [options]");
1844
3217
  console.log("");
1845
- console.log(chalk7.bold("Arguments:"));
1846
- console.log(` ${chalk7.cyan("[path]")} Path to scan (default: current directory)`);
3218
+ console.log(chalk11.bold("Arguments:"));
3219
+ console.log(` ${chalk11.cyan("[path]")} Path to scan (default: current directory)`);
1847
3220
  console.log("");
1848
- console.log(chalk7.bold("Output options:"));
1849
- console.log(` ${chalk7.cyan("--format <format>")} Output format: ${chalk7.white("text")} | json | sarif | md (default: text)`);
1850
- console.log(` ${chalk7.cyan("--out <file>")} Write output to a file instead of stdout`);
3221
+ console.log(chalk11.bold("Output options:"));
3222
+ console.log(` ${chalk11.cyan("--format <format>")} Output format: ${chalk11.white("text")} | json | sarif | md (default: text)`);
3223
+ console.log(` ${chalk11.cyan("--out <file>")} Write output to a file instead of stdout`);
1851
3224
  console.log("");
1852
- console.log(chalk7.bold("Baseline & gating:"));
1853
- console.log(` ${chalk7.cyan("--baseline <file>")} Compare results against a saved baseline`);
1854
- console.log(` ${chalk7.cyan("--drift-budget <score>")} Fail if drift score exceeds this value (0\u2013100)`);
1855
- console.log(` ${chalk7.cyan("--drift-worsening <percent>")} Fail if drift worsens by more than % since baseline`);
1856
- console.log(` ${chalk7.cyan("--fail-on <level>")} Fail exit code on warn or error findings`);
3225
+ console.log(chalk11.bold("Baseline & gating:"));
3226
+ console.log(` ${chalk11.cyan("--baseline <file>")} Compare results against a saved baseline`);
3227
+ console.log(` ${chalk11.cyan("--drift-budget <score>")} Fail if drift score exceeds this value (0\u2013100)`);
3228
+ console.log(` ${chalk11.cyan("--drift-worsening <percent>")} Fail if drift worsens by more than % since baseline`);
3229
+ console.log(` ${chalk11.cyan("--fail-on <level>")} Fail exit code on warn or error findings`);
1857
3230
  console.log("");
1858
- console.log(chalk7.bold("Performance:"));
1859
- console.log(` ${chalk7.cyan("--concurrency <n>")} Max concurrent registry calls (default: 8)`);
1860
- console.log(` ${chalk7.cyan("--changed-only")} Only scan files changed since last git commit`);
3231
+ console.log(chalk11.bold("Performance:"));
3232
+ console.log(` ${chalk11.cyan("--concurrency <n>")} Max concurrent registry calls (default: 8)`);
3233
+ console.log(` ${chalk11.cyan("--changed-only")} Only scan files changed since last git commit`);
1861
3234
  console.log("");
1862
- console.log(chalk7.bold("Privacy & offline:"));
1863
- console.log(` ${chalk7.cyan("--offline")} Run without any network calls; skip result upload`);
1864
- console.log(` ${chalk7.cyan("--package-manifest <file>")} Use a local package-version manifest (JSON or ZIP) for offline mode`);
1865
- console.log(` ${chalk7.cyan("--no-local-artifacts")} Do not write .vibgrate JSON artifacts to disk`);
1866
- console.log(` ${chalk7.cyan("--max-privacy")} Strongest privacy mode: minimal scanners + no local artifacts`);
3235
+ console.log(chalk11.bold("Privacy & offline:"));
3236
+ console.log(` ${chalk11.cyan("--offline")} Run without any network calls; skip result upload`);
3237
+ console.log(` ${chalk11.cyan("--package-manifest <file>")} Use a local package-version manifest (JSON or ZIP) for offline mode`);
3238
+ console.log(` ${chalk11.cyan("--no-local-artifacts")} Do not write .vibgrate JSON artifacts to disk`);
3239
+ console.log(` ${chalk11.cyan("--max-privacy")} Strongest privacy mode: minimal scanners + no local artifacts`);
1867
3240
  console.log("");
1868
- console.log(chalk7.bold("Tooling:"));
1869
- console.log(` ${chalk7.cyan("--install-tools")} Auto-install missing security scanners via Homebrew`);
1870
- console.log(` ${chalk7.cyan("--ui-purpose")} Enable UI purpose evidence extraction (slower)`);
3241
+ console.log(chalk11.bold("Tooling:"));
3242
+ console.log(` ${chalk11.cyan("--install-tools")} Auto-install missing security scanners via Homebrew`);
3243
+ console.log(` ${chalk11.cyan("--ui-purpose")} Enable UI purpose evidence extraction (slower)`);
1871
3244
  console.log("");
1872
- console.log(chalk7.bold("Uploading results:"));
1873
- console.log(` ${chalk7.cyan("--push")} Auto-push results to Vibgrate API after scan`);
1874
- console.log(` ${chalk7.cyan("--dsn <dsn>")} DSN token for push (or set VIBGRATE_DSN env var)`);
1875
- console.log(` ${chalk7.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
1876
- console.log(` ${chalk7.cyan("--strict")} Fail if the upload to Vibgrate API fails`);
3245
+ console.log(chalk11.bold("Uploading results:"));
3246
+ console.log(` ${chalk11.cyan("--push")} Auto-push results to Vibgrate API after scan`);
3247
+ console.log(` ${chalk11.cyan("--dsn <dsn>")} DSN token for push (or set VIBGRATE_DSN env var)`);
3248
+ console.log(` ${chalk11.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
3249
+ console.log(` ${chalk11.cyan("--strict")} Fail if the upload to Vibgrate API fails`);
1877
3250
  console.log("");
1878
- console.log(chalk7.bold("Examples:"));
1879
- console.log(` ${chalk7.dim("# Scan the current directory and display a text report")}`);
3251
+ console.log(chalk11.bold("Examples:"));
3252
+ console.log(` ${chalk11.dim("# Scan the current directory and display a text report")}`);
1880
3253
  console.log(" vibgrate scan .");
1881
3254
  console.log("");
1882
- console.log(` ${chalk7.dim("# Scan, fail if drift score > 40, and write SARIF for GitHub Actions")}`);
3255
+ console.log(` ${chalk11.dim("# Scan, fail if drift score > 40, and write SARIF for GitHub Actions")}`);
1883
3256
  console.log(" vibgrate scan . --drift-budget 40 --format sarif --out drift.sarif");
1884
3257
  console.log("");
1885
- console.log(` ${chalk7.dim("# Scan and automatically upload results via a DSN")}`);
3258
+ console.log(` ${chalk11.dim("# Scan and automatically upload results via a DSN")}`);
1886
3259
  console.log(" vibgrate scan . --push --dsn $VIBGRATE_DSN");
1887
3260
  console.log("");
1888
- console.log(` ${chalk7.dim("# Offline scan using a pre-downloaded package manifest")}`);
3261
+ console.log(` ${chalk11.dim("# Offline scan using a pre-downloaded package manifest")}`);
1889
3262
  console.log(" vibgrate scan . --offline --package-manifest ./manifest.zip");
1890
3263
  },
1891
3264
  init: () => {
1892
3265
  console.log("");
1893
- console.log(chalk7.bold.underline("vibgrate init") + chalk7.dim(" \u2014 Initialise vibgrate in a project directory"));
3266
+ console.log(chalk11.bold.underline("vibgrate init") + chalk11.dim(" \u2014 Initialise vibgrate in a project directory"));
1894
3267
  console.log("");
1895
- console.log(chalk7.bold("Usage:"));
3268
+ console.log(chalk11.bold("Usage:"));
1896
3269
  console.log(" vibgrate init [path] [options]");
1897
3270
  console.log("");
1898
- console.log(chalk7.bold("Arguments:"));
1899
- console.log(` ${chalk7.cyan("[path]")} Directory to initialise (default: current directory)`);
3271
+ console.log(chalk11.bold("Arguments:"));
3272
+ console.log(` ${chalk11.cyan("[path]")} Directory to initialise (default: current directory)`);
1900
3273
  console.log("");
1901
- console.log(chalk7.bold("Options:"));
1902
- console.log(` ${chalk7.cyan("--baseline")} Create an initial drift baseline after init`);
1903
- console.log(` ${chalk7.cyan("--yes")} Skip all confirmation prompts`);
3274
+ console.log(chalk11.bold("Options:"));
3275
+ console.log(` ${chalk11.cyan("--baseline")} Create an initial drift baseline after init`);
3276
+ console.log(` ${chalk11.cyan("--yes")} Skip all confirmation prompts`);
1904
3277
  console.log("");
1905
- console.log(chalk7.bold("What it does:"));
3278
+ console.log(chalk11.bold("What it does:"));
1906
3279
  console.log(" \u2022 Creates a .vibgrate/ directory");
1907
3280
  console.log(" \u2022 Writes a vibgrate.config.ts starter config");
1908
3281
  console.log(" \u2022 Optionally runs an initial baseline scan (--baseline)");
1909
3282
  console.log("");
1910
- console.log(chalk7.bold("Examples:"));
3283
+ console.log(chalk11.bold("Examples:"));
1911
3284
  console.log(" vibgrate init");
1912
3285
  console.log(" vibgrate init ./my-project --baseline");
1913
3286
  },
1914
3287
  baseline: () => {
1915
3288
  console.log("");
1916
- console.log(chalk7.bold.underline("vibgrate baseline") + chalk7.dim(" \u2014 Save a drift baseline snapshot"));
3289
+ console.log(chalk11.bold.underline("vibgrate baseline") + chalk11.dim(" \u2014 Save a drift baseline snapshot"));
1917
3290
  console.log("");
1918
- console.log(chalk7.bold("Usage:"));
3291
+ console.log(chalk11.bold("Usage:"));
1919
3292
  console.log(" vibgrate baseline [path]");
1920
3293
  console.log("");
1921
- console.log(chalk7.bold("Arguments:"));
1922
- console.log(` ${chalk7.cyan("[path]")} Path to baseline (default: current directory)`);
3294
+ console.log(chalk11.bold("Arguments:"));
3295
+ console.log(` ${chalk11.cyan("[path]")} Path to baseline (default: current directory)`);
1923
3296
  console.log("");
1924
- console.log(chalk7.bold("What it does:"));
3297
+ console.log(chalk11.bold("What it does:"));
1925
3298
  console.log(" Runs a full scan and saves the result as .vibgrate/baseline.json.");
1926
3299
  console.log(" Future scans can compare against this file using --baseline.");
1927
3300
  console.log("");
1928
- console.log(chalk7.bold("Examples:"));
3301
+ console.log(chalk11.bold("Examples:"));
1929
3302
  console.log(" vibgrate baseline .");
1930
3303
  console.log(" vibgrate scan . --baseline .vibgrate/baseline.json --drift-worsening 10");
1931
3304
  },
1932
3305
  report: () => {
1933
3306
  console.log("");
1934
- console.log(chalk7.bold.underline("vibgrate report") + chalk7.dim(" \u2014 Generate a report from a saved scan artifact"));
3307
+ console.log(chalk11.bold.underline("vibgrate report") + chalk11.dim(" \u2014 Generate a report from a saved scan artifact"));
1935
3308
  console.log("");
1936
- console.log(chalk7.bold("Usage:"));
3309
+ console.log(chalk11.bold("Usage:"));
1937
3310
  console.log(" vibgrate report [options]");
1938
3311
  console.log("");
1939
- console.log(chalk7.bold("Options:"));
1940
- console.log(` ${chalk7.cyan("--in <file>")} Input artifact file (default: .vibgrate/scan_result.json)`);
1941
- console.log(` ${chalk7.cyan("--format <format>")} Output format: ${chalk7.white("text")} | md | json (default: text)`);
3312
+ console.log(chalk11.bold("Options:"));
3313
+ console.log(` ${chalk11.cyan("--in <file>")} Input artifact file (default: .vibgrate/scan_result.json)`);
3314
+ console.log(` ${chalk11.cyan("--format <format>")} Output format: ${chalk11.white("text")} | md | json (default: text)`);
1942
3315
  console.log("");
1943
- console.log(chalk7.bold("Examples:"));
3316
+ console.log(chalk11.bold("Examples:"));
1944
3317
  console.log(" vibgrate report");
1945
3318
  console.log(" vibgrate report --format md > DRIFT-REPORT.md");
1946
3319
  console.log(" vibgrate report --in ./ci/scan_result.json --format json");
1947
3320
  },
1948
3321
  sbom: () => {
1949
3322
  console.log("");
1950
- console.log(chalk7.bold.underline("vibgrate sbom") + chalk7.dim(" \u2014 Export a Software Bill of Materials from a scan artifact"));
3323
+ console.log(chalk11.bold.underline("vibgrate sbom") + chalk11.dim(" \u2014 Export a Software Bill of Materials from a scan artifact"));
1951
3324
  console.log("");
1952
- console.log(chalk7.bold("Usage:"));
3325
+ console.log(chalk11.bold("Usage:"));
1953
3326
  console.log(" vibgrate sbom [options]");
1954
3327
  console.log("");
1955
- console.log(chalk7.bold("Options:"));
1956
- console.log(` ${chalk7.cyan("--in <file>")} Input artifact (default: .vibgrate/scan_result.json)`);
1957
- console.log(` ${chalk7.cyan("--format <format>")} SBOM format: ${chalk7.white("cyclonedx")} | spdx (default: cyclonedx)`);
1958
- console.log(` ${chalk7.cyan("--out <file>")} Write SBOM to file instead of stdout`);
3328
+ console.log(chalk11.bold("Options:"));
3329
+ console.log(` ${chalk11.cyan("--in <file>")} Input artifact (default: .vibgrate/scan_result.json)`);
3330
+ console.log(` ${chalk11.cyan("--format <format>")} SBOM format: ${chalk11.white("cyclonedx")} | spdx (default: cyclonedx)`);
3331
+ console.log(` ${chalk11.cyan("--out <file>")} Write SBOM to file instead of stdout`);
1959
3332
  console.log("");
1960
- console.log(chalk7.bold("Examples:"));
3333
+ console.log(chalk11.bold("Examples:"));
1961
3334
  console.log(" vibgrate sbom --format cyclonedx --out sbom.json");
1962
3335
  console.log(" vibgrate sbom --format spdx --out sbom.spdx.json");
1963
3336
  },
1964
3337
  push: () => {
1965
3338
  console.log("");
1966
- console.log(chalk7.bold.underline("vibgrate push") + chalk7.dim(" \u2014 Upload a scan artifact to the Vibgrate API"));
3339
+ console.log(chalk11.bold.underline("vibgrate push") + chalk11.dim(" \u2014 Upload a scan artifact to the Vibgrate API"));
1967
3340
  console.log("");
1968
- console.log(chalk7.bold("Usage:"));
3341
+ console.log(chalk11.bold("Usage:"));
1969
3342
  console.log(" vibgrate push [options]");
1970
3343
  console.log("");
1971
- console.log(chalk7.bold("Options:"));
1972
- console.log(` ${chalk7.cyan("--dsn <dsn>")} DSN token (or set VIBGRATE_DSN env var)`);
1973
- console.log(` ${chalk7.cyan("--file <file>")} Artifact to upload (default: .vibgrate/scan_result.json)`);
1974
- console.log(` ${chalk7.cyan("--region <region>")} Override data residency region: us | eu`);
1975
- console.log(` ${chalk7.cyan("--strict")} Fail with non-zero exit code on upload error`);
3344
+ console.log(chalk11.bold("Options:"));
3345
+ console.log(` ${chalk11.cyan("--dsn <dsn>")} DSN token (or set VIBGRATE_DSN env var)`);
3346
+ console.log(` ${chalk11.cyan("--file <file>")} Artifact to upload (default: .vibgrate/scan_result.json)`);
3347
+ console.log(` ${chalk11.cyan("--region <region>")} Override data residency region: us | eu`);
3348
+ console.log(` ${chalk11.cyan("--strict")} Fail with non-zero exit code on upload error`);
1976
3349
  console.log("");
1977
- console.log(chalk7.bold("Examples:"));
3350
+ console.log(chalk11.bold("Examples:"));
1978
3351
  console.log(" vibgrate push --dsn $VIBGRATE_DSN");
1979
3352
  console.log(" vibgrate push --file ./ci/scan_result.json --strict");
1980
3353
  },
1981
3354
  dsn: () => {
1982
3355
  console.log("");
1983
- console.log(chalk7.bold.underline("vibgrate dsn") + chalk7.dim(" \u2014 Manage DSN tokens for API authentication"));
3356
+ console.log(chalk11.bold.underline("vibgrate dsn") + chalk11.dim(" \u2014 Manage DSN tokens for API authentication"));
1984
3357
  console.log("");
1985
- console.log(chalk7.bold("Subcommands:"));
1986
- console.log(` ${chalk7.cyan("vibgrate dsn create")} Generate a new DSN token`);
3358
+ console.log(chalk11.bold("Subcommands:"));
3359
+ console.log(` ${chalk11.cyan("vibgrate dsn create")} Generate a new DSN token`);
1987
3360
  console.log("");
1988
- console.log(chalk7.bold("dsn create options:"));
1989
- console.log(` ${chalk7.cyan("--workspace <id>")} Workspace ID or "new" to auto-generate ${chalk7.red("(required)")}`);
1990
- console.log(` ${chalk7.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
1991
- console.log(` ${chalk7.cyan("--ingest <url>")} Override ingest API URL`);
1992
- console.log(` ${chalk7.cyan("--write <path>")} Write the DSN to a file (add to .gitignore!)`);
3361
+ console.log(chalk11.bold("dsn create options:"));
3362
+ console.log(` ${chalk11.cyan("--workspace <id>")} Workspace ID or "new" to auto-generate ${chalk11.red("(required)")}`);
3363
+ console.log(` ${chalk11.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
3364
+ console.log(` ${chalk11.cyan("--ingest <url>")} Override ingest API URL`);
3365
+ console.log(` ${chalk11.cyan("--write <path>")} Write the DSN to a file (add to .gitignore!)`);
1993
3366
  console.log("");
1994
- console.log(chalk7.bold("Examples:"));
3367
+ console.log(chalk11.bold("Examples:"));
1995
3368
  console.log(" vibgrate dsn create --workspace new");
1996
3369
  console.log(" vibgrate dsn create --workspace abc123");
1997
3370
  console.log(" vibgrate dsn create --workspace new --region eu --write .vibgrate/.dsn");
1998
3371
  },
1999
3372
  update: () => {
2000
3373
  console.log("");
2001
- console.log(chalk7.bold.underline("vibgrate update") + chalk7.dim(" \u2014 Update the vibgrate CLI to the latest version"));
3374
+ console.log(chalk11.bold.underline("vibgrate update") + chalk11.dim(" \u2014 Update the vibgrate CLI to the latest version"));
2002
3375
  console.log("");
2003
- console.log(chalk7.bold("Usage:"));
3376
+ console.log(chalk11.bold("Usage:"));
2004
3377
  console.log(" vibgrate update [options]");
2005
3378
  console.log("");
2006
- console.log(chalk7.bold("Options:"));
2007
- console.log(` ${chalk7.cyan("--check")} Check for a newer version without installing`);
2008
- console.log(` ${chalk7.cyan("--pm <manager>")} Force a package manager: npm | pnpm | yarn | bun`);
3379
+ console.log(chalk11.bold("Options:"));
3380
+ console.log(` ${chalk11.cyan("--check")} Check for a newer version without installing`);
3381
+ console.log(` ${chalk11.cyan("--pm <manager>")} Force a package manager: npm | pnpm | yarn | bun`);
2009
3382
  console.log("");
2010
- console.log(chalk7.bold("Examples:"));
3383
+ console.log(chalk11.bold("Examples:"));
2011
3384
  console.log(" vibgrate update");
2012
3385
  console.log(" vibgrate update --check");
2013
3386
  console.log(" vibgrate update --pm pnpm");
@@ -2015,40 +3388,40 @@ var detailedHelp = {
2015
3388
  };
2016
3389
  function printSummaryHelp() {
2017
3390
  console.log("");
2018
- console.log(chalk7.bold("vibgrate") + chalk7.dim(" \u2014 Continuous Drift Intelligence"));
3391
+ console.log(chalk11.bold("vibgrate") + chalk11.dim(" \u2014 Continuous Drift Intelligence"));
2019
3392
  console.log("");
2020
- console.log(chalk7.bold("Usage:"));
3393
+ console.log(chalk11.bold("Usage:"));
2021
3394
  console.log(" vibgrate <command> [options]");
2022
3395
  console.log(" vibgrate help [command] Show detailed help for a command");
2023
3396
  console.log("");
2024
- console.log(chalk7.bold("Getting started:"));
2025
- console.log(` ${chalk7.cyan("init")} Initialise vibgrate in a project (creates config & .vibgrate/ dir)`);
3397
+ console.log(chalk11.bold("Getting started:"));
3398
+ console.log(` ${chalk11.cyan("init")} Initialise vibgrate in a project (creates config & .vibgrate/ dir)`);
2026
3399
  console.log("");
2027
- console.log(chalk7.bold("Core scanning:"));
2028
- console.log(` ${chalk7.cyan("scan")} Scan a project for upgrade drift and generate a report`);
2029
- console.log(` ${chalk7.cyan("baseline")} Save a baseline snapshot to compare future scans against`);
3400
+ console.log(chalk11.bold("Core scanning:"));
3401
+ console.log(` ${chalk11.cyan("scan")} Scan a project for upgrade drift and generate a report`);
3402
+ console.log(` ${chalk11.cyan("baseline")} Save a baseline snapshot to compare future scans against`);
2030
3403
  console.log("");
2031
- console.log(chalk7.bold("Reporting & export:"));
2032
- console.log(` ${chalk7.cyan("report")} Re-generate a report from a previously saved scan artifact`);
2033
- console.log(` ${chalk7.cyan("sbom")} Export a Software Bill of Materials (CycloneDX or SPDX)`);
3404
+ console.log(chalk11.bold("Reporting & export:"));
3405
+ console.log(` ${chalk11.cyan("report")} Re-generate a report from a previously saved scan artifact`);
3406
+ console.log(` ${chalk11.cyan("sbom")} Export a Software Bill of Materials (CycloneDX or SPDX)`);
2034
3407
  console.log("");
2035
- console.log(chalk7.bold("CI/CD integration:"));
2036
- console.log(` ${chalk7.cyan("push")} Upload a scan artifact to the Vibgrate API`);
2037
- console.log(` ${chalk7.cyan("dsn")} Create and manage DSN tokens for API authentication`);
3408
+ console.log(chalk11.bold("CI/CD integration:"));
3409
+ console.log(` ${chalk11.cyan("push")} Upload a scan artifact to the Vibgrate API`);
3410
+ console.log(` ${chalk11.cyan("dsn")} Create and manage DSN tokens for API authentication`);
2038
3411
  console.log("");
2039
- console.log(chalk7.bold("Maintenance:"));
2040
- console.log(` ${chalk7.cyan("update")} Update the vibgrate CLI to the latest version`);
3412
+ console.log(chalk11.bold("Maintenance:"));
3413
+ console.log(` ${chalk11.cyan("update")} Update the vibgrate CLI to the latest version`);
2041
3414
  console.log("");
2042
- console.log(chalk7.dim("Run") + ` ${chalk7.cyan("vibgrate help <command>")} ` + chalk7.dim("for detailed options, e.g.") + ` ${chalk7.cyan("vibgrate help scan")}`);
3415
+ console.log(chalk11.dim("Run") + ` ${chalk11.cyan("vibgrate help <command>")} ` + chalk11.dim("for detailed options, e.g.") + ` ${chalk11.cyan("vibgrate help scan")}`);
2043
3416
  }
2044
- var helpCommand = new Command7("help").description("Show help for vibgrate commands").argument("[command]", "Command to show detailed help for").helpOption(false).action((cmd) => {
3417
+ var helpCommand = new Command10("help").description("Show help for vibgrate commands").argument("[command]", "Command to show detailed help for").helpOption(false).action((cmd) => {
2045
3418
  const name = cmd?.toLowerCase().trim();
2046
3419
  if (name && detailedHelp[name]) {
2047
3420
  detailedHelp[name]();
2048
3421
  } else if (name) {
2049
3422
  console.log("");
2050
- console.log(chalk7.red(`Unknown command: ${name}`));
2051
- console.log(chalk7.dim(`Available commands: ${Object.keys(detailedHelp).join(", ")}`));
3423
+ console.log(chalk11.red(`Unknown command: ${name}`));
3424
+ console.log(chalk11.dim(`Available commands: ${Object.keys(detailedHelp).join(", ")}`));
2052
3425
  printSummaryHelp();
2053
3426
  } else {
2054
3427
  printSummaryHelp();
@@ -2057,7 +3430,7 @@ var helpCommand = new Command7("help").description("Show help for vibgrate comma
2057
3430
  });
2058
3431
 
2059
3432
  // src/cli.ts
2060
- var program = new Command8();
3433
+ var program = new Command11();
2061
3434
  program.name("vibgrate").description("Continuous Drift Intelligence").version(VERSION).addHelpText("after", "\nSee https://vibgrate.com/help for more guidance");
2062
3435
  program.addCommand(helpCommand);
2063
3436
  program.addCommand(initCommand);
@@ -2074,8 +3447,8 @@ function notifyIfUpdateAvailable() {
2074
3447
  void checkForUpdate().then((update) => {
2075
3448
  if (!update?.updateAvailable) return;
2076
3449
  console.error("");
2077
- console.error(chalk8.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
2078
- console.error(chalk8.dim(' Run "vibgrate update" to install the latest version.'));
3450
+ console.error(chalk12.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
3451
+ console.error(chalk12.dim(' Run "vibgrate update" to install the latest version.'));
2079
3452
  console.error("");
2080
3453
  }).catch(() => {
2081
3454
  });