@vibgrate/cli 2026.617.1 → 2026.618.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
pathExists,
|
|
7
7
|
readJsonFile,
|
|
8
8
|
writeTextFile
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-34KD4UJ3.js";
|
|
10
10
|
import {
|
|
11
11
|
computeRepoFingerprint,
|
|
12
12
|
detectVcs,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolveRepositoryName,
|
|
17
17
|
runScan,
|
|
18
18
|
writeDefaultConfig
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-XKZBEQYY.js";
|
|
20
20
|
import {
|
|
21
21
|
require_semver
|
|
22
22
|
} from "./chunk-5IXVOEZN.js";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
// src/cli.ts
|
|
32
32
|
import { Command as Command13 } from "commander";
|
|
33
|
-
import
|
|
33
|
+
import chalk15 from "chalk";
|
|
34
34
|
|
|
35
35
|
// src/commands/init.ts
|
|
36
36
|
import * as path from "path";
|
|
@@ -49,7 +49,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
49
49
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
50
50
|
}
|
|
51
51
|
if (opts.baseline) {
|
|
52
|
-
const { runBaseline } = await import("./baseline-
|
|
52
|
+
const { runBaseline } = await import("./baseline-6CJI5Z45.js");
|
|
53
53
|
await runBaseline(rootDir);
|
|
54
54
|
}
|
|
55
55
|
console.log("");
|
|
@@ -62,7 +62,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
62
62
|
// src/commands/scan.ts
|
|
63
63
|
import * as path4 from "path";
|
|
64
64
|
import { Command as Command3 } from "commander";
|
|
65
|
-
import
|
|
65
|
+
import chalk4 from "chalk";
|
|
66
66
|
|
|
67
67
|
// src/version.ts
|
|
68
68
|
import { createRequire } from "module";
|
|
@@ -129,6 +129,9 @@ function dashHostForIngestHost(ingestHost) {
|
|
|
129
129
|
const match = REGIONS.find((r) => r.ingestHost === ingestHost);
|
|
130
130
|
return match?.dashHost ?? "dash.vibgrate.com";
|
|
131
131
|
}
|
|
132
|
+
function ingestHostForRegionId(region) {
|
|
133
|
+
return findRegion(region.toLowerCase())?.ingestHost;
|
|
134
|
+
}
|
|
132
135
|
|
|
133
136
|
// src/commands/dsn.ts
|
|
134
137
|
async function provisionDsn(keyId, secret, workspaceId, ingestHost, region) {
|
|
@@ -261,18 +264,62 @@ function emitIngestIdLine(ingestId, options) {
|
|
|
261
264
|
}
|
|
262
265
|
}
|
|
263
266
|
|
|
267
|
+
// src/utils/upload.ts
|
|
268
|
+
import chalk3 from "chalk";
|
|
269
|
+
function postOnce(input, host) {
|
|
270
|
+
const url = `${input.scheme}://${host}/v1/ingest/scan`;
|
|
271
|
+
return fetch(url, {
|
|
272
|
+
method: "POST",
|
|
273
|
+
headers: {
|
|
274
|
+
"Content-Type": "application/json",
|
|
275
|
+
"Content-Encoding": input.contentEncoding,
|
|
276
|
+
"X-Vibgrate-Timestamp": input.timestamp,
|
|
277
|
+
"Authorization": `VibgrateDSN ${input.keyId}:${input.secret}`,
|
|
278
|
+
"Connection": "close"
|
|
279
|
+
// Prevent keep-alive delays on exit
|
|
280
|
+
},
|
|
281
|
+
body: input.body
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function regionRedirectHost(response) {
|
|
285
|
+
try {
|
|
286
|
+
const payload = await response.clone().json();
|
|
287
|
+
if (payload?.code !== "REGION_MISMATCH" || !payload.region) return void 0;
|
|
288
|
+
return ingestHostForRegionId(payload.region);
|
|
289
|
+
} catch {
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function uploadScanArtifact(input) {
|
|
294
|
+
let host = input.host;
|
|
295
|
+
let response = await postOnce(input, host);
|
|
296
|
+
if (response.status === 409) {
|
|
297
|
+
const target = await regionRedirectHost(response);
|
|
298
|
+
if (target && target !== host) {
|
|
299
|
+
console.log(
|
|
300
|
+
chalk3.yellow(
|
|
301
|
+
`\u21BB Workspace is pinned to a different region \u2014 retrying upload to ${target}...`
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
host = target;
|
|
305
|
+
response = await postOnce(input, host);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return { response, host };
|
|
309
|
+
}
|
|
310
|
+
|
|
264
311
|
// src/commands/scan.ts
|
|
265
312
|
async function autoPush(artifact, rootDir, opts) {
|
|
266
313
|
const dsn = resolveDsn(opts.dsn);
|
|
267
314
|
if (!dsn) {
|
|
268
|
-
console.error(
|
|
269
|
-
console.error(
|
|
315
|
+
console.error(chalk4.red("No DSN provided for push."));
|
|
316
|
+
console.error(chalk4.dim('Run "vibgrate login", set VIBGRATE_DSN, or use the --dsn flag.'));
|
|
270
317
|
if (opts.strict) process.exit(1);
|
|
271
318
|
return;
|
|
272
319
|
}
|
|
273
320
|
const parsed = parseDsn(dsn);
|
|
274
321
|
if (!parsed) {
|
|
275
|
-
console.error(
|
|
322
|
+
console.error(chalk4.red("Invalid DSN format."));
|
|
276
323
|
if (opts.strict) process.exit(1);
|
|
277
324
|
return;
|
|
278
325
|
}
|
|
@@ -283,29 +330,26 @@ async function autoPush(artifact, rootDir, opts) {
|
|
|
283
330
|
try {
|
|
284
331
|
host = resolveIngestHost(opts.region);
|
|
285
332
|
} catch (e) {
|
|
286
|
-
console.error(
|
|
333
|
+
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
287
334
|
if (opts.strict) process.exit(1);
|
|
288
335
|
return;
|
|
289
336
|
}
|
|
290
337
|
}
|
|
291
|
-
const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
|
|
292
338
|
const originalSize = JSON.stringify(artifact).length;
|
|
293
339
|
const compressedSize = body.length;
|
|
294
340
|
const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
|
|
295
|
-
console.log(
|
|
341
|
+
console.log(chalk4.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
|
|
296
342
|
try {
|
|
297
|
-
const response = await
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// Prevent keep-alive delays on exit
|
|
306
|
-
},
|
|
307
|
-
body
|
|
343
|
+
const { response, host: uploadedHost } = await uploadScanArtifact({
|
|
344
|
+
scheme: parsed.scheme,
|
|
345
|
+
host,
|
|
346
|
+
keyId: parsed.keyId,
|
|
347
|
+
secret: parsed.secret,
|
|
348
|
+
body,
|
|
349
|
+
contentEncoding,
|
|
350
|
+
timestamp
|
|
308
351
|
});
|
|
352
|
+
host = uploadedHost;
|
|
309
353
|
if (!response.ok) {
|
|
310
354
|
const text = await response.text();
|
|
311
355
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
@@ -313,36 +357,36 @@ async function autoPush(artifact, rootDir, opts) {
|
|
|
313
357
|
const result = await response.json();
|
|
314
358
|
if (result.unchanged) {
|
|
315
359
|
console.log(
|
|
316
|
-
|
|
360
|
+
chalk4.green("\u2714") + ` Repository unchanged since ${result.lastScannedAt ?? "last scan"} \u2014 skipped upload (no credit used).`
|
|
317
361
|
);
|
|
318
362
|
if (result.previousIngestId) {
|
|
319
363
|
emitIngestIdLine(result.previousIngestId, { unchanged: true });
|
|
320
|
-
const dashUrl = `https
|
|
321
|
-
console.log(
|
|
364
|
+
const dashUrl = `https://${dashHostForIngestHost(host)}/${parsed.workspaceId}/scan/${result.previousIngestId}`;
|
|
365
|
+
console.log(chalk4.dim(` Previous report: ${dashUrl}`));
|
|
322
366
|
} else if (opts.strict) {
|
|
323
|
-
console.error(
|
|
367
|
+
console.error(chalk4.red("Repository unchanged but no previous ingest id returned."));
|
|
324
368
|
process.exit(1);
|
|
325
369
|
}
|
|
326
370
|
return;
|
|
327
371
|
}
|
|
328
|
-
console.log(
|
|
372
|
+
console.log(chalk4.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
|
|
329
373
|
if (result.ingestId) {
|
|
330
374
|
emitIngestIdLine(result.ingestId);
|
|
331
|
-
const dashUrl = `https
|
|
375
|
+
const dashUrl = `https://${dashHostForIngestHost(host)}/${parsed.workspaceId}/scan/${result.ingestId}`;
|
|
332
376
|
const CLEAR_LINE = process.platform === "win32" ? "\x1B[0G\x1B[2K" : "";
|
|
333
377
|
console.log("");
|
|
334
|
-
console.log(CLEAR_LINE +
|
|
378
|
+
console.log(CLEAR_LINE + chalk4.dim(" Processing continues in the background. Results available shortly."));
|
|
335
379
|
console.log("");
|
|
336
|
-
console.log(CLEAR_LINE +
|
|
337
|
-
console.log(CLEAR_LINE +
|
|
338
|
-
console.log(CLEAR_LINE + " " +
|
|
339
|
-
console.log(CLEAR_LINE +
|
|
380
|
+
console.log(CLEAR_LINE + chalk4.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"));
|
|
381
|
+
console.log(CLEAR_LINE + chalk4.bold(" \u{1F4CA} View Scan Report"));
|
|
382
|
+
console.log(CLEAR_LINE + " " + chalk4.underline.cyan(dashUrl));
|
|
383
|
+
console.log(CLEAR_LINE + chalk4.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"));
|
|
340
384
|
console.log("");
|
|
341
385
|
console.log("");
|
|
342
386
|
}
|
|
343
387
|
} catch (e) {
|
|
344
388
|
const msg = e instanceof Error ? e.message : String(e);
|
|
345
|
-
console.error(
|
|
389
|
+
console.error(chalk4.red(`Upload failed: ${msg}`));
|
|
346
390
|
if (opts.strict) process.exit(1);
|
|
347
391
|
}
|
|
348
392
|
}
|
|
@@ -362,14 +406,15 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
362
406
|
'Exclude paths matching a glob pattern. Repeatable, and a single value may list several patterns separated by commas or semicolons (e.g. --exclude "legacy/**,vendor/**"). Merged with excludes from the config file.',
|
|
363
407
|
collectExcludes,
|
|
364
408
|
[]
|
|
365
|
-
).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) => {
|
|
409
|
+
).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").option("--repository-name <name>", "Override the repository name recorded for this scan (defaults to the directory or package.json name)").action(async (targetPath, opts) => {
|
|
366
410
|
const rootDir = path4.resolve(targetPath);
|
|
367
411
|
if (!await pathExists2(rootDir)) {
|
|
368
|
-
console.error(
|
|
412
|
+
console.error(chalk4.red(`Path does not exist: ${rootDir}`));
|
|
369
413
|
process.exit(1);
|
|
370
414
|
}
|
|
371
415
|
const hasDsn = !!resolveDsn(opts.dsn);
|
|
372
416
|
const willPush = !opts.offline && (opts.push || hasDsn);
|
|
417
|
+
let pinnedRegion;
|
|
373
418
|
if (willPush && hasDsn) {
|
|
374
419
|
const dsn = resolveDsn(opts.dsn);
|
|
375
420
|
const parsed = parseDsn(dsn);
|
|
@@ -377,25 +422,26 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
377
422
|
const ingestHost = opts.region ? resolveIngestHost(opts.region) : parsed.host;
|
|
378
423
|
const vcs = await detectVcs(rootDir);
|
|
379
424
|
const fingerprint = await computeRepoFingerprint(rootDir, vcs);
|
|
380
|
-
const repositoryName = await resolveRepositoryName(rootDir);
|
|
425
|
+
const repositoryName = opts.repositoryName?.trim() || await resolveRepositoryName(rootDir);
|
|
381
426
|
try {
|
|
382
427
|
const preflight = await fetchScanPreflight(parsed, ingestHost, {
|
|
383
428
|
repositoryName,
|
|
384
429
|
vcsSha: fingerprint.vcsSha
|
|
385
430
|
});
|
|
431
|
+
pinnedRegion = preflight.region;
|
|
386
432
|
if (preflight.vm && !preflight.vm.allowed) {
|
|
387
|
-
console.error(
|
|
433
|
+
console.error(chalk4.red(preflight.error ?? "VM meter usage exhausted"));
|
|
388
434
|
console.error(
|
|
389
|
-
|
|
435
|
+
chalk4.dim(
|
|
390
436
|
`VM minutes: ${preflight.vm.used}/${preflight.vm.limit} (${preflight.plan.label} plan) \u2014 enable overages or upgrade your plan.`
|
|
391
437
|
)
|
|
392
438
|
);
|
|
393
439
|
process.exit(1);
|
|
394
440
|
}
|
|
395
441
|
if (preflight.status === "error" || !preflight.scans.allowed) {
|
|
396
|
-
console.error(
|
|
442
|
+
console.error(chalk4.red(preflight.error ?? "Scan ingestion not allowed for this workspace."));
|
|
397
443
|
console.error(
|
|
398
|
-
|
|
444
|
+
chalk4.dim(
|
|
399
445
|
`Credits: ${preflight.scans.used}/${preflight.scans.limit} (${preflight.plan.label} plan)`
|
|
400
446
|
)
|
|
401
447
|
);
|
|
@@ -403,27 +449,27 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
403
449
|
process.exit(1);
|
|
404
450
|
}
|
|
405
451
|
console.log(
|
|
406
|
-
|
|
452
|
+
chalk4.dim(
|
|
407
453
|
`Plan: ${preflight.plan.label} \u2014 scan credits ${preflight.scans.used}/${preflight.scans.limit} this month`
|
|
408
454
|
)
|
|
409
455
|
);
|
|
410
456
|
if (preflight.repository?.unchanged) {
|
|
411
457
|
console.log(
|
|
412
|
-
|
|
458
|
+
chalk4.green("\u2714") + ` Repository unchanged at ${preflight.repository.lastVcsSha?.slice(0, 7) ?? "same revision"} \u2014 skipping scan.`
|
|
413
459
|
);
|
|
414
460
|
if (preflight.repository.lastIngestId) {
|
|
415
461
|
emitIngestIdLine(preflight.repository.lastIngestId, { unchanged: true });
|
|
416
|
-
const dashUrl = `https
|
|
417
|
-
console.log(
|
|
462
|
+
const dashUrl = `https://${dashHostForIngestHost(ingestHost)}/${parsed.workspaceId}/scan/${preflight.repository.lastIngestId}`;
|
|
463
|
+
console.log(chalk4.dim(` Latest report: ${dashUrl}`));
|
|
418
464
|
} else if (opts.strict) {
|
|
419
|
-
console.error(
|
|
465
|
+
console.error(chalk4.red("Repository unchanged but no previous ingest id available."));
|
|
420
466
|
process.exit(1);
|
|
421
467
|
}
|
|
422
468
|
return;
|
|
423
469
|
}
|
|
424
470
|
} catch (e) {
|
|
425
471
|
const msg = e instanceof Error ? e.message : String(e);
|
|
426
|
-
console.error(
|
|
472
|
+
console.error(chalk4.yellow(`Preflight check failed: ${msg}`));
|
|
427
473
|
if (opts.strict) process.exit(1);
|
|
428
474
|
}
|
|
429
475
|
}
|
|
@@ -439,7 +485,9 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
439
485
|
concurrency: parseInt(opts.concurrency, 10) || 8,
|
|
440
486
|
push: opts.push,
|
|
441
487
|
dsn: opts.dsn,
|
|
442
|
-
region
|
|
488
|
+
// An explicit --region wins; otherwise route to the workspace's pinned
|
|
489
|
+
// region as reported by preflight.
|
|
490
|
+
region: opts.region ?? pinnedRegion,
|
|
443
491
|
strict: opts.strict,
|
|
444
492
|
uiPurpose: opts.uiPurpose,
|
|
445
493
|
noLocalArtifacts: opts.noLocalArtifacts,
|
|
@@ -448,31 +496,32 @@ var scanCommand = new Command3("scan").description("Scan a project for upgrade d
|
|
|
448
496
|
packageManifest: opts.packageManifest,
|
|
449
497
|
driftBudget: parseNonNegativeNumber(opts.driftBudget, "--drift-budget"),
|
|
450
498
|
driftWorseningPercent: parseNonNegativeNumber(opts.driftWorsening, "--drift-worsening"),
|
|
451
|
-
projectScanTimeout: opts.projectScanTimeout ? parseInt(opts.projectScanTimeout, 10) || void 0 : void 0
|
|
499
|
+
projectScanTimeout: opts.projectScanTimeout ? parseInt(opts.projectScanTimeout, 10) || void 0 : void 0,
|
|
500
|
+
repositoryName: opts.repositoryName?.trim() || void 0
|
|
452
501
|
};
|
|
453
502
|
const artifact = await runScan(rootDir, scanOpts);
|
|
454
503
|
if (opts.failOn) {
|
|
455
504
|
const hasErrors = artifact.findings.some((f) => f.level === "error");
|
|
456
505
|
const hasWarnings = artifact.findings.some((f) => f.level === "warning");
|
|
457
506
|
if (opts.failOn === "error" && hasErrors) {
|
|
458
|
-
console.error(
|
|
507
|
+
console.error(chalk4.red(`
|
|
459
508
|
Failing: ${artifact.findings.filter((f) => f.level === "error").length} error finding(s) detected.`));
|
|
460
509
|
process.exit(2);
|
|
461
510
|
}
|
|
462
511
|
if (opts.failOn === "warn" && (hasErrors || hasWarnings)) {
|
|
463
|
-
console.error(
|
|
512
|
+
console.error(chalk4.red(`
|
|
464
513
|
Failing: findings detected at warn level or above.`));
|
|
465
514
|
process.exit(2);
|
|
466
515
|
}
|
|
467
516
|
}
|
|
468
517
|
if (scanOpts.driftBudget !== void 0 && artifact.drift.score > scanOpts.driftBudget) {
|
|
469
|
-
console.error(
|
|
518
|
+
console.error(chalk4.red(`
|
|
470
519
|
Failing fitness function: drift score ${artifact.drift.score}/100 exceeds budget ${scanOpts.driftBudget}.`));
|
|
471
520
|
process.exit(2);
|
|
472
521
|
}
|
|
473
522
|
if (scanOpts.driftWorseningPercent !== void 0) {
|
|
474
523
|
if (artifact.delta === void 0) {
|
|
475
|
-
console.error(
|
|
524
|
+
console.error(chalk4.red("\nFailing fitness function: --drift-worsening requires --baseline to compare against previous drift."));
|
|
476
525
|
process.exit(2);
|
|
477
526
|
}
|
|
478
527
|
if (artifact.delta > 0) {
|
|
@@ -480,7 +529,7 @@ Failing fitness function: drift score ${artifact.drift.score}/100 exceeds budget
|
|
|
480
529
|
const denominator = Math.max(Math.abs(baselineScore), 1e-4);
|
|
481
530
|
const worseningPercent = artifact.delta / denominator * 100;
|
|
482
531
|
if (worseningPercent > scanOpts.driftWorseningPercent) {
|
|
483
|
-
console.error(
|
|
532
|
+
console.error(chalk4.red(`
|
|
484
533
|
Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (threshold ${scanOpts.driftWorseningPercent}%).`));
|
|
485
534
|
process.exit(2);
|
|
486
535
|
}
|
|
@@ -494,28 +543,28 @@ Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (thr
|
|
|
494
543
|
// src/commands/report.ts
|
|
495
544
|
import * as path5 from "path";
|
|
496
545
|
import { Command as Command4 } from "commander";
|
|
497
|
-
import
|
|
546
|
+
import chalk6 from "chalk";
|
|
498
547
|
|
|
499
548
|
// src/formatters/text.ts
|
|
500
|
-
import
|
|
549
|
+
import chalk5 from "chalk";
|
|
501
550
|
function formatText(artifact) {
|
|
502
551
|
const lines = [];
|
|
503
|
-
const teal =
|
|
504
|
-
const mint =
|
|
552
|
+
const teal = chalk5.hex("#3FB0A4");
|
|
553
|
+
const mint = chalk5.hex("#4FE3C1");
|
|
505
554
|
lines.push("");
|
|
506
555
|
lines.push(" " + teal("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u256E") + mint("\u279C"));
|
|
507
|
-
lines.push(" " +
|
|
508
|
-
lines.push(" " +
|
|
556
|
+
lines.push(" " + chalk5.dim("\u2524") + teal("\u2502") + " " + mint("\u25FC") + " " + mint("\u25FC") + " " + teal("\u2502") + chalk5.dim("\u251C") + " " + chalk5.bold.white("vibgrate"));
|
|
557
|
+
lines.push(" " + chalk5.dim("\u2524") + teal("\u2502") + " " + chalk5.dim("\u2581\u2581") + " " + teal("\u2502") + chalk5.dim("\u251C") + " " + chalk5.dim(`Drift Intelligence Engine v${VERSION}`));
|
|
509
558
|
lines.push(" " + teal("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
510
559
|
lines.push("");
|
|
511
560
|
lines.push(teal("\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"));
|
|
512
|
-
lines.push(teal("\u2551 ") +
|
|
561
|
+
lines.push(teal("\u2551 ") + chalk5.bold.white("Vibgrate Drift Report") + teal(" \u2551"));
|
|
513
562
|
lines.push(teal("\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"));
|
|
514
563
|
lines.push("");
|
|
515
564
|
for (const project of artifact.projects) {
|
|
516
|
-
lines.push(
|
|
565
|
+
lines.push(chalk5.bold(` \u2500\u2500 ${project.name} `) + chalk5.dim(`(${project.type}) ${project.path}`));
|
|
517
566
|
if (project.runtime) {
|
|
518
|
-
const behindStr = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ?
|
|
567
|
+
const behindStr = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? chalk5.yellow(` (${project.runtimeMajorsBehind} major${project.runtimeMajorsBehind > 1 ? "s" : ""} behind)`) : chalk5.green(" (current)");
|
|
519
568
|
lines.push(` Runtime: ${project.runtime}${behindStr}`);
|
|
520
569
|
}
|
|
521
570
|
if (project.targetFramework) {
|
|
@@ -524,7 +573,7 @@ function formatText(artifact) {
|
|
|
524
573
|
if (project.frameworks.length > 0) {
|
|
525
574
|
lines.push(" Frameworks:");
|
|
526
575
|
for (const fw of project.frameworks) {
|
|
527
|
-
const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ?
|
|
576
|
+
const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? chalk5.green("current") : chalk5.yellow(`${fw.majorsBehind} behind`) : chalk5.dim("unknown");
|
|
528
577
|
lines.push(` ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
|
|
529
578
|
}
|
|
530
579
|
}
|
|
@@ -532,13 +581,13 @@ function formatText(artifact) {
|
|
|
532
581
|
const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
|
|
533
582
|
if (total > 0) {
|
|
534
583
|
lines.push(" Dependencies:");
|
|
535
|
-
lines.push(` ${
|
|
584
|
+
lines.push(` ${chalk5.green(`${b.current} current`)} ${chalk5.yellow(`${b.oneBehind} 1-behind`)} ${chalk5.red(`${b.twoPlusBehind} 2+ behind`)} ${chalk5.dim(`${b.unknown} unknown`)}`);
|
|
536
585
|
}
|
|
537
586
|
lines.push("");
|
|
538
587
|
}
|
|
539
588
|
if (artifact.delta !== void 0) {
|
|
540
|
-
const deltaStr = artifact.delta > 0 ?
|
|
541
|
-
lines.push(
|
|
589
|
+
const deltaStr = artifact.delta > 0 ? chalk5.green(`+${artifact.delta}`) : artifact.delta < 0 ? chalk5.red(`${artifact.delta}`) : chalk5.dim("0");
|
|
590
|
+
lines.push(chalk5.bold(" Drift Delta: ") + deltaStr + " (vs baseline)");
|
|
542
591
|
lines.push("");
|
|
543
592
|
}
|
|
544
593
|
if (artifact.extended) {
|
|
@@ -549,30 +598,30 @@ function formatText(artifact) {
|
|
|
549
598
|
const warnings = artifact.findings.filter((f) => f.level === "warning");
|
|
550
599
|
const notes = artifact.findings.filter((f) => f.level === "note");
|
|
551
600
|
const summary = [
|
|
552
|
-
errors.length > 0 ?
|
|
553
|
-
warnings.length > 0 ?
|
|
554
|
-
notes.length > 0 ?
|
|
555
|
-
].filter(Boolean).join(
|
|
556
|
-
lines.push(
|
|
601
|
+
errors.length > 0 ? chalk5.red(`${errors.length} error${errors.length !== 1 ? "s" : ""}`) : "",
|
|
602
|
+
warnings.length > 0 ? chalk5.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}`) : "",
|
|
603
|
+
notes.length > 0 ? chalk5.blue(`${notes.length} note${notes.length !== 1 ? "s" : ""}`) : ""
|
|
604
|
+
].filter(Boolean).join(chalk5.dim(", "));
|
|
605
|
+
lines.push(chalk5.bold.underline(` Findings`) + chalk5.dim(` (${summary})`));
|
|
557
606
|
for (const f of artifact.findings) {
|
|
558
|
-
const icon = f.level === "error" ?
|
|
607
|
+
const icon = f.level === "error" ? chalk5.red("\u2716") : f.level === "warning" ? chalk5.yellow("\u26A0") : chalk5.blue("\u2139");
|
|
559
608
|
lines.push(` ${icon} ${f.message}`);
|
|
560
|
-
lines.push(
|
|
609
|
+
lines.push(chalk5.dim(` ${f.ruleId} in ${f.location}`));
|
|
561
610
|
}
|
|
562
611
|
lines.push("");
|
|
563
612
|
}
|
|
564
613
|
const actions = generatePriorityActions(artifact);
|
|
565
614
|
if (actions.length > 0) {
|
|
566
|
-
lines.push(
|
|
567
|
-
lines.push(
|
|
568
|
-
lines.push(
|
|
615
|
+
lines.push(chalk5.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"));
|
|
616
|
+
lines.push(chalk5.bold.cyan("\u2551 Top Priority Actions \u2551"));
|
|
617
|
+
lines.push(chalk5.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"));
|
|
569
618
|
lines.push("");
|
|
570
619
|
for (let i = 0; i < actions.length; i++) {
|
|
571
620
|
const a = actions[i];
|
|
572
|
-
const num =
|
|
573
|
-
lines.push(`${num} ${
|
|
574
|
-
lines.push(
|
|
575
|
-
if (a.impact) lines.push(` Impact: ${
|
|
621
|
+
const num = chalk5.bold.cyan(` ${i + 1}.`);
|
|
622
|
+
lines.push(`${num} ${chalk5.bold(a.title)}`);
|
|
623
|
+
lines.push(chalk5.dim(` ${a.explanation}`));
|
|
624
|
+
if (a.impact) lines.push(` Impact: ${chalk5.green(a.impact)}`);
|
|
576
625
|
lines.push("");
|
|
577
626
|
}
|
|
578
627
|
}
|
|
@@ -580,38 +629,38 @@ function formatText(artifact) {
|
|
|
580
629
|
lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
|
|
581
630
|
}
|
|
582
631
|
if (artifact.solutions && artifact.solutions.length > 0) {
|
|
583
|
-
lines.push(
|
|
584
|
-
lines.push(
|
|
585
|
-
lines.push(
|
|
632
|
+
lines.push(chalk5.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"));
|
|
633
|
+
lines.push(chalk5.bold.cyan("\u2551 Solution Drift Summary \u2551"));
|
|
634
|
+
lines.push(chalk5.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"));
|
|
586
635
|
lines.push("");
|
|
587
636
|
for (const solution of artifact.solutions) {
|
|
588
637
|
const solScore = solution.drift?.score;
|
|
589
|
-
const color = typeof solScore === "number" ? solScore >= 70 ?
|
|
590
|
-
lines.push(` \u2022 ${solution.name} (${solution.projectPaths.length} projects) \u2014 ${typeof solScore === "number" ? color(`${solScore}/100`) :
|
|
638
|
+
const color = typeof solScore === "number" ? solScore >= 70 ? chalk5.green : solScore >= 40 ? chalk5.yellow : chalk5.red : chalk5.dim;
|
|
639
|
+
lines.push(` \u2022 ${solution.name} (${solution.projectPaths.length} projects) \u2014 ${typeof solScore === "number" ? color(`${solScore}/100`) : chalk5.dim("n/a")}`);
|
|
591
640
|
}
|
|
592
641
|
lines.push("");
|
|
593
642
|
}
|
|
594
|
-
const scoreColor = artifact.drift.score >= 70 ?
|
|
595
|
-
lines.push(
|
|
596
|
-
lines.push(
|
|
597
|
-
lines.push(
|
|
643
|
+
const scoreColor = artifact.drift.score >= 70 ? chalk5.green : artifact.drift.score >= 40 ? chalk5.yellow : chalk5.red;
|
|
644
|
+
lines.push(chalk5.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"));
|
|
645
|
+
lines.push(chalk5.bold.cyan("\u2551 Drift Score Summary \u2551"));
|
|
646
|
+
lines.push(chalk5.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"));
|
|
598
647
|
lines.push("");
|
|
599
|
-
lines.push(
|
|
600
|
-
lines.push(
|
|
601
|
-
lines.push(
|
|
648
|
+
lines.push(chalk5.bold(" Drift Score: ") + scoreColor.bold(`${artifact.drift.score}/100`));
|
|
649
|
+
lines.push(chalk5.bold(" Risk Level: ") + riskBadge(artifact.drift.riskLevel));
|
|
650
|
+
lines.push(chalk5.bold(" Projects: ") + `${artifact.projects.length}`);
|
|
602
651
|
if (artifact.vcs) {
|
|
603
652
|
const vcsParts = [artifact.vcs.type];
|
|
604
653
|
if (artifact.vcs.branch) vcsParts.push(artifact.vcs.branch);
|
|
605
|
-
if (artifact.vcs.shortSha) vcsParts.push(
|
|
606
|
-
lines.push(
|
|
654
|
+
if (artifact.vcs.shortSha) vcsParts.push(chalk5.dim(artifact.vcs.shortSha));
|
|
655
|
+
lines.push(chalk5.bold(" VCS: ") + vcsParts.join(" "));
|
|
607
656
|
}
|
|
608
657
|
lines.push("");
|
|
609
658
|
const m = new Set(artifact.drift.measured ?? ["runtime", "framework", "dependency", "eol"]);
|
|
610
|
-
lines.push(" " +
|
|
611
|
-
lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) :
|
|
612
|
-
lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) :
|
|
613
|
-
lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) :
|
|
614
|
-
lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) :
|
|
659
|
+
lines.push(" " + chalk5.bold.underline("Score Breakdown"));
|
|
660
|
+
lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) : chalk5.dim("n/a")}`);
|
|
661
|
+
lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) : chalk5.dim("n/a")}`);
|
|
662
|
+
lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) : chalk5.dim("n/a")}`);
|
|
663
|
+
lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) : chalk5.dim("n/a")}`);
|
|
615
664
|
lines.push("");
|
|
616
665
|
const scannedParts = [`Scanned at ${artifact.timestamp}`];
|
|
617
666
|
if (artifact.durationMs !== void 0) {
|
|
@@ -625,18 +674,18 @@ function formatText(artifact) {
|
|
|
625
674
|
scannedParts.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files`);
|
|
626
675
|
scannedParts.push(`${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
|
|
627
676
|
}
|
|
628
|
-
lines.push(
|
|
677
|
+
lines.push(chalk5.dim(` ${scannedParts.join(" \xB7 ")}`));
|
|
629
678
|
lines.push("");
|
|
630
679
|
return lines.join("\n");
|
|
631
680
|
}
|
|
632
681
|
function riskBadge(level) {
|
|
633
682
|
switch (level) {
|
|
634
683
|
case "low":
|
|
635
|
-
return
|
|
684
|
+
return chalk5.bgGreen.black(" LOW ");
|
|
636
685
|
case "moderate":
|
|
637
|
-
return
|
|
686
|
+
return chalk5.bgYellow.black(" MODERATE ");
|
|
638
687
|
case "high":
|
|
639
|
-
return
|
|
688
|
+
return chalk5.bgRed.white(" HIGH ");
|
|
640
689
|
default:
|
|
641
690
|
return level;
|
|
642
691
|
}
|
|
@@ -645,8 +694,8 @@ function scoreBar(score) {
|
|
|
645
694
|
const width = 20;
|
|
646
695
|
const filled = Math.round(score / 100 * width);
|
|
647
696
|
const empty = width - filled;
|
|
648
|
-
const color = score >= 70 ?
|
|
649
|
-
return color("\u2588".repeat(filled)) +
|
|
697
|
+
const color = score >= 70 ? chalk5.green : score >= 40 ? chalk5.yellow : chalk5.red;
|
|
698
|
+
return color("\u2588".repeat(filled)) + chalk5.dim("\u2591".repeat(empty)) + ` ${Math.round(score)}`;
|
|
650
699
|
}
|
|
651
700
|
var CATEGORY_LABELS = {
|
|
652
701
|
frontend: "Frontend",
|
|
@@ -675,11 +724,11 @@ function formatExtended(ext) {
|
|
|
675
724
|
const inv = ext.toolingInventory;
|
|
676
725
|
const categories = Object.entries(inv).filter(([, items]) => items.length > 0);
|
|
677
726
|
if (categories.length > 0) {
|
|
678
|
-
lines.push(
|
|
727
|
+
lines.push(chalk5.bold.underline(" Tech Stack"));
|
|
679
728
|
for (const [cat, items] of categories) {
|
|
680
729
|
const label = CATEGORY_LABELS[cat] ?? cat;
|
|
681
|
-
const names = items.map((i) =>
|
|
682
|
-
lines.push(` ${
|
|
730
|
+
const names = items.map((i) => chalk5.white(i.name)).join(chalk5.dim(", "));
|
|
731
|
+
lines.push(` ${chalk5.cyan(label)}: ${names}`);
|
|
683
732
|
}
|
|
684
733
|
lines.push("");
|
|
685
734
|
}
|
|
@@ -688,14 +737,14 @@ function formatExtended(ext) {
|
|
|
688
737
|
const svc = ext.serviceDependencies;
|
|
689
738
|
const categories = Object.entries(svc).filter(([, items]) => items.length > 0);
|
|
690
739
|
if (categories.length > 0) {
|
|
691
|
-
lines.push(
|
|
740
|
+
lines.push(chalk5.bold.underline(" Services & Integrations"));
|
|
692
741
|
for (const [cat, items] of categories) {
|
|
693
742
|
const label = CATEGORY_LABELS[cat] ?? cat;
|
|
694
743
|
const names = items.map((i) => {
|
|
695
|
-
const ver = i.version ?
|
|
696
|
-
return
|
|
697
|
-
}).join(
|
|
698
|
-
lines.push(` ${
|
|
744
|
+
const ver = i.version ? chalk5.dim(` ${i.version}`) : "";
|
|
745
|
+
return chalk5.white(i.name) + ver;
|
|
746
|
+
}).join(chalk5.dim(", "));
|
|
747
|
+
lines.push(` ${chalk5.cyan(label)}: ${names}`);
|
|
699
748
|
}
|
|
700
749
|
lines.push("");
|
|
701
750
|
}
|
|
@@ -703,19 +752,19 @@ function formatExtended(ext) {
|
|
|
703
752
|
if (ext.breakingChangeExposure) {
|
|
704
753
|
const bc = ext.breakingChangeExposure;
|
|
705
754
|
if (bc.deprecatedPackages.length > 0 || bc.legacyPolyfills.length > 0) {
|
|
706
|
-
lines.push(
|
|
707
|
-
const exposureColor = bc.exposureScore >= 40 ?
|
|
755
|
+
lines.push(chalk5.bold.underline(" Breaking Change Exposure"));
|
|
756
|
+
const exposureColor = bc.exposureScore >= 40 ? chalk5.red : bc.exposureScore >= 20 ? chalk5.yellow : chalk5.green;
|
|
708
757
|
lines.push(` Exposure Score: ${exposureColor.bold(`${bc.exposureScore}/100`)}`);
|
|
709
758
|
if (bc.deprecatedPackages.length > 0) {
|
|
710
|
-
lines.push(` ${
|
|
759
|
+
lines.push(` ${chalk5.red("Deprecated")}: ${bc.deprecatedPackages.map((p) => chalk5.dim(p)).join(", ")}`);
|
|
711
760
|
}
|
|
712
761
|
if (bc.legacyPolyfills.length > 0) {
|
|
713
|
-
lines.push(` ${
|
|
762
|
+
lines.push(` ${chalk5.yellow("Polyfills")}: ${bc.legacyPolyfills.map((p) => chalk5.dim(p)).join(", ")}`);
|
|
714
763
|
}
|
|
715
764
|
if (bc.peerConflictsDetected) {
|
|
716
|
-
lines.push(` ${
|
|
765
|
+
lines.push(` ${chalk5.red("\u26A0")} Peer dependency conflicts detected`);
|
|
717
766
|
}
|
|
718
|
-
lines.push(` Recommendation: ${
|
|
767
|
+
lines.push(` Recommendation: ${chalk5.bold(bc.overallRecommendation)}`);
|
|
719
768
|
const projectsWithPlans = bc.projectIntelligence.filter((p) => p.packages.length > 0).slice(0, 3);
|
|
720
769
|
if (projectsWithPlans.length > 0) {
|
|
721
770
|
lines.push(" Major Upgrade Intelligence:");
|
|
@@ -731,21 +780,21 @@ function formatExtended(ext) {
|
|
|
731
780
|
}
|
|
732
781
|
if (ext.tsModernity && ext.tsModernity.typescriptVersion) {
|
|
733
782
|
const ts = ext.tsModernity;
|
|
734
|
-
lines.push(
|
|
783
|
+
lines.push(chalk5.bold.underline(" TypeScript"));
|
|
735
784
|
const parts = [];
|
|
736
785
|
parts.push(`v${ts.typescriptVersion}`);
|
|
737
|
-
if (ts.strict === true) parts.push(
|
|
738
|
-
else if (ts.strict === false) parts.push(
|
|
786
|
+
if (ts.strict === true) parts.push(chalk5.green("strict \u2714"));
|
|
787
|
+
else if (ts.strict === false) parts.push(chalk5.yellow("strict \u2716"));
|
|
739
788
|
if (ts.moduleType) parts.push(ts.moduleType.toUpperCase());
|
|
740
789
|
if (ts.target) parts.push(`target: ${ts.target}`);
|
|
741
|
-
lines.push(` ${parts.join(
|
|
790
|
+
lines.push(` ${parts.join(chalk5.dim(" \xB7 "))}`);
|
|
742
791
|
lines.push("");
|
|
743
792
|
}
|
|
744
793
|
if (ext.buildDeploy) {
|
|
745
794
|
const bd = ext.buildDeploy;
|
|
746
795
|
const hasSomething = bd.ci.length > 0 || bd.docker.dockerfileCount > 0 || bd.packageManagers.length > 0;
|
|
747
796
|
if (hasSomething) {
|
|
748
|
-
lines.push(
|
|
797
|
+
lines.push(chalk5.bold.underline(" Build & Deploy"));
|
|
749
798
|
if (bd.ci.length > 0) lines.push(` CI: ${bd.ci.join(", ")}`);
|
|
750
799
|
if (bd.docker.dockerfileCount > 0) {
|
|
751
800
|
lines.push(` Docker: ${bd.docker.dockerfileCount} Dockerfile${bd.docker.dockerfileCount !== 1 ? "s" : ""} (${bd.docker.baseImages.join(", ")})`);
|
|
@@ -758,42 +807,42 @@ function formatExtended(ext) {
|
|
|
758
807
|
}
|
|
759
808
|
if (ext.uiPurpose) {
|
|
760
809
|
const up = ext.uiPurpose;
|
|
761
|
-
lines.push(
|
|
762
|
-
lines.push(` Frameworks: ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") :
|
|
763
|
-
lines.push(` Evidence: ${up.topEvidence.length}${up.capped ?
|
|
810
|
+
lines.push(chalk5.bold.underline(" Product Purpose Signals"));
|
|
811
|
+
lines.push(` Frameworks: ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : chalk5.dim("unknown")}`);
|
|
812
|
+
lines.push(` Evidence: ${up.topEvidence.length}${up.capped ? chalk5.dim(` of ${up.evidenceCount} (capped)`) : ""}`);
|
|
764
813
|
const top = up.topEvidence.slice(0, 8);
|
|
765
814
|
if (top.length > 0) {
|
|
766
815
|
lines.push(" Top Signals:");
|
|
767
816
|
for (const item of top) {
|
|
768
|
-
lines.push(` - [${item.kind}] ${item.value} ${
|
|
817
|
+
lines.push(` - [${item.kind}] ${item.value} ${chalk5.dim(`(${item.file})`)}`);
|
|
769
818
|
}
|
|
770
819
|
}
|
|
771
820
|
if (up.unknownSignals.length > 0) {
|
|
772
821
|
lines.push(" Unknowns:");
|
|
773
822
|
for (const u of up.unknownSignals.slice(0, 4)) {
|
|
774
|
-
lines.push(` - ${
|
|
823
|
+
lines.push(` - ${chalk5.yellow(u)}`);
|
|
775
824
|
}
|
|
776
825
|
}
|
|
777
826
|
lines.push("");
|
|
778
827
|
}
|
|
779
828
|
if (ext.securityPosture) {
|
|
780
829
|
const sec = ext.securityPosture;
|
|
781
|
-
lines.push(
|
|
830
|
+
lines.push(chalk5.bold.underline(" Security Posture"));
|
|
782
831
|
const checks = [];
|
|
783
|
-
checks.push(sec.lockfilePresent ?
|
|
784
|
-
checks.push(sec.gitignoreCoversEnv ?
|
|
785
|
-
checks.push(sec.gitignoreCoversNodeModules ?
|
|
786
|
-
if (sec.multipleLockfileTypes) checks.push(
|
|
787
|
-
if (sec.envFilesTracked) checks.push(
|
|
788
|
-
lines.push(` ${checks.join(
|
|
832
|
+
checks.push(sec.lockfilePresent ? chalk5.green("Lockfile \u2714") : chalk5.red("Lockfile \u2716"));
|
|
833
|
+
checks.push(sec.gitignoreCoversEnv ? chalk5.green(".env \u2714") : chalk5.red(".env \u2716"));
|
|
834
|
+
checks.push(sec.gitignoreCoversNodeModules ? chalk5.green("node_modules \u2714") : chalk5.yellow("node_modules \u2716"));
|
|
835
|
+
if (sec.multipleLockfileTypes) checks.push(chalk5.yellow("Multiple lockfiles \u26A0"));
|
|
836
|
+
if (sec.envFilesTracked) checks.push(chalk5.red("Env files tracked \u2716"));
|
|
837
|
+
lines.push(` ${checks.join(chalk5.dim(" \xB7 "))}`);
|
|
789
838
|
lines.push("");
|
|
790
839
|
}
|
|
791
840
|
if (ext.platformMatrix) {
|
|
792
841
|
const pm = ext.platformMatrix;
|
|
793
842
|
if (pm.nativeModules.length > 0 || pm.dockerBaseImages.length > 0) {
|
|
794
|
-
lines.push(
|
|
843
|
+
lines.push(chalk5.bold.underline(" Platform"));
|
|
795
844
|
if (pm.nativeModules.length > 0) {
|
|
796
|
-
lines.push(` Native modules: ${pm.nativeModules.map((m) =>
|
|
845
|
+
lines.push(` Native modules: ${pm.nativeModules.map((m) => chalk5.dim(m)).join(", ")}`);
|
|
797
846
|
}
|
|
798
847
|
if (pm.osAssumptions.length > 0) {
|
|
799
848
|
lines.push(` OS assumptions: ${pm.osAssumptions.join(", ")}`);
|
|
@@ -803,25 +852,25 @@ function formatExtended(ext) {
|
|
|
803
852
|
}
|
|
804
853
|
if (ext.codeQuality) {
|
|
805
854
|
const cq = ext.codeQuality;
|
|
806
|
-
lines.push(
|
|
807
|
-
lines.push(` Files: ${
|
|
855
|
+
lines.push(chalk5.bold.underline(" Code Quality"));
|
|
856
|
+
lines.push(` Files: ${chalk5.white(`${cq.filesAnalyzed}`)} \xB7 Functions: ${chalk5.white(`${cq.functionsAnalyzed}`)} \xB7 Avg complexity: ${chalk5.white(`${cq.avgCyclomaticComplexity}`)} \xB7 Avg length: ${chalk5.white(`${cq.avgFunctionLength}`)} lines`);
|
|
808
857
|
lines.push(` Max nesting: ${cq.maxNestingDepth} \xB7 Circular deps: ${cq.circularDependencies} \xB7 Dead code: ${cq.deadCodePercent}%`);
|
|
809
858
|
if (cq.godFiles.length > 0) {
|
|
810
859
|
const preview = cq.godFiles.slice(0, 3).map((f) => `${f.path} (${f.lines} lines)`).join(", ");
|
|
811
|
-
lines.push(` ${
|
|
860
|
+
lines.push(` ${chalk5.yellow("God files")}: ${preview}`);
|
|
812
861
|
}
|
|
813
862
|
lines.push("");
|
|
814
863
|
}
|
|
815
864
|
if (ext.dependencyGraph) {
|
|
816
865
|
const dg = ext.dependencyGraph;
|
|
817
866
|
if (dg.lockfileType) {
|
|
818
|
-
lines.push(
|
|
819
|
-
lines.push(` ${dg.lockfileType}: ${
|
|
867
|
+
lines.push(chalk5.bold.underline(" Dependency Graph"));
|
|
868
|
+
lines.push(` ${dg.lockfileType}: ${chalk5.white(`${dg.totalUnique}`)} unique, ${chalk5.white(`${dg.totalInstalled}`)} installed`);
|
|
820
869
|
if (dg.duplicatedPackages.length > 0) {
|
|
821
|
-
lines.push(` ${
|
|
870
|
+
lines.push(` ${chalk5.yellow(`${dg.duplicatedPackages.length} duplicated`)} packages`);
|
|
822
871
|
}
|
|
823
872
|
if (dg.phantomDependencies.length > 0) {
|
|
824
|
-
lines.push(` ${
|
|
873
|
+
lines.push(` ${chalk5.red(`${dg.phantomDependencies.length} phantom`)} dependencies`);
|
|
825
874
|
}
|
|
826
875
|
lines.push("");
|
|
827
876
|
}
|
|
@@ -830,17 +879,17 @@ function formatExtended(ext) {
|
|
|
830
879
|
}
|
|
831
880
|
function formatArchitectureDiagram(arch) {
|
|
832
881
|
const lines = [];
|
|
833
|
-
lines.push(
|
|
834
|
-
lines.push(
|
|
835
|
-
lines.push(
|
|
882
|
+
lines.push(chalk5.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"));
|
|
883
|
+
lines.push(chalk5.bold.cyan("\u2551 Architecture Layers \u2551"));
|
|
884
|
+
lines.push(chalk5.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"));
|
|
836
885
|
lines.push("");
|
|
837
|
-
lines.push(
|
|
838
|
-
lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ?
|
|
886
|
+
lines.push(chalk5.bold(" Archetype: ") + `${arch.archetype}` + chalk5.dim(` (${Math.round(arch.archetypeConfidence * 100)}% confidence)`));
|
|
887
|
+
lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ? chalk5.dim(` (${arch.unclassified} unclassified)`) : ""));
|
|
839
888
|
lines.push("");
|
|
840
889
|
if (arch.layers.length > 0) {
|
|
841
890
|
for (const layer of arch.layers) {
|
|
842
|
-
const risk = layer.riskLevel === "none" ?
|
|
843
|
-
lines.push(` ${
|
|
891
|
+
const risk = layer.riskLevel === "none" ? chalk5.dim("none") : layer.riskLevel === "low" ? chalk5.green("low") : layer.riskLevel === "moderate" ? chalk5.yellow("moderate") : chalk5.red("high");
|
|
892
|
+
lines.push(` ${chalk5.bold(layer.layer)} ${layer.fileCount} file${layer.fileCount !== 1 ? "s" : ""} drift ${scoreBar(layer.driftScore)} risk ${risk}`);
|
|
844
893
|
}
|
|
845
894
|
lines.push("");
|
|
846
895
|
}
|
|
@@ -1211,8 +1260,8 @@ function formatMarkdown(artifact) {
|
|
|
1211
1260
|
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) => {
|
|
1212
1261
|
const artifactPath = path5.resolve(opts.in);
|
|
1213
1262
|
if (!await pathExists(artifactPath)) {
|
|
1214
|
-
console.error(
|
|
1215
|
-
console.error(
|
|
1263
|
+
console.error(chalk6.red(`Artifact not found: ${artifactPath}`));
|
|
1264
|
+
console.error(chalk6.dim('Run "vibgrate scan" first to generate a scan artifact.'));
|
|
1216
1265
|
process.exit(1);
|
|
1217
1266
|
}
|
|
1218
1267
|
const artifact = await readJsonFile(artifactPath);
|
|
@@ -1232,7 +1281,7 @@ var reportCommand = new Command4("report").description("Generate a drift report
|
|
|
1232
1281
|
|
|
1233
1282
|
// src/commands/login.ts
|
|
1234
1283
|
import { Command as Command5 } from "commander";
|
|
1235
|
-
import
|
|
1284
|
+
import chalk7 from "chalk";
|
|
1236
1285
|
|
|
1237
1286
|
// src/utils/open-url.ts
|
|
1238
1287
|
import { spawn } from "child_process";
|
|
@@ -1268,7 +1317,7 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1268
1317
|
try {
|
|
1269
1318
|
ingestHost = resolveIngestHost(opts.region, opts.ingest);
|
|
1270
1319
|
} catch (e) {
|
|
1271
|
-
console.error(
|
|
1320
|
+
console.error(chalk7.red(e instanceof Error ? e.message : String(e)));
|
|
1272
1321
|
process.exit(1);
|
|
1273
1322
|
}
|
|
1274
1323
|
const base = `https://${ingestHost}/v1/auth/device`;
|
|
@@ -1280,28 +1329,28 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1280
1329
|
body: "{}"
|
|
1281
1330
|
});
|
|
1282
1331
|
if (!res.ok) {
|
|
1283
|
-
console.error(
|
|
1332
|
+
console.error(chalk7.red(`Failed to start login (HTTP ${res.status}).`));
|
|
1284
1333
|
process.exit(1);
|
|
1285
1334
|
}
|
|
1286
1335
|
start = await res.json();
|
|
1287
1336
|
} catch (e) {
|
|
1288
|
-
console.error(
|
|
1337
|
+
console.error(chalk7.red(`Could not reach ${ingestHost}: ${e instanceof Error ? e.message : String(e)}`));
|
|
1289
1338
|
process.exit(1);
|
|
1290
1339
|
}
|
|
1291
1340
|
console.log("");
|
|
1292
1341
|
console.log("To finish signing in, open this URL and approve the request:");
|
|
1293
1342
|
console.log("");
|
|
1294
|
-
console.log(" " +
|
|
1343
|
+
console.log(" " + chalk7.cyan(start.verificationUri));
|
|
1295
1344
|
console.log("");
|
|
1296
|
-
console.log(" Your code: " +
|
|
1345
|
+
console.log(" Your code: " + chalk7.bold(start.userCode));
|
|
1297
1346
|
console.log("");
|
|
1298
1347
|
if (opts.browser) {
|
|
1299
1348
|
const opened = openUrl(start.verificationUriComplete);
|
|
1300
1349
|
if (opened) {
|
|
1301
|
-
console.log(
|
|
1350
|
+
console.log(chalk7.dim("Opening your browser\u2026 (if it does not open, use the URL above)"));
|
|
1302
1351
|
}
|
|
1303
1352
|
}
|
|
1304
|
-
console.log(
|
|
1353
|
+
console.log(chalk7.dim("Waiting for approval\u2026"));
|
|
1305
1354
|
const intervalMs = Math.max(2, start.interval || 5) * 1e3;
|
|
1306
1355
|
const deadline = Date.now() + (start.expiresIn || 900) * 1e3;
|
|
1307
1356
|
while (Date.now() < deadline) {
|
|
@@ -1327,40 +1376,40 @@ var loginCommand = new Command5("login").description("Authenticate the CLI with
|
|
|
1327
1376
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1328
1377
|
});
|
|
1329
1378
|
console.log("");
|
|
1330
|
-
console.log(
|
|
1379
|
+
console.log(chalk7.green("\u2714") + " Logged in.");
|
|
1331
1380
|
if (token.workspaceId) {
|
|
1332
|
-
console.log(" Workspace: " +
|
|
1381
|
+
console.log(" Workspace: " + chalk7.bold(token.workspaceId));
|
|
1333
1382
|
}
|
|
1334
|
-
console.log(
|
|
1335
|
-
console.log(
|
|
1383
|
+
console.log(chalk7.dim(` Credentials saved to ${credentialsPath()}`));
|
|
1384
|
+
console.log(chalk7.dim(' You can now run "vibgrate scan --push".'));
|
|
1336
1385
|
return;
|
|
1337
1386
|
}
|
|
1338
1387
|
if (token.status === "access_denied") {
|
|
1339
|
-
console.error(
|
|
1388
|
+
console.error(chalk7.red("\u2716 Login was denied in the browser."));
|
|
1340
1389
|
process.exit(1);
|
|
1341
1390
|
}
|
|
1342
1391
|
if (token.status === "expired" || token.status === "invalid") {
|
|
1343
|
-
console.error(
|
|
1392
|
+
console.error(chalk7.red('\u2716 Login request expired. Run "vibgrate login" again.'));
|
|
1344
1393
|
process.exit(1);
|
|
1345
1394
|
}
|
|
1346
1395
|
if (token.status === "error") {
|
|
1347
|
-
console.error(
|
|
1396
|
+
console.error(chalk7.red(`\u2716 ${token.error ?? "Login failed."}`));
|
|
1348
1397
|
process.exit(1);
|
|
1349
1398
|
}
|
|
1350
1399
|
}
|
|
1351
|
-
console.error(
|
|
1400
|
+
console.error(chalk7.red('\u2716 Timed out waiting for approval. Run "vibgrate login" again.'));
|
|
1352
1401
|
process.exit(1);
|
|
1353
1402
|
});
|
|
1354
1403
|
|
|
1355
1404
|
// src/commands/logout.ts
|
|
1356
1405
|
import { Command as Command6 } from "commander";
|
|
1357
|
-
import
|
|
1406
|
+
import chalk8 from "chalk";
|
|
1358
1407
|
var logoutCommand = new Command6("logout").description("Clear stored Vibgrate login credentials").action(() => {
|
|
1359
1408
|
const cleared = clearStoredCredentials();
|
|
1360
1409
|
if (cleared) {
|
|
1361
|
-
console.log(
|
|
1410
|
+
console.log(chalk8.green("\u2714") + " Logged out. Stored credentials removed.");
|
|
1362
1411
|
} else {
|
|
1363
|
-
console.log(
|
|
1412
|
+
console.log(chalk8.dim(`No stored credentials found at ${credentialsPath()}.`));
|
|
1364
1413
|
}
|
|
1365
1414
|
});
|
|
1366
1415
|
|
|
@@ -1368,7 +1417,7 @@ var logoutCommand = new Command6("logout").description("Clear stored Vibgrate lo
|
|
|
1368
1417
|
import * as crypto2 from "crypto";
|
|
1369
1418
|
import * as path6 from "path";
|
|
1370
1419
|
import { Command as Command7 } from "commander";
|
|
1371
|
-
import
|
|
1420
|
+
import chalk9 from "chalk";
|
|
1372
1421
|
|
|
1373
1422
|
// src/utils/compact-artifact.ts
|
|
1374
1423
|
import * as zlib from "zlib";
|
|
@@ -1643,22 +1692,22 @@ function computeHmac(body, secret) {
|
|
|
1643
1692
|
var pushCommand = new Command7("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 (${availableRegionIds().join(", ")})`).option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
|
|
1644
1693
|
const dsn = resolveDsn(opts.dsn);
|
|
1645
1694
|
if (!dsn) {
|
|
1646
|
-
console.error(
|
|
1647
|
-
console.error(
|
|
1695
|
+
console.error(chalk9.red("No DSN provided."));
|
|
1696
|
+
console.error(chalk9.dim('Run "vibgrate login", set VIBGRATE_DSN, or use the --dsn flag.'));
|
|
1648
1697
|
if (opts.strict) process.exit(1);
|
|
1649
1698
|
return;
|
|
1650
1699
|
}
|
|
1651
1700
|
const parsed = parseDsn2(dsn);
|
|
1652
1701
|
if (!parsed) {
|
|
1653
|
-
console.error(
|
|
1654
|
-
console.error(
|
|
1702
|
+
console.error(chalk9.red("Invalid DSN format."));
|
|
1703
|
+
console.error(chalk9.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>"));
|
|
1655
1704
|
if (opts.strict) process.exit(1);
|
|
1656
1705
|
return;
|
|
1657
1706
|
}
|
|
1658
1707
|
const filePath = path6.resolve(opts.file);
|
|
1659
1708
|
if (!await pathExists(filePath)) {
|
|
1660
|
-
console.error(
|
|
1661
|
-
console.error(
|
|
1709
|
+
console.error(chalk9.red(`Scan artifact not found: ${filePath}`));
|
|
1710
|
+
console.error(chalk9.dim('Run "vibgrate scan" first.'));
|
|
1662
1711
|
if (opts.strict) process.exit(1);
|
|
1663
1712
|
return;
|
|
1664
1713
|
}
|
|
@@ -1670,46 +1719,43 @@ var pushCommand = new Command7("push").description("Push scan results to Vibgrat
|
|
|
1670
1719
|
try {
|
|
1671
1720
|
host = resolveIngestHost(opts.region);
|
|
1672
1721
|
} catch (e) {
|
|
1673
|
-
console.error(
|
|
1722
|
+
console.error(chalk9.red(e instanceof Error ? e.message : String(e)));
|
|
1674
1723
|
if (opts.strict) process.exit(1);
|
|
1675
1724
|
return;
|
|
1676
1725
|
}
|
|
1677
1726
|
}
|
|
1678
|
-
const url = `${parsed.scheme}://${host}/v1/ingest/scan`;
|
|
1679
1727
|
const originalSize = JSON.stringify(artifact).length;
|
|
1680
1728
|
const compressedSize = body.length;
|
|
1681
1729
|
const ratio = ((1 - compressedSize / originalSize) * 100).toFixed(0);
|
|
1682
|
-
console.log(
|
|
1730
|
+
console.log(chalk9.dim(`Uploading to ${host}... (${(compressedSize / 1024).toFixed(0)} KB, ${ratio}% smaller)`));
|
|
1683
1731
|
try {
|
|
1684
|
-
const response = await
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
// Prevent keep-alive delays on exit
|
|
1693
|
-
},
|
|
1694
|
-
body
|
|
1732
|
+
const { response, host: uploadedHost } = await uploadScanArtifact({
|
|
1733
|
+
scheme: parsed.scheme,
|
|
1734
|
+
host,
|
|
1735
|
+
keyId: parsed.keyId,
|
|
1736
|
+
secret: parsed.secret,
|
|
1737
|
+
body,
|
|
1738
|
+
contentEncoding,
|
|
1739
|
+
timestamp
|
|
1695
1740
|
});
|
|
1741
|
+
host = uploadedHost;
|
|
1696
1742
|
if (!response.ok) {
|
|
1697
1743
|
const text = await response.text();
|
|
1698
1744
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
1699
1745
|
}
|
|
1700
1746
|
const result = await response.json();
|
|
1701
|
-
console.log(
|
|
1747
|
+
console.log(chalk9.green("\u2714") + ` Scan queued for processing (${result.ingestId ?? "ok"})`);
|
|
1702
1748
|
console.log();
|
|
1703
|
-
console.log(
|
|
1749
|
+
console.log(chalk9.dim("Processing continues in the background. Results available shortly."));
|
|
1704
1750
|
console.log();
|
|
1705
1751
|
if (result.ingestId) {
|
|
1706
1752
|
const dashHost = dashHostForIngestHost(host);
|
|
1707
1753
|
const reportUrl = `https://${dashHost}/${parsed.workspaceId}/scan/${result.ingestId}`;
|
|
1708
|
-
console.log(
|
|
1754
|
+
console.log(chalk9.dim("View report: ") + chalk9.underline(reportUrl));
|
|
1709
1755
|
}
|
|
1710
1756
|
} catch (e) {
|
|
1711
1757
|
const msg = e instanceof Error ? e.message : String(e);
|
|
1712
|
-
console.error(
|
|
1758
|
+
console.error(chalk9.red(`Upload failed: ${msg}`));
|
|
1713
1759
|
if (opts.strict) process.exit(1);
|
|
1714
1760
|
}
|
|
1715
1761
|
});
|
|
@@ -1718,7 +1764,7 @@ var pushCommand = new Command7("push").description("Push scan results to Vibgrat
|
|
|
1718
1764
|
import { execSync } from "child_process";
|
|
1719
1765
|
import * as path8 from "path";
|
|
1720
1766
|
import { Command as Command8 } from "commander";
|
|
1721
|
-
import
|
|
1767
|
+
import chalk10 from "chalk";
|
|
1722
1768
|
|
|
1723
1769
|
// src/utils/update-check.ts
|
|
1724
1770
|
var import_semver = __toESM(require_semver(), 1);
|
|
@@ -1865,21 +1911,21 @@ async function isDevDependency(cwd) {
|
|
|
1865
1911
|
}
|
|
1866
1912
|
}
|
|
1867
1913
|
var updateCommand = new Command8("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) => {
|
|
1868
|
-
console.log(
|
|
1869
|
-
console.log(
|
|
1914
|
+
console.log(chalk10.dim(`Current version: ${VERSION}`));
|
|
1915
|
+
console.log(chalk10.dim("Checking npm registry..."));
|
|
1870
1916
|
const latest = await fetchLatestVersion();
|
|
1871
1917
|
if (!latest) {
|
|
1872
|
-
console.error(
|
|
1918
|
+
console.error(chalk10.red("Could not reach the npm registry. Check your network connection."));
|
|
1873
1919
|
process.exit(1);
|
|
1874
1920
|
}
|
|
1875
1921
|
const semver2 = await import("./semver-2FJFIYVN.js");
|
|
1876
1922
|
if (!semver2.gt(latest, VERSION)) {
|
|
1877
|
-
console.log(
|
|
1923
|
+
console.log(chalk10.green("\u2714") + ` You are on the latest version (${VERSION}).`);
|
|
1878
1924
|
return;
|
|
1879
1925
|
}
|
|
1880
|
-
console.log(
|
|
1926
|
+
console.log(chalk10.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
|
|
1881
1927
|
if (opts.check) {
|
|
1882
|
-
console.log(
|
|
1928
|
+
console.log(chalk10.dim('Run "vibgrate update" to install.'));
|
|
1883
1929
|
return;
|
|
1884
1930
|
}
|
|
1885
1931
|
const cwd = process.cwd();
|
|
@@ -1889,17 +1935,17 @@ var updateCommand = new Command8("update").description("Update vibgrate to the l
|
|
|
1889
1935
|
let cmd;
|
|
1890
1936
|
if (isGlobal) {
|
|
1891
1937
|
cmd = getGlobalUpdateCommand(pm, "@vibgrate/cli", latest);
|
|
1892
|
-
console.log(
|
|
1938
|
+
console.log(chalk10.dim(`Updating global installation with ${pm}: ${cmd}`));
|
|
1893
1939
|
} else {
|
|
1894
1940
|
const isDev = await isDevDependency(cwd);
|
|
1895
1941
|
cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev);
|
|
1896
|
-
console.log(
|
|
1942
|
+
console.log(chalk10.dim(`Using ${pm}: ${cmd}`));
|
|
1897
1943
|
}
|
|
1898
1944
|
try {
|
|
1899
1945
|
execSync(cmd, { cwd, stdio: "inherit" });
|
|
1900
|
-
console.log(
|
|
1946
|
+
console.log(chalk10.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
|
|
1901
1947
|
} catch {
|
|
1902
|
-
console.error(
|
|
1948
|
+
console.error(chalk10.red(`Update failed. Run manually: ${cmd}`));
|
|
1903
1949
|
process.exit(1);
|
|
1904
1950
|
}
|
|
1905
1951
|
});
|
|
@@ -1908,7 +1954,7 @@ var updateCommand = new Command8("update").description("Update vibgrate to the l
|
|
|
1908
1954
|
import * as path9 from "path";
|
|
1909
1955
|
import { randomUUID } from "crypto";
|
|
1910
1956
|
import { Command as Command9 } from "commander";
|
|
1911
|
-
import
|
|
1957
|
+
import chalk11 from "chalk";
|
|
1912
1958
|
function flattenDependencies(artifact) {
|
|
1913
1959
|
const rows = [];
|
|
1914
1960
|
for (const project of artifact.projects) {
|
|
@@ -2048,7 +2094,7 @@ function formatDeltaText(base, current) {
|
|
|
2048
2094
|
async function readArtifactOrExit(filePath) {
|
|
2049
2095
|
const absolutePath = path9.resolve(filePath);
|
|
2050
2096
|
if (!await pathExists(absolutePath)) {
|
|
2051
|
-
console.error(
|
|
2097
|
+
console.error(chalk11.red(`Artifact not found: ${absolutePath}`));
|
|
2052
2098
|
process.exit(1);
|
|
2053
2099
|
}
|
|
2054
2100
|
return readJsonFile(absolutePath);
|
|
@@ -2057,14 +2103,14 @@ var exportCommand = new Command9("export").description("Export scan artifact as
|
|
|
2057
2103
|
const artifact = await readArtifactOrExit(opts.in);
|
|
2058
2104
|
const format = opts.format.toLowerCase();
|
|
2059
2105
|
if (format !== "cyclonedx" && format !== "spdx") {
|
|
2060
|
-
console.error(
|
|
2106
|
+
console.error(chalk11.red("Invalid SBOM format. Use cyclonedx or spdx."));
|
|
2061
2107
|
process.exit(1);
|
|
2062
2108
|
}
|
|
2063
2109
|
const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
|
|
2064
2110
|
const body = JSON.stringify(sbom, null, 2);
|
|
2065
2111
|
if (opts.out) {
|
|
2066
2112
|
await writeTextFile(path9.resolve(opts.out), body);
|
|
2067
|
-
console.log(
|
|
2113
|
+
console.log(chalk11.green("\u2714") + ` SBOM written to ${opts.out}`);
|
|
2068
2114
|
} else {
|
|
2069
2115
|
console.log(body);
|
|
2070
2116
|
}
|
|
@@ -2075,7 +2121,7 @@ var deltaCommand = new Command9("delta").description("Show SBOM delta between tw
|
|
|
2075
2121
|
const report = formatDeltaText(base, current);
|
|
2076
2122
|
if (opts.out) {
|
|
2077
2123
|
await writeTextFile(path9.resolve(opts.out), report);
|
|
2078
|
-
console.log(
|
|
2124
|
+
console.log(chalk11.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
|
|
2079
2125
|
} else {
|
|
2080
2126
|
console.log(report);
|
|
2081
2127
|
}
|
|
@@ -2089,7 +2135,7 @@ import * as fs4 from "fs/promises";
|
|
|
2089
2135
|
import { existsSync, rmSync as rmSync2 } from "fs";
|
|
2090
2136
|
import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
|
|
2091
2137
|
import { Command as Command10 } from "commander";
|
|
2092
|
-
import
|
|
2138
|
+
import chalk12 from "chalk";
|
|
2093
2139
|
|
|
2094
2140
|
// src/behavioural/derive.ts
|
|
2095
2141
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3577,7 +3623,7 @@ async function runNodeWorker(rootDir, language, opts) {
|
|
|
3577
3623
|
continue;
|
|
3578
3624
|
}
|
|
3579
3625
|
if (opts.verbose) {
|
|
3580
|
-
process.stderr.write(
|
|
3626
|
+
process.stderr.write(chalk12.dim(`[${language}] ${trimmed}
|
|
3581
3627
|
`));
|
|
3582
3628
|
}
|
|
3583
3629
|
if (trimmed.startsWith("[error]")) {
|
|
@@ -3718,7 +3764,7 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3718
3764
|
let stdoutBuf = "";
|
|
3719
3765
|
let killed = false;
|
|
3720
3766
|
if (opts.verbose) {
|
|
3721
|
-
process.stderr.write(
|
|
3767
|
+
process.stderr.write(chalk12.dim(`[${language}] Spawning: ${spec.cmd} ${spec.args.join(" ")}
|
|
3722
3768
|
`));
|
|
3723
3769
|
}
|
|
3724
3770
|
const runtimeReq = NATIVE_RUNTIME_REQUIREMENTS[language];
|
|
@@ -3759,7 +3805,7 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3759
3805
|
const trimmed = line.trim();
|
|
3760
3806
|
if (!trimmed) continue;
|
|
3761
3807
|
if (opts.verbose) {
|
|
3762
|
-
process.stderr.write(
|
|
3808
|
+
process.stderr.write(chalk12.dim(`[${language}] ${trimmed}
|
|
3763
3809
|
`));
|
|
3764
3810
|
}
|
|
3765
3811
|
if (trimmed.startsWith("[error]")) {
|
|
@@ -3789,14 +3835,14 @@ async function runNativeWorker(rootDir, language, opts) {
|
|
|
3789
3835
|
async function pushFacts(facts, dsn, verbose) {
|
|
3790
3836
|
const parsed = parseDsn2(dsn);
|
|
3791
3837
|
if (!parsed) {
|
|
3792
|
-
process.stderr.write(
|
|
3793
|
-
process.stderr.write(
|
|
3838
|
+
process.stderr.write(chalk12.red("Invalid DSN format.\n"));
|
|
3839
|
+
process.stderr.write(chalk12.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
|
|
3794
3840
|
return false;
|
|
3795
3841
|
}
|
|
3796
3842
|
const body = facts.join("\n") + "\n";
|
|
3797
3843
|
const url = `${parsed.scheme}://${parsed.host}/v1/ingest/hcs`;
|
|
3798
3844
|
if (verbose) {
|
|
3799
|
-
process.stderr.write(
|
|
3845
|
+
process.stderr.write(chalk12.dim(`Pushing ${facts.length} facts to ${parsed.host}...
|
|
3800
3846
|
`));
|
|
3801
3847
|
}
|
|
3802
3848
|
try {
|
|
@@ -3814,17 +3860,17 @@ async function pushFacts(facts, dsn, verbose) {
|
|
|
3814
3860
|
});
|
|
3815
3861
|
if (!response.ok) {
|
|
3816
3862
|
const text = await response.text().catch(() => "");
|
|
3817
|
-
process.stderr.write(
|
|
3863
|
+
process.stderr.write(chalk12.red(`Push failed: HTTP ${response.status} ${text}
|
|
3818
3864
|
`));
|
|
3819
3865
|
return false;
|
|
3820
3866
|
}
|
|
3821
3867
|
if (verbose) {
|
|
3822
|
-
process.stderr.write(
|
|
3868
|
+
process.stderr.write(chalk12.green(`\u2714 Pushed ${facts.length} facts successfully
|
|
3823
3869
|
`));
|
|
3824
3870
|
}
|
|
3825
3871
|
return true;
|
|
3826
3872
|
} catch (err) {
|
|
3827
|
-
process.stderr.write(
|
|
3873
|
+
process.stderr.write(chalk12.red(`Push failed: ${err instanceof Error ? err.message : String(err)}
|
|
3828
3874
|
`));
|
|
3829
3875
|
return false;
|
|
3830
3876
|
}
|
|
@@ -3891,21 +3937,21 @@ var ProgressTracker = class {
|
|
|
3891
3937
|
const parts = [];
|
|
3892
3938
|
for (const [lang, st] of this.langStatus) {
|
|
3893
3939
|
if (st.done) {
|
|
3894
|
-
parts.push(
|
|
3940
|
+
parts.push(chalk12.green(`${lang} \u2713`));
|
|
3895
3941
|
} else if (st.phase === "waiting") {
|
|
3896
|
-
parts.push(
|
|
3942
|
+
parts.push(chalk12.dim(`${lang} \xB7`));
|
|
3897
3943
|
} else if (st.phase === "discovering") {
|
|
3898
|
-
parts.push(
|
|
3944
|
+
parts.push(chalk12.yellow(`${lang} \u2026`));
|
|
3899
3945
|
} else if (st.phase === "scanning" && st.fileCount > 0) {
|
|
3900
3946
|
const pct = Math.round(st.fileIndex / st.fileCount * 100);
|
|
3901
|
-
parts.push(
|
|
3947
|
+
parts.push(chalk12.cyan(`${lang} ${st.fileIndex}/${st.fileCount} ${pct}%`));
|
|
3902
3948
|
} else if (st.phase === "extracting") {
|
|
3903
|
-
parts.push(
|
|
3949
|
+
parts.push(chalk12.cyan(`${lang} extracting`));
|
|
3904
3950
|
} else {
|
|
3905
|
-
parts.push(
|
|
3951
|
+
parts.push(chalk12.dim(`${lang} ${st.phase}`));
|
|
3906
3952
|
}
|
|
3907
3953
|
}
|
|
3908
|
-
const line = ` ${parts.join(" ")} ${
|
|
3954
|
+
const line = ` ${parts.join(" ")} ${chalk12.dim(`${this.totalFacts} facts`)}`;
|
|
3909
3955
|
const padding = Math.max(0, this.lastLineLen - line.length);
|
|
3910
3956
|
process.stderr.write(`\r${line}${" ".repeat(padding)}`);
|
|
3911
3957
|
this.lastLineLen = line.length;
|
|
@@ -3925,7 +3971,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3925
3971
|
if (urlArg) {
|
|
3926
3972
|
const cloneTimeoutMins = parseInt(opts.cloneTimeoutMins, 10);
|
|
3927
3973
|
if (isNaN(cloneTimeoutMins) || cloneTimeoutMins < 1) {
|
|
3928
|
-
process.stderr.write(
|
|
3974
|
+
process.stderr.write(chalk12.red("--clone-timeout-mins must be >= 1\n"));
|
|
3929
3975
|
process.exit(EXIT_USAGE_ERROR);
|
|
3930
3976
|
}
|
|
3931
3977
|
manifestRemoteUrl = redactRemoteUrl(urlArg);
|
|
@@ -3937,7 +3983,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3937
3983
|
});
|
|
3938
3984
|
rootDir = cloned.dir;
|
|
3939
3985
|
if (opts.keepClone) {
|
|
3940
|
-
process.stderr.write(
|
|
3986
|
+
process.stderr.write(chalk12.dim(`Clone retained at: ${cloned.dir}
|
|
3941
3987
|
`));
|
|
3942
3988
|
} else {
|
|
3943
3989
|
process.once("exit", () => {
|
|
@@ -3949,14 +3995,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3949
3995
|
}
|
|
3950
3996
|
} catch (err) {
|
|
3951
3997
|
const msg = err instanceof CloneError ? err.message : err instanceof Error ? err.message : String(err);
|
|
3952
|
-
process.stderr.write(
|
|
3998
|
+
process.stderr.write(chalk12.red(`Clone failed: ${msg}
|
|
3953
3999
|
`));
|
|
3954
4000
|
process.exit(EXIT_USAGE_ERROR);
|
|
3955
4001
|
}
|
|
3956
4002
|
} else {
|
|
3957
4003
|
rootDir = path12.resolve(targetPath);
|
|
3958
4004
|
if (!await pathExists(rootDir)) {
|
|
3959
|
-
process.stderr.write(
|
|
4005
|
+
process.stderr.write(chalk12.red(`Path does not exist: ${rootDir}
|
|
3960
4006
|
`));
|
|
3961
4007
|
process.exit(EXIT_USAGE_ERROR);
|
|
3962
4008
|
}
|
|
@@ -3964,48 +4010,48 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
3964
4010
|
try {
|
|
3965
4011
|
stat4 = await fs4.stat(rootDir);
|
|
3966
4012
|
} catch {
|
|
3967
|
-
process.stderr.write(
|
|
4013
|
+
process.stderr.write(chalk12.red(`Cannot read path: ${rootDir}
|
|
3968
4014
|
`));
|
|
3969
4015
|
process.exit(EXIT_USAGE_ERROR);
|
|
3970
4016
|
}
|
|
3971
4017
|
if (!stat4.isDirectory()) {
|
|
3972
|
-
process.stderr.write(
|
|
4018
|
+
process.stderr.write(chalk12.red(`Path must be a directory: ${rootDir}
|
|
3973
4019
|
`));
|
|
3974
4020
|
process.exit(EXIT_USAGE_ERROR);
|
|
3975
4021
|
}
|
|
3976
4022
|
}
|
|
3977
4023
|
const dsn = opts.dsn || process.env.VIBGRATE_DSN;
|
|
3978
4024
|
if (opts.push && !dsn) {
|
|
3979
|
-
process.stderr.write(
|
|
4025
|
+
process.stderr.write(chalk12.red("--push requires --dsn or VIBGRATE_DSN environment variable\n"));
|
|
3980
4026
|
process.exit(EXIT_USAGE_ERROR);
|
|
3981
4027
|
}
|
|
3982
4028
|
if (dsn && !parseDsn2(dsn)) {
|
|
3983
|
-
process.stderr.write(
|
|
3984
|
-
process.stderr.write(
|
|
4029
|
+
process.stderr.write(chalk12.red("Invalid DSN format.\n"));
|
|
4030
|
+
process.stderr.write(chalk12.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>\n"));
|
|
3985
4031
|
process.exit(EXIT_USAGE_ERROR);
|
|
3986
4032
|
}
|
|
3987
4033
|
const concurrency = opts.concurrency ? parseInt(opts.concurrency, 10) : os4.cpus().length;
|
|
3988
4034
|
if (isNaN(concurrency) || concurrency < 1) {
|
|
3989
|
-
process.stderr.write(
|
|
4035
|
+
process.stderr.write(chalk12.red("--concurrency must be >= 1\n"));
|
|
3990
4036
|
process.exit(EXIT_USAGE_ERROR);
|
|
3991
4037
|
}
|
|
3992
4038
|
const timeoutMins = parseInt(opts.timeoutMins, 10);
|
|
3993
4039
|
if (isNaN(timeoutMins) || timeoutMins < 1) {
|
|
3994
|
-
process.stderr.write(
|
|
4040
|
+
process.stderr.write(chalk12.red("--timeout-mins must be >= 1\n"));
|
|
3995
4041
|
process.exit(EXIT_USAGE_ERROR);
|
|
3996
4042
|
}
|
|
3997
4043
|
const timeoutMs = timeoutMins * 60 * 1e3;
|
|
3998
4044
|
if (opts.out) {
|
|
3999
4045
|
const outDir = path12.dirname(path12.resolve(opts.out));
|
|
4000
4046
|
if (!await pathExists(outDir)) {
|
|
4001
|
-
process.stderr.write(
|
|
4047
|
+
process.stderr.write(chalk12.red(`Output directory does not exist: ${outDir}
|
|
4002
4048
|
`));
|
|
4003
4049
|
process.exit(EXIT_USAGE_ERROR);
|
|
4004
4050
|
}
|
|
4005
4051
|
try {
|
|
4006
4052
|
const outStat = await fs4.stat(path12.resolve(opts.out));
|
|
4007
4053
|
if (outStat.isDirectory()) {
|
|
4008
|
-
process.stderr.write(
|
|
4054
|
+
process.stderr.write(chalk12.red(`--out cannot be a directory: ${opts.out}
|
|
4009
4055
|
`));
|
|
4010
4056
|
process.exit(EXIT_USAGE_ERROR);
|
|
4011
4057
|
}
|
|
@@ -4016,14 +4062,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4016
4062
|
if (opts.feedback) {
|
|
4017
4063
|
const feedbackPath = path12.resolve(opts.feedback);
|
|
4018
4064
|
if (!await pathExists(feedbackPath)) {
|
|
4019
|
-
process.stderr.write(
|
|
4065
|
+
process.stderr.write(chalk12.red(`Feedback file not found: ${feedbackPath}
|
|
4020
4066
|
`));
|
|
4021
4067
|
process.exit(EXIT_USAGE_ERROR);
|
|
4022
4068
|
}
|
|
4023
4069
|
try {
|
|
4024
4070
|
feedbackLines = await loadFeedback(feedbackPath);
|
|
4025
4071
|
} catch (err) {
|
|
4026
|
-
process.stderr.write(
|
|
4072
|
+
process.stderr.write(chalk12.red(`Invalid feedback file: ${err instanceof Error ? err.message : String(err)}
|
|
4027
4073
|
`));
|
|
4028
4074
|
process.exit(EXIT_USAGE_ERROR);
|
|
4029
4075
|
}
|
|
@@ -4033,9 +4079,9 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4033
4079
|
targetLanguages = opts.language.split(",").map((l) => normalizeLanguageToken(l)).filter(Boolean);
|
|
4034
4080
|
for (const lang of targetLanguages) {
|
|
4035
4081
|
if (!SUPPORTED_LANGUAGES.has(lang)) {
|
|
4036
|
-
process.stderr.write(
|
|
4082
|
+
process.stderr.write(chalk12.red(`Unknown language: "${lang}"
|
|
4037
4083
|
`));
|
|
4038
|
-
process.stderr.write(
|
|
4084
|
+
process.stderr.write(chalk12.dim(`Supported: ${[...SUPPORTED_LANGUAGES].sort().join(", ")}
|
|
4039
4085
|
`));
|
|
4040
4086
|
process.exit(EXIT_USAGE_ERROR);
|
|
4041
4087
|
}
|
|
@@ -4043,14 +4089,14 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4043
4089
|
} else {
|
|
4044
4090
|
const detected = await detectLanguages(rootDir, opts.includeTests ?? false);
|
|
4045
4091
|
if (detected.length === 0) {
|
|
4046
|
-
process.stderr.write(
|
|
4092
|
+
process.stderr.write(chalk12.yellow("No supported source files detected.\n"));
|
|
4047
4093
|
process.exit(EXIT_SUCCESS);
|
|
4048
4094
|
}
|
|
4049
4095
|
targetLanguages = detected.map((d) => d.language);
|
|
4050
4096
|
if (opts.verbose) {
|
|
4051
|
-
process.stderr.write(
|
|
4097
|
+
process.stderr.write(chalk12.dim("Detected languages:\n"));
|
|
4052
4098
|
for (const d of detected) {
|
|
4053
|
-
process.stderr.write(
|
|
4099
|
+
process.stderr.write(chalk12.dim(` ${d.language}: ${d.fileCount} files
|
|
4054
4100
|
`));
|
|
4055
4101
|
}
|
|
4056
4102
|
}
|
|
@@ -4058,20 +4104,20 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4058
4104
|
const runnableLanguages = targetLanguages.filter((l) => ALL_WORKER_LANGS.has(l));
|
|
4059
4105
|
const unknownWorkerLangs = targetLanguages.filter((l) => !ALL_WORKER_LANGS.has(l));
|
|
4060
4106
|
if (unknownWorkerLangs.length > 0 && opts.verbose) {
|
|
4061
|
-
process.stderr.write(
|
|
4107
|
+
process.stderr.write(chalk12.dim(
|
|
4062
4108
|
`No worker registered for: ${unknownWorkerLangs.join(", ")} \u2014 skipping.
|
|
4063
4109
|
`
|
|
4064
4110
|
));
|
|
4065
4111
|
}
|
|
4066
4112
|
if (runnableLanguages.length === 0) {
|
|
4067
|
-
process.stderr.write(
|
|
4113
|
+
process.stderr.write(chalk12.yellow("No languages with available HCS workers found.\n"));
|
|
4068
4114
|
process.exit(EXIT_SUCCESS);
|
|
4069
4115
|
}
|
|
4070
4116
|
const startTime = Date.now();
|
|
4071
4117
|
const globalDeadline = startTime + timeoutMs;
|
|
4072
4118
|
process.stderr.write(
|
|
4073
|
-
|
|
4074
|
-
`) +
|
|
4119
|
+
chalk12.bold(`Extracting HCS facts from ${rootDir}
|
|
4120
|
+
`) + chalk12.dim(`Languages: ${runnableLanguages.join(", ")} Concurrency: ${concurrency} Timeout: ${timeoutMins}m
|
|
4075
4121
|
`)
|
|
4076
4122
|
);
|
|
4077
4123
|
const progress = new ProgressTracker(runnableLanguages);
|
|
@@ -4118,7 +4164,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4118
4164
|
hasSchemaFailure = true;
|
|
4119
4165
|
allErrors.push(`[${language}] Schema validation: ${validation.error}`);
|
|
4120
4166
|
if (opts.verbose) {
|
|
4121
|
-
process.stderr.write(
|
|
4167
|
+
process.stderr.write(chalk12.red(`[${language}] Invalid fact: ${validation.error}
|
|
4122
4168
|
`));
|
|
4123
4169
|
}
|
|
4124
4170
|
}
|
|
@@ -4155,7 +4201,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4155
4201
|
allErrors.push("[behavioural] derived fact failed schema validation");
|
|
4156
4202
|
}
|
|
4157
4203
|
}
|
|
4158
|
-
process.stderr.write(
|
|
4204
|
+
process.stderr.write(chalk12.dim(
|
|
4159
4205
|
`Derived ${summary.assertions} behavioural assertion(s) \xB7 confidence ${summary.behaviouralConfidence} (${summary.assertedFacts}/${summary.surfaceFacts} surface facts)
|
|
4160
4206
|
`
|
|
4161
4207
|
));
|
|
@@ -4190,7 +4236,7 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4190
4236
|
if (opts.out) {
|
|
4191
4237
|
const outPath = path12.resolve(opts.out);
|
|
4192
4238
|
await fs4.writeFile(outPath, ndjsonOutput, "utf-8");
|
|
4193
|
-
process.stderr.write(
|
|
4239
|
+
process.stderr.write(chalk12.green(`\u2714 Wrote ${allFacts.length} facts to ${outPath}
|
|
4194
4240
|
`));
|
|
4195
4241
|
} else {
|
|
4196
4242
|
process.stdout.write(ndjsonOutput);
|
|
@@ -4202,38 +4248,38 @@ var extractCommand = new Command10("extract").description("Analyze source code a
|
|
|
4202
4248
|
}
|
|
4203
4249
|
}
|
|
4204
4250
|
if (opts.verbose) {
|
|
4205
|
-
process.stderr.write(
|
|
4206
|
-
process.stderr.write(
|
|
4251
|
+
process.stderr.write(chalk12.dim("\n\u2500\u2500 Summary \u2500\u2500\n"));
|
|
4252
|
+
process.stderr.write(chalk12.dim(` Facts emitted : ${allFacts.length}
|
|
4207
4253
|
`));
|
|
4208
|
-
process.stderr.write(
|
|
4254
|
+
process.stderr.write(chalk12.dim(` Languages : ${runnableLanguages.join(", ")}
|
|
4209
4255
|
`));
|
|
4210
|
-
process.stderr.write(
|
|
4256
|
+
process.stderr.write(chalk12.dim(` Elapsed : ${elapsed}s
|
|
4211
4257
|
`));
|
|
4212
4258
|
if (allErrors.length > 0) {
|
|
4213
|
-
process.stderr.write(
|
|
4259
|
+
process.stderr.write(chalk12.dim(` Errors : ${allErrors.length}
|
|
4214
4260
|
`));
|
|
4215
4261
|
for (const err of allErrors.slice(0, 10)) {
|
|
4216
|
-
process.stderr.write(
|
|
4262
|
+
process.stderr.write(chalk12.dim(` ${err}
|
|
4217
4263
|
`));
|
|
4218
4264
|
}
|
|
4219
4265
|
if (allErrors.length > 10) {
|
|
4220
|
-
process.stderr.write(
|
|
4266
|
+
process.stderr.write(chalk12.dim(` ... and ${allErrors.length - 10} more
|
|
4221
4267
|
`));
|
|
4222
4268
|
}
|
|
4223
4269
|
}
|
|
4224
|
-
process.stderr.write(
|
|
4270
|
+
process.stderr.write(chalk12.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n"));
|
|
4225
4271
|
}
|
|
4226
4272
|
if (hasTimeout) {
|
|
4227
|
-
process.stderr.write(
|
|
4273
|
+
process.stderr.write(chalk12.red(`Timeout exceeded (${timeoutMins} minutes)
|
|
4228
4274
|
`));
|
|
4229
4275
|
process.exit(EXIT_TIMEOUT);
|
|
4230
4276
|
}
|
|
4231
4277
|
if (hasSchemaFailure) {
|
|
4232
|
-
process.stderr.write(
|
|
4278
|
+
process.stderr.write(chalk12.red("Fact schema validation failures detected\n"));
|
|
4233
4279
|
process.exit(EXIT_SCHEMA_FAILURE);
|
|
4234
4280
|
}
|
|
4235
4281
|
if (hasParseFailure) {
|
|
4236
|
-
process.stderr.write(
|
|
4282
|
+
process.stderr.write(chalk12.red("Parsing failures detected\n"));
|
|
4237
4283
|
process.exit(EXIT_PARSE_FAILURE);
|
|
4238
4284
|
}
|
|
4239
4285
|
});
|
|
@@ -4243,7 +4289,7 @@ import * as path13 from "path";
|
|
|
4243
4289
|
import * as fs5 from "fs/promises";
|
|
4244
4290
|
import * as crypto4 from "crypto";
|
|
4245
4291
|
import { Command as Command11 } from "commander";
|
|
4246
|
-
import
|
|
4292
|
+
import chalk13 from "chalk";
|
|
4247
4293
|
var EXIT_INVALID_PATH = 2;
|
|
4248
4294
|
var EXIT_USAGE_ERROR2 = 5;
|
|
4249
4295
|
var VALID_FORMATS = /* @__PURE__ */ new Set(["ndjson", "json", "patch"]);
|
|
@@ -4476,7 +4522,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4476
4522
|
const genDir = path13.resolve(generatedPath);
|
|
4477
4523
|
for (const [label, dir] of [["original_path", origDir], ["generated_path", genDir]]) {
|
|
4478
4524
|
if (!await pathExists(dir)) {
|
|
4479
|
-
process.stderr.write(
|
|
4525
|
+
process.stderr.write(chalk13.red(`${label} does not exist: ${dir}
|
|
4480
4526
|
`));
|
|
4481
4527
|
process.exit(EXIT_INVALID_PATH);
|
|
4482
4528
|
}
|
|
@@ -4484,25 +4530,25 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4484
4530
|
try {
|
|
4485
4531
|
stat4 = await fs5.stat(dir);
|
|
4486
4532
|
} catch {
|
|
4487
|
-
process.stderr.write(
|
|
4533
|
+
process.stderr.write(chalk13.red(`Cannot read ${label}: ${dir}
|
|
4488
4534
|
`));
|
|
4489
4535
|
process.exit(EXIT_INVALID_PATH);
|
|
4490
4536
|
}
|
|
4491
4537
|
if (!stat4.isDirectory()) {
|
|
4492
|
-
process.stderr.write(
|
|
4538
|
+
process.stderr.write(chalk13.red(`${label} must be a directory: ${dir}
|
|
4493
4539
|
`));
|
|
4494
4540
|
process.exit(EXIT_INVALID_PATH);
|
|
4495
4541
|
}
|
|
4496
4542
|
}
|
|
4497
4543
|
const format = opts.format.toLowerCase();
|
|
4498
4544
|
if (!VALID_FORMATS.has(format)) {
|
|
4499
|
-
process.stderr.write(
|
|
4545
|
+
process.stderr.write(chalk13.red(`Invalid format: "${opts.format}". Allowed: ndjson, json, patch
|
|
4500
4546
|
`));
|
|
4501
4547
|
process.exit(EXIT_USAGE_ERROR2);
|
|
4502
4548
|
}
|
|
4503
4549
|
const contextLines = parseInt(opts.context, 10);
|
|
4504
4550
|
if (isNaN(contextLines) || contextLines < 0) {
|
|
4505
|
-
process.stderr.write(
|
|
4551
|
+
process.stderr.write(chalk13.red("--context must be >= 0\n"));
|
|
4506
4552
|
process.exit(EXIT_USAGE_ERROR2);
|
|
4507
4553
|
}
|
|
4508
4554
|
const includeGlobs = opts.include ? opts.include.split(",").map((g) => g.trim()).filter(Boolean) : void 0;
|
|
@@ -4514,9 +4560,9 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4514
4560
|
const origMap = new Map(origFiles.map((f) => [f.relPath, f]));
|
|
4515
4561
|
const genMap = new Map(genFiles.map((f) => [f.relPath, f]));
|
|
4516
4562
|
if (opts.verbose) {
|
|
4517
|
-
process.stderr.write(
|
|
4563
|
+
process.stderr.write(chalk13.dim(`Original: ${origFiles.length} files
|
|
4518
4564
|
`));
|
|
4519
|
-
process.stderr.write(
|
|
4565
|
+
process.stderr.write(chalk13.dim(`Generated: ${genFiles.length} files
|
|
4520
4566
|
`));
|
|
4521
4567
|
}
|
|
4522
4568
|
const deltas = [];
|
|
@@ -4550,7 +4596,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4550
4596
|
}
|
|
4551
4597
|
deltas.push(delta);
|
|
4552
4598
|
if (opts.verbose) {
|
|
4553
|
-
process.stderr.write(
|
|
4599
|
+
process.stderr.write(chalk13.dim(` modified: ${relPath} (+${diffResult.insertions} -${diffResult.deletions})
|
|
4554
4600
|
`));
|
|
4555
4601
|
}
|
|
4556
4602
|
}
|
|
@@ -4614,7 +4660,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4614
4660
|
}
|
|
4615
4661
|
deltas.push(delta);
|
|
4616
4662
|
if (opts.verbose) {
|
|
4617
|
-
process.stderr.write(
|
|
4663
|
+
process.stderr.write(chalk13.dim(
|
|
4618
4664
|
` renamed: ${pair.origPath} \u2192 ${pair.genPath} (${(pair.similarity * 100).toFixed(0)}% similar)
|
|
4619
4665
|
`
|
|
4620
4666
|
));
|
|
@@ -4639,7 +4685,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4639
4685
|
}
|
|
4640
4686
|
deltas.push(delta);
|
|
4641
4687
|
if (opts.verbose) {
|
|
4642
|
-
process.stderr.write(
|
|
4688
|
+
process.stderr.write(chalk13.dim(` removed: ${file.relPath}
|
|
4643
4689
|
`));
|
|
4644
4690
|
}
|
|
4645
4691
|
}
|
|
@@ -4661,7 +4707,7 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4661
4707
|
}
|
|
4662
4708
|
deltas.push(delta);
|
|
4663
4709
|
if (opts.verbose) {
|
|
4664
|
-
process.stderr.write(
|
|
4710
|
+
process.stderr.write(chalk13.dim(` added: ${file.relPath}
|
|
4665
4711
|
`));
|
|
4666
4712
|
}
|
|
4667
4713
|
}
|
|
@@ -4689,9 +4735,9 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4689
4735
|
const modified = deltas.filter((d) => d.changeKind === "modified").length;
|
|
4690
4736
|
const renamed = deltas.filter((d) => d.changeKind === "renamed").length;
|
|
4691
4737
|
process.stderr.write(
|
|
4692
|
-
|
|
4738
|
+
chalk13.green(`\u2714 `) + `${deltas.length} changes: ` + chalk13.green(`+${added} added`) + ", " + chalk13.red(`-${removed} removed`) + ", " + chalk13.yellow(`~${modified} modified`) + ", " + chalk13.blue(`\u2192${renamed} renamed`) + "\n"
|
|
4693
4739
|
);
|
|
4694
|
-
process.stderr.write(
|
|
4740
|
+
process.stderr.write(chalk13.dim(` Written to ${outPath}
|
|
4695
4741
|
`));
|
|
4696
4742
|
if (opts.verbose && opts.stats) {
|
|
4697
4743
|
let totalIns = 0;
|
|
@@ -4702,193 +4748,193 @@ var diffCommand = new Command11("diff").description("Compare two directory trees
|
|
|
4702
4748
|
totalDel += d.stats.deletions;
|
|
4703
4749
|
}
|
|
4704
4750
|
}
|
|
4705
|
-
process.stderr.write(
|
|
4751
|
+
process.stderr.write(chalk13.dim(` Total: +${totalIns} insertions, -${totalDel} deletions
|
|
4706
4752
|
`));
|
|
4707
4753
|
}
|
|
4708
4754
|
});
|
|
4709
4755
|
|
|
4710
4756
|
// src/commands/help.ts
|
|
4711
4757
|
import { Command as Command12 } from "commander";
|
|
4712
|
-
import
|
|
4758
|
+
import chalk14 from "chalk";
|
|
4713
4759
|
var HELP_URL = "https://vibgrate.com/help";
|
|
4714
4760
|
function printFooter() {
|
|
4715
4761
|
console.log("");
|
|
4716
|
-
console.log(
|
|
4762
|
+
console.log(chalk14.dim(`See ${HELP_URL} for more guidance`));
|
|
4717
4763
|
}
|
|
4718
4764
|
var detailedHelp = {
|
|
4719
4765
|
scan: () => {
|
|
4720
4766
|
console.log("");
|
|
4721
|
-
console.log(
|
|
4767
|
+
console.log(chalk14.bold.underline("vibgrate scan") + chalk14.dim(" \u2014 Scan a project for upgrade drift"));
|
|
4722
4768
|
console.log("");
|
|
4723
|
-
console.log(
|
|
4769
|
+
console.log(chalk14.bold("Usage:"));
|
|
4724
4770
|
console.log(" vibgrate scan [path] [options]");
|
|
4725
4771
|
console.log("");
|
|
4726
|
-
console.log(
|
|
4727
|
-
console.log(` ${
|
|
4772
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4773
|
+
console.log(` ${chalk14.cyan("[path]")} Path to scan (default: current directory)`);
|
|
4728
4774
|
console.log("");
|
|
4729
|
-
console.log(
|
|
4730
|
-
console.log(` ${
|
|
4731
|
-
console.log(` ${
|
|
4775
|
+
console.log(chalk14.bold("Output options:"));
|
|
4776
|
+
console.log(` ${chalk14.cyan("--format <format>")} Output format: ${chalk14.white("text")} | json | sarif | md (default: text)`);
|
|
4777
|
+
console.log(` ${chalk14.cyan("--out <file>")} Write output to a file instead of stdout`);
|
|
4732
4778
|
console.log("");
|
|
4733
|
-
console.log(
|
|
4734
|
-
console.log(` ${
|
|
4735
|
-
console.log(` ${
|
|
4736
|
-
console.log(` ${
|
|
4737
|
-
console.log(` ${
|
|
4779
|
+
console.log(chalk14.bold("Baseline & gating:"));
|
|
4780
|
+
console.log(` ${chalk14.cyan("--baseline <file>")} Compare results against a saved baseline`);
|
|
4781
|
+
console.log(` ${chalk14.cyan("--drift-budget <score>")} Fail if drift score exceeds this value (0\u2013100)`);
|
|
4782
|
+
console.log(` ${chalk14.cyan("--drift-worsening <percent>")} Fail if drift worsens by more than % since baseline`);
|
|
4783
|
+
console.log(` ${chalk14.cyan("--fail-on <level>")} Fail exit code on warn or error findings`);
|
|
4738
4784
|
console.log("");
|
|
4739
|
-
console.log(
|
|
4740
|
-
console.log(` ${
|
|
4741
|
-
console.log(` ${
|
|
4785
|
+
console.log(chalk14.bold("Performance:"));
|
|
4786
|
+
console.log(` ${chalk14.cyan("--concurrency <n>")} Max concurrent registry calls (default: 8)`);
|
|
4787
|
+
console.log(` ${chalk14.cyan("--changed-only")} Only scan files changed since last git commit`);
|
|
4742
4788
|
console.log("");
|
|
4743
|
-
console.log(
|
|
4744
|
-
console.log(` ${
|
|
4745
|
-
console.log(` ${
|
|
4746
|
-
console.log(` ${
|
|
4747
|
-
console.log(` ${
|
|
4789
|
+
console.log(chalk14.bold("Privacy & offline:"));
|
|
4790
|
+
console.log(` ${chalk14.cyan("--offline")} Run without any network calls; skip result upload`);
|
|
4791
|
+
console.log(` ${chalk14.cyan("--package-manifest <file>")} Use a local package-version manifest (JSON or ZIP) for offline mode`);
|
|
4792
|
+
console.log(` ${chalk14.cyan("--no-local-artifacts")} Do not write .vibgrate JSON artifacts to disk`);
|
|
4793
|
+
console.log(` ${chalk14.cyan("--max-privacy")} Strongest privacy mode: minimal scanners + no local artifacts`);
|
|
4748
4794
|
console.log("");
|
|
4749
|
-
console.log(
|
|
4750
|
-
console.log(` ${
|
|
4751
|
-
console.log(` ${
|
|
4795
|
+
console.log(chalk14.bold("Tooling:"));
|
|
4796
|
+
console.log(` ${chalk14.cyan("--install-tools")} Auto-install missing security scanners via Homebrew`);
|
|
4797
|
+
console.log(` ${chalk14.cyan("--ui-purpose")} Enable UI purpose evidence extraction (slower)`);
|
|
4752
4798
|
console.log("");
|
|
4753
|
-
console.log(
|
|
4754
|
-
console.log(` ${
|
|
4755
|
-
console.log(` ${
|
|
4756
|
-
console.log(` ${
|
|
4757
|
-
console.log(` ${
|
|
4799
|
+
console.log(chalk14.bold("Uploading results:"));
|
|
4800
|
+
console.log(` ${chalk14.cyan("--push")} Auto-push results to Vibgrate API after scan`);
|
|
4801
|
+
console.log(` ${chalk14.cyan("--dsn <dsn>")} DSN token for push (or set VIBGRATE_DSN env var)`);
|
|
4802
|
+
console.log(` ${chalk14.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
|
|
4803
|
+
console.log(` ${chalk14.cyan("--strict")} Fail if the upload to Vibgrate API fails`);
|
|
4758
4804
|
console.log("");
|
|
4759
|
-
console.log(
|
|
4760
|
-
console.log(` ${
|
|
4805
|
+
console.log(chalk14.bold("Examples:"));
|
|
4806
|
+
console.log(` ${chalk14.dim("# Scan the current directory and display a text report")}`);
|
|
4761
4807
|
console.log(" vibgrate scan .");
|
|
4762
4808
|
console.log("");
|
|
4763
|
-
console.log(` ${
|
|
4809
|
+
console.log(` ${chalk14.dim("# Scan, fail if drift score > 40, and write SARIF for GitHub Actions")}`);
|
|
4764
4810
|
console.log(" vibgrate scan . --drift-budget 40 --format sarif --out drift.sarif");
|
|
4765
4811
|
console.log("");
|
|
4766
|
-
console.log(` ${
|
|
4812
|
+
console.log(` ${chalk14.dim("# Scan and automatically upload results via a DSN")}`);
|
|
4767
4813
|
console.log(" vibgrate scan . --push --dsn $VIBGRATE_DSN");
|
|
4768
4814
|
console.log("");
|
|
4769
|
-
console.log(` ${
|
|
4815
|
+
console.log(` ${chalk14.dim("# Offline scan using a pre-downloaded package manifest")}`);
|
|
4770
4816
|
console.log(" vibgrate scan . --offline --package-manifest ./manifest.zip");
|
|
4771
4817
|
},
|
|
4772
4818
|
init: () => {
|
|
4773
4819
|
console.log("");
|
|
4774
|
-
console.log(
|
|
4820
|
+
console.log(chalk14.bold.underline("vibgrate init") + chalk14.dim(" \u2014 Initialise vibgrate in a project directory"));
|
|
4775
4821
|
console.log("");
|
|
4776
|
-
console.log(
|
|
4822
|
+
console.log(chalk14.bold("Usage:"));
|
|
4777
4823
|
console.log(" vibgrate init [path] [options]");
|
|
4778
4824
|
console.log("");
|
|
4779
|
-
console.log(
|
|
4780
|
-
console.log(` ${
|
|
4825
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4826
|
+
console.log(` ${chalk14.cyan("[path]")} Directory to initialise (default: current directory)`);
|
|
4781
4827
|
console.log("");
|
|
4782
|
-
console.log(
|
|
4783
|
-
console.log(` ${
|
|
4784
|
-
console.log(` ${
|
|
4828
|
+
console.log(chalk14.bold("Options:"));
|
|
4829
|
+
console.log(` ${chalk14.cyan("--baseline")} Create an initial drift baseline after init`);
|
|
4830
|
+
console.log(` ${chalk14.cyan("--yes")} Skip all confirmation prompts`);
|
|
4785
4831
|
console.log("");
|
|
4786
|
-
console.log(
|
|
4832
|
+
console.log(chalk14.bold("What it does:"));
|
|
4787
4833
|
console.log(" \u2022 Creates a .vibgrate/ directory");
|
|
4788
4834
|
console.log(" \u2022 Writes a vibgrate.config.ts starter config");
|
|
4789
4835
|
console.log(" \u2022 Optionally runs an initial baseline scan (--baseline)");
|
|
4790
4836
|
console.log("");
|
|
4791
|
-
console.log(
|
|
4837
|
+
console.log(chalk14.bold("Examples:"));
|
|
4792
4838
|
console.log(" vibgrate init");
|
|
4793
4839
|
console.log(" vibgrate init ./my-project --baseline");
|
|
4794
4840
|
},
|
|
4795
4841
|
baseline: () => {
|
|
4796
4842
|
console.log("");
|
|
4797
|
-
console.log(
|
|
4843
|
+
console.log(chalk14.bold.underline("vibgrate baseline") + chalk14.dim(" \u2014 Save a drift baseline snapshot"));
|
|
4798
4844
|
console.log("");
|
|
4799
|
-
console.log(
|
|
4845
|
+
console.log(chalk14.bold("Usage:"));
|
|
4800
4846
|
console.log(" vibgrate baseline [path]");
|
|
4801
4847
|
console.log("");
|
|
4802
|
-
console.log(
|
|
4803
|
-
console.log(` ${
|
|
4848
|
+
console.log(chalk14.bold("Arguments:"));
|
|
4849
|
+
console.log(` ${chalk14.cyan("[path]")} Path to baseline (default: current directory)`);
|
|
4804
4850
|
console.log("");
|
|
4805
|
-
console.log(
|
|
4851
|
+
console.log(chalk14.bold("What it does:"));
|
|
4806
4852
|
console.log(" Runs a full scan and saves the result as .vibgrate/baseline.json.");
|
|
4807
4853
|
console.log(" Future scans can compare against this file using --baseline.");
|
|
4808
4854
|
console.log("");
|
|
4809
|
-
console.log(
|
|
4855
|
+
console.log(chalk14.bold("Examples:"));
|
|
4810
4856
|
console.log(" vibgrate baseline .");
|
|
4811
4857
|
console.log(" vibgrate scan . --baseline .vibgrate/baseline.json --drift-worsening 10");
|
|
4812
4858
|
},
|
|
4813
4859
|
report: () => {
|
|
4814
4860
|
console.log("");
|
|
4815
|
-
console.log(
|
|
4861
|
+
console.log(chalk14.bold.underline("vibgrate report") + chalk14.dim(" \u2014 Generate a report from a saved scan artifact"));
|
|
4816
4862
|
console.log("");
|
|
4817
|
-
console.log(
|
|
4863
|
+
console.log(chalk14.bold("Usage:"));
|
|
4818
4864
|
console.log(" vibgrate report [options]");
|
|
4819
4865
|
console.log("");
|
|
4820
|
-
console.log(
|
|
4821
|
-
console.log(` ${
|
|
4822
|
-
console.log(` ${
|
|
4866
|
+
console.log(chalk14.bold("Options:"));
|
|
4867
|
+
console.log(` ${chalk14.cyan("--in <file>")} Input artifact file (default: .vibgrate/scan_result.json)`);
|
|
4868
|
+
console.log(` ${chalk14.cyan("--format <format>")} Output format: ${chalk14.white("text")} | md | json (default: text)`);
|
|
4823
4869
|
console.log("");
|
|
4824
|
-
console.log(
|
|
4870
|
+
console.log(chalk14.bold("Examples:"));
|
|
4825
4871
|
console.log(" vibgrate report");
|
|
4826
4872
|
console.log(" vibgrate report --format md > DRIFT-REPORT.md");
|
|
4827
4873
|
console.log(" vibgrate report --in ./ci/scan_result.json --format json");
|
|
4828
4874
|
},
|
|
4829
4875
|
sbom: () => {
|
|
4830
4876
|
console.log("");
|
|
4831
|
-
console.log(
|
|
4877
|
+
console.log(chalk14.bold.underline("vibgrate sbom") + chalk14.dim(" \u2014 Export a Software Bill of Materials from a scan artifact"));
|
|
4832
4878
|
console.log("");
|
|
4833
|
-
console.log(
|
|
4879
|
+
console.log(chalk14.bold("Usage:"));
|
|
4834
4880
|
console.log(" vibgrate sbom [options]");
|
|
4835
4881
|
console.log("");
|
|
4836
|
-
console.log(
|
|
4837
|
-
console.log(` ${
|
|
4838
|
-
console.log(` ${
|
|
4839
|
-
console.log(` ${
|
|
4882
|
+
console.log(chalk14.bold("Options:"));
|
|
4883
|
+
console.log(` ${chalk14.cyan("--in <file>")} Input artifact (default: .vibgrate/scan_result.json)`);
|
|
4884
|
+
console.log(` ${chalk14.cyan("--format <format>")} SBOM format: ${chalk14.white("cyclonedx")} | spdx (default: cyclonedx)`);
|
|
4885
|
+
console.log(` ${chalk14.cyan("--out <file>")} Write SBOM to file instead of stdout`);
|
|
4840
4886
|
console.log("");
|
|
4841
|
-
console.log(
|
|
4887
|
+
console.log(chalk14.bold("Examples:"));
|
|
4842
4888
|
console.log(" vibgrate sbom --format cyclonedx --out sbom.json");
|
|
4843
4889
|
console.log(" vibgrate sbom --format spdx --out sbom.spdx.json");
|
|
4844
4890
|
},
|
|
4845
4891
|
push: () => {
|
|
4846
4892
|
console.log("");
|
|
4847
|
-
console.log(
|
|
4893
|
+
console.log(chalk14.bold.underline("vibgrate push") + chalk14.dim(" \u2014 Upload a scan artifact to the Vibgrate API"));
|
|
4848
4894
|
console.log("");
|
|
4849
|
-
console.log(
|
|
4895
|
+
console.log(chalk14.bold("Usage:"));
|
|
4850
4896
|
console.log(" vibgrate push [options]");
|
|
4851
4897
|
console.log("");
|
|
4852
|
-
console.log(
|
|
4853
|
-
console.log(` ${
|
|
4854
|
-
console.log(` ${
|
|
4855
|
-
console.log(` ${
|
|
4856
|
-
console.log(` ${
|
|
4898
|
+
console.log(chalk14.bold("Options:"));
|
|
4899
|
+
console.log(` ${chalk14.cyan("--dsn <dsn>")} DSN token (or set VIBGRATE_DSN env var)`);
|
|
4900
|
+
console.log(` ${chalk14.cyan("--file <file>")} Artifact to upload (default: .vibgrate/scan_result.json)`);
|
|
4901
|
+
console.log(` ${chalk14.cyan("--region <region>")} Override data residency region: us | eu`);
|
|
4902
|
+
console.log(` ${chalk14.cyan("--strict")} Fail with non-zero exit code on upload error`);
|
|
4857
4903
|
console.log("");
|
|
4858
|
-
console.log(
|
|
4904
|
+
console.log(chalk14.bold("Examples:"));
|
|
4859
4905
|
console.log(" vibgrate push --dsn $VIBGRATE_DSN");
|
|
4860
4906
|
console.log(" vibgrate push --file ./ci/scan_result.json --strict");
|
|
4861
4907
|
},
|
|
4862
4908
|
dsn: () => {
|
|
4863
4909
|
console.log("");
|
|
4864
|
-
console.log(
|
|
4910
|
+
console.log(chalk14.bold.underline("vibgrate dsn") + chalk14.dim(" \u2014 Manage DSN tokens for API authentication"));
|
|
4865
4911
|
console.log("");
|
|
4866
|
-
console.log(
|
|
4867
|
-
console.log(` ${
|
|
4912
|
+
console.log(chalk14.bold("Subcommands:"));
|
|
4913
|
+
console.log(` ${chalk14.cyan("vibgrate dsn create")} Generate a new DSN token`);
|
|
4868
4914
|
console.log("");
|
|
4869
|
-
console.log(
|
|
4870
|
-
console.log(` ${
|
|
4871
|
-
console.log(` ${
|
|
4872
|
-
console.log(` ${
|
|
4873
|
-
console.log(` ${
|
|
4915
|
+
console.log(chalk14.bold("dsn create options:"));
|
|
4916
|
+
console.log(` ${chalk14.cyan("--workspace <id>")} Workspace ID or "new" to auto-generate ${chalk14.red("(required)")}`);
|
|
4917
|
+
console.log(` ${chalk14.cyan("--region <region>")} Data residency region: us | eu (default: us)`);
|
|
4918
|
+
console.log(` ${chalk14.cyan("--ingest <url>")} Override ingest API URL`);
|
|
4919
|
+
console.log(` ${chalk14.cyan("--write <path>")} Write the DSN to a file (add to .gitignore!)`);
|
|
4874
4920
|
console.log("");
|
|
4875
|
-
console.log(
|
|
4921
|
+
console.log(chalk14.bold("Examples:"));
|
|
4876
4922
|
console.log(" vibgrate dsn create --workspace new");
|
|
4877
4923
|
console.log(" vibgrate dsn create --workspace abc123");
|
|
4878
4924
|
console.log(" vibgrate dsn create --workspace new --region eu --write .vibgrate/.dsn");
|
|
4879
4925
|
},
|
|
4880
4926
|
update: () => {
|
|
4881
4927
|
console.log("");
|
|
4882
|
-
console.log(
|
|
4928
|
+
console.log(chalk14.bold.underline("vibgrate update") + chalk14.dim(" \u2014 Update the vibgrate CLI to the latest version"));
|
|
4883
4929
|
console.log("");
|
|
4884
|
-
console.log(
|
|
4930
|
+
console.log(chalk14.bold("Usage:"));
|
|
4885
4931
|
console.log(" vibgrate update [options]");
|
|
4886
4932
|
console.log("");
|
|
4887
|
-
console.log(
|
|
4888
|
-
console.log(` ${
|
|
4889
|
-
console.log(` ${
|
|
4933
|
+
console.log(chalk14.bold("Options:"));
|
|
4934
|
+
console.log(` ${chalk14.cyan("--check")} Check for a newer version without installing`);
|
|
4935
|
+
console.log(` ${chalk14.cyan("--pm <manager>")} Force a package manager: npm | pnpm | yarn | bun`);
|
|
4890
4936
|
console.log("");
|
|
4891
|
-
console.log(
|
|
4937
|
+
console.log(chalk14.bold("Examples:"));
|
|
4892
4938
|
console.log(" vibgrate update");
|
|
4893
4939
|
console.log(" vibgrate update --check");
|
|
4894
4940
|
console.log(" vibgrate update --pm pnpm");
|
|
@@ -4896,33 +4942,33 @@ var detailedHelp = {
|
|
|
4896
4942
|
};
|
|
4897
4943
|
function printSummaryHelp() {
|
|
4898
4944
|
console.log("");
|
|
4899
|
-
console.log(
|
|
4945
|
+
console.log(chalk14.bold("vibgrate") + chalk14.dim(" \u2014 Continuous Drift Intelligence"));
|
|
4900
4946
|
console.log("");
|
|
4901
|
-
console.log(
|
|
4947
|
+
console.log(chalk14.bold("Usage:"));
|
|
4902
4948
|
console.log(" vibgrate <command> [options]");
|
|
4903
4949
|
console.log(" vibgrate help [command] Show detailed help for a command");
|
|
4904
4950
|
console.log("");
|
|
4905
|
-
console.log(
|
|
4906
|
-
console.log(` ${
|
|
4907
|
-
console.log(` ${
|
|
4908
|
-
console.log(` ${
|
|
4951
|
+
console.log(chalk14.bold("Getting started:"));
|
|
4952
|
+
console.log(` ${chalk14.cyan("init")} Initialise vibgrate in a project (creates config & .vibgrate/ dir)`);
|
|
4953
|
+
console.log(` ${chalk14.cyan("login")} Sign the CLI into your workspace via the browser`);
|
|
4954
|
+
console.log(` ${chalk14.cyan("logout")} Clear stored login credentials`);
|
|
4909
4955
|
console.log("");
|
|
4910
|
-
console.log(
|
|
4911
|
-
console.log(` ${
|
|
4912
|
-
console.log(` ${
|
|
4956
|
+
console.log(chalk14.bold("Core scanning:"));
|
|
4957
|
+
console.log(` ${chalk14.cyan("scan")} Scan a project for upgrade drift and generate a report`);
|
|
4958
|
+
console.log(` ${chalk14.cyan("baseline")} Save a baseline snapshot to compare future scans against`);
|
|
4913
4959
|
console.log("");
|
|
4914
|
-
console.log(
|
|
4915
|
-
console.log(` ${
|
|
4916
|
-
console.log(` ${
|
|
4960
|
+
console.log(chalk14.bold("Reporting & export:"));
|
|
4961
|
+
console.log(` ${chalk14.cyan("report")} Re-generate a report from a previously saved scan artifact`);
|
|
4962
|
+
console.log(` ${chalk14.cyan("sbom")} Export a Software Bill of Materials (CycloneDX or SPDX)`);
|
|
4917
4963
|
console.log("");
|
|
4918
|
-
console.log(
|
|
4919
|
-
console.log(` ${
|
|
4920
|
-
console.log(` ${
|
|
4964
|
+
console.log(chalk14.bold("CI/CD integration:"));
|
|
4965
|
+
console.log(` ${chalk14.cyan("push")} Upload a scan artifact to the Vibgrate API`);
|
|
4966
|
+
console.log(` ${chalk14.cyan("dsn")} Create and manage DSN tokens for API authentication`);
|
|
4921
4967
|
console.log("");
|
|
4922
|
-
console.log(
|
|
4923
|
-
console.log(` ${
|
|
4968
|
+
console.log(chalk14.bold("Maintenance:"));
|
|
4969
|
+
console.log(` ${chalk14.cyan("update")} Update the vibgrate CLI to the latest version`);
|
|
4924
4970
|
console.log("");
|
|
4925
|
-
console.log(
|
|
4971
|
+
console.log(chalk14.dim("Run") + ` ${chalk14.cyan("vibgrate help <command>")} ` + chalk14.dim("for detailed options, e.g.") + ` ${chalk14.cyan("vibgrate help scan")}`);
|
|
4926
4972
|
}
|
|
4927
4973
|
var helpCommand = new Command12("help").description("Show help for vibgrate commands").argument("[command]", "Command to show detailed help for").helpOption(false).action((cmd) => {
|
|
4928
4974
|
const name = cmd?.toLowerCase().trim();
|
|
@@ -4930,8 +4976,8 @@ var helpCommand = new Command12("help").description("Show help for vibgrate comm
|
|
|
4930
4976
|
detailedHelp[name]();
|
|
4931
4977
|
} else if (name) {
|
|
4932
4978
|
console.log("");
|
|
4933
|
-
console.log(
|
|
4934
|
-
console.log(
|
|
4979
|
+
console.log(chalk14.red(`Unknown command: ${name}`));
|
|
4980
|
+
console.log(chalk14.dim(`Available commands: ${Object.keys(detailedHelp).join(", ")}`));
|
|
4935
4981
|
printSummaryHelp();
|
|
4936
4982
|
} else {
|
|
4937
4983
|
printSummaryHelp();
|
|
@@ -4959,8 +5005,8 @@ function notifyIfUpdateAvailable() {
|
|
|
4959
5005
|
void checkForUpdate().then((update) => {
|
|
4960
5006
|
if (!update?.updateAvailable) return;
|
|
4961
5007
|
console.error("");
|
|
4962
|
-
console.error(
|
|
4963
|
-
console.error(
|
|
5008
|
+
console.error(chalk15.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
|
|
5009
|
+
console.error(chalk15.dim(' Run "vibgrate update" to install the latest version.'));
|
|
4964
5010
|
console.error("");
|
|
4965
5011
|
}).catch(() => {
|
|
4966
5012
|
});
|