@vibgrate/cli 1.0.45 → 1.0.46

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.
@@ -51,6 +51,26 @@ function formatMarkdown(artifact) {
51
51
  }
52
52
  lines.push("");
53
53
  }
54
+ if (artifact.extended?.uiPurpose) {
55
+ const up = artifact.extended.uiPurpose;
56
+ lines.push("## Product Purpose Signals");
57
+ lines.push("");
58
+ lines.push(`- **Frameworks:** ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : "unknown"}`);
59
+ lines.push(`- **Evidence Items:** ${up.topEvidence.length}${up.capped ? ` (capped from ${up.evidenceCount})` : ""}`);
60
+ if (up.topEvidence.length > 0) {
61
+ lines.push("- **Top Evidence:**");
62
+ for (const item of up.topEvidence.slice(0, 10)) {
63
+ lines.push(` - [${item.kind}] ${item.value} (${item.file})`);
64
+ }
65
+ }
66
+ if (up.unknownSignals.length > 0) {
67
+ lines.push("- **Unknowns:**");
68
+ for (const u of up.unknownSignals.slice(0, 5)) {
69
+ lines.push(` - ${u}`);
70
+ }
71
+ }
72
+ lines.push("");
73
+ }
54
74
  if (artifact.findings.length > 0) {
55
75
  lines.push("## Findings");
56
76
  lines.push("");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-YFJC5JSQ.js";
3
+ } from "./chunk-HEILEAVO.js";
4
4
  import {
5
5
  writeJsonFile
6
6
  } from "./chunk-RNVZIZNL.js";
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  formatMarkdown
4
- } from "./chunk-GN3IWKSY.js";
4
+ } from "./chunk-PTMLMDZU.js";
5
5
  import {
6
6
  baselineCommand
7
- } from "./chunk-LO66M6OC.js";
7
+ } from "./chunk-SKROLJET.js";
8
8
  import {
9
9
  VERSION,
10
10
  dsnCommand,
@@ -12,16 +12,17 @@ import {
12
12
  pushCommand,
13
13
  scanCommand,
14
14
  writeDefaultConfig
15
- } from "./chunk-YFJC5JSQ.js";
15
+ } from "./chunk-HEILEAVO.js";
16
16
  import {
17
17
  ensureDir,
18
18
  pathExists,
19
- readJsonFile
19
+ readJsonFile,
20
+ writeTextFile
20
21
  } from "./chunk-RNVZIZNL.js";
21
22
 
22
23
  // src/cli.ts
23
- import { Command as Command4 } from "commander";
24
- import chalk4 from "chalk";
24
+ import { Command as Command5 } from "commander";
25
+ import chalk5 from "chalk";
25
26
 
26
27
  // src/commands/init.ts
27
28
  import * as path from "path";
@@ -40,7 +41,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
40
41
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
41
42
  }
42
43
  if (opts.baseline) {
43
- const { runBaseline } = await import("./baseline-FDWMBM2O.js");
44
+ const { runBaseline } = await import("./baseline-AWRL3ITR.js");
44
45
  await runBaseline(rootDir);
45
46
  }
46
47
  console.log("");
@@ -222,8 +223,186 @@ var updateCommand = new Command3("update").description("Update vibgrate to the l
222
223
  }
223
224
  });
224
225
 
226
+ // src/commands/sbom.ts
227
+ import * as path5 from "path";
228
+ import { randomUUID } from "crypto";
229
+ import { Command as Command4 } from "commander";
230
+ import chalk4 from "chalk";
231
+ function flattenDependencies(artifact) {
232
+ const rows = [];
233
+ for (const project of artifact.projects) {
234
+ for (const dep of project.dependencies) {
235
+ rows.push({
236
+ project: project.name,
237
+ package: dep.package,
238
+ version: dep.resolvedVersion ?? dep.currentSpec,
239
+ currentSpec: dep.currentSpec,
240
+ drift: dep.drift,
241
+ majorsBehind: dep.majorsBehind
242
+ });
243
+ }
244
+ }
245
+ return rows;
246
+ }
247
+ function toCycloneDx(artifact) {
248
+ const dependencies = flattenDependencies(artifact);
249
+ return {
250
+ bomFormat: "CycloneDX",
251
+ specVersion: "1.5",
252
+ serialNumber: `urn:uuid:${randomUUID()}`,
253
+ version: 1,
254
+ metadata: {
255
+ timestamp: artifact.timestamp,
256
+ tools: [
257
+ {
258
+ vendor: "Vibgrate",
259
+ name: "@vibgrate/cli",
260
+ version: artifact.vibgrateVersion
261
+ }
262
+ ],
263
+ component: {
264
+ type: "application",
265
+ name: artifact.rootPath
266
+ }
267
+ },
268
+ components: dependencies.map((dep) => ({
269
+ type: "library",
270
+ name: dep.package,
271
+ version: dep.version,
272
+ properties: [
273
+ { name: "vibgrate:project", value: dep.project },
274
+ { name: "vibgrate:currentSpec", value: dep.currentSpec },
275
+ { name: "vibgrate:drift", value: dep.drift },
276
+ { name: "vibgrate:majorsBehind", value: String(dep.majorsBehind ?? "unknown") }
277
+ ]
278
+ }))
279
+ };
280
+ }
281
+ function toSpdx(artifact) {
282
+ const dependencies = flattenDependencies(artifact);
283
+ return {
284
+ spdxVersion: "SPDX-2.3",
285
+ dataLicense: "CC0-1.0",
286
+ SPDXID: "SPDXRef-DOCUMENT",
287
+ name: `${artifact.rootPath}-sbom`,
288
+ documentNamespace: `https://vibgrate.com/spdx/${artifact.rootPath}/${randomUUID()}`,
289
+ creationInfo: {
290
+ created: artifact.timestamp,
291
+ creators: [`Tool: @vibgrate/cli-${artifact.vibgrateVersion}`]
292
+ },
293
+ packages: dependencies.map((dep, i) => ({
294
+ name: dep.package,
295
+ SPDXID: `SPDXRef-Package-${i + 1}`,
296
+ versionInfo: dep.version,
297
+ downloadLocation: "NOASSERTION",
298
+ filesAnalyzed: false,
299
+ externalRefs: [
300
+ {
301
+ referenceCategory: "PACKAGE-MANAGER",
302
+ referenceType: "purl",
303
+ referenceLocator: `pkg:npm/${encodeURIComponent(dep.package)}@${encodeURIComponent(dep.version)}`
304
+ }
305
+ ],
306
+ annotations: [
307
+ {
308
+ annotationType: "OTHER",
309
+ annotator: "Tool: @vibgrate/cli",
310
+ annotationDate: artifact.timestamp,
311
+ comment: `project=${dep.project}; drift=${dep.drift}; majorsBehind=${dep.majorsBehind ?? "unknown"}`
312
+ }
313
+ ]
314
+ }))
315
+ };
316
+ }
317
+ function projectDependencyMap(artifact) {
318
+ const map = /* @__PURE__ */ new Map();
319
+ for (const project of artifact.projects) {
320
+ for (const dep of project.dependencies) {
321
+ map.set(`${project.name}:${dep.package}`, dep);
322
+ }
323
+ }
324
+ return map;
325
+ }
326
+ function formatDeltaText(base, current) {
327
+ const baseMap = projectDependencyMap(base);
328
+ const currentMap = projectDependencyMap(current);
329
+ const added = [];
330
+ const removed = [];
331
+ const changed = [];
332
+ for (const [key, dep] of currentMap.entries()) {
333
+ if (!baseMap.has(key)) {
334
+ added.push(`${key} @ ${dep.resolvedVersion ?? dep.currentSpec}`);
335
+ continue;
336
+ }
337
+ const prev = baseMap.get(key);
338
+ const prevVersion = prev.resolvedVersion ?? prev.currentSpec;
339
+ const nowVersion = dep.resolvedVersion ?? dep.currentSpec;
340
+ if (prevVersion !== nowVersion || prev.majorsBehind !== dep.majorsBehind) {
341
+ changed.push(`${key} ${prevVersion} -> ${nowVersion} (majorsBehind ${prev.majorsBehind ?? "unknown"} -> ${dep.majorsBehind ?? "unknown"})`);
342
+ }
343
+ }
344
+ for (const [key, dep] of baseMap.entries()) {
345
+ if (!currentMap.has(key)) {
346
+ removed.push(`${key} @ ${dep.resolvedVersion ?? dep.currentSpec}`);
347
+ }
348
+ }
349
+ const lines = [
350
+ "Vibgrate SBOM Delta",
351
+ "===================",
352
+ `Baseline: ${base.timestamp}`,
353
+ `Current: ${current.timestamp}`,
354
+ `Drift score delta: ${(current.drift.score - base.drift.score).toFixed(2)} points`,
355
+ "",
356
+ `Added dependencies (${added.length})`,
357
+ ...added.map((d) => ` + ${d}`),
358
+ "",
359
+ `Removed dependencies (${removed.length})`,
360
+ ...removed.map((d) => ` - ${d}`),
361
+ "",
362
+ `Changed dependencies (${changed.length})`,
363
+ ...changed.map((d) => ` * ${d}`)
364
+ ];
365
+ return lines.join("\n");
366
+ }
367
+ async function readArtifactOrExit(filePath) {
368
+ const absolutePath = path5.resolve(filePath);
369
+ if (!await pathExists(absolutePath)) {
370
+ console.error(chalk4.red(`Artifact not found: ${absolutePath}`));
371
+ process.exit(1);
372
+ }
373
+ return readJsonFile(absolutePath);
374
+ }
375
+ var exportCommand = new Command4("export").description("Export scan artifact as SBOM").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--out <file>", "Output SBOM file").option("--format <format>", "SBOM format (cyclonedx|spdx)", "cyclonedx").action(async (opts) => {
376
+ const artifact = await readArtifactOrExit(opts.in);
377
+ const format = opts.format.toLowerCase();
378
+ if (format !== "cyclonedx" && format !== "spdx") {
379
+ console.error(chalk4.red("Invalid SBOM format. Use cyclonedx or spdx."));
380
+ process.exit(1);
381
+ }
382
+ const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
383
+ const body = JSON.stringify(sbom, null, 2);
384
+ if (opts.out) {
385
+ await writeTextFile(path5.resolve(opts.out), body);
386
+ console.log(chalk4.green("\u2714") + ` SBOM written to ${opts.out}`);
387
+ } else {
388
+ console.log(body);
389
+ }
390
+ });
391
+ var deltaCommand = new Command4("delta").description("Show SBOM delta between two scan artifacts").requiredOption("--from <file>", "Baseline scan artifact path").requiredOption("--to <file>", "Current scan artifact path").option("--out <file>", "Write report to file").action(async (opts) => {
392
+ const base = await readArtifactOrExit(opts.from);
393
+ const current = await readArtifactOrExit(opts.to);
394
+ const report = formatDeltaText(base, current);
395
+ if (opts.out) {
396
+ await writeTextFile(path5.resolve(opts.out), report);
397
+ console.log(chalk4.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
398
+ } else {
399
+ console.log(report);
400
+ }
401
+ });
402
+ var sbomCommand = new Command4("sbom").description("SBOM export and delta reports for dependency drift tracking").addCommand(exportCommand).addCommand(deltaCommand);
403
+
225
404
  // src/cli.ts
226
- var program = new Command4();
405
+ var program = new Command5();
227
406
  program.name("vibgrate").description("Continuous Drift Intelligence for Node & .NET").version(VERSION);
228
407
  program.addCommand(initCommand);
229
408
  program.addCommand(scanCommand);
@@ -232,12 +411,13 @@ program.addCommand(reportCommand);
232
411
  program.addCommand(dsnCommand);
233
412
  program.addCommand(pushCommand);
234
413
  program.addCommand(updateCommand);
414
+ program.addCommand(sbomCommand);
235
415
  program.parseAsync().then(async () => {
236
416
  const update = await checkForUpdate();
237
417
  if (update?.updateAvailable) {
238
418
  console.error("");
239
- console.error(chalk4.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
240
- console.error(chalk4.dim(' Run "vibgrate update" to install the latest version.'));
419
+ console.error(chalk5.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
420
+ console.error(chalk5.dim(' Run "vibgrate update" to install the latest version.'));
241
421
  console.error("");
242
422
  }
243
423
  }).catch((err) => {
package/dist/index.d.ts CHANGED
@@ -49,6 +49,14 @@ interface ProjectScan {
49
49
  projectReferences?: ProjectReference[];
50
50
  /** Number of source files in the project directory */
51
51
  fileCount?: number;
52
+ /** Project-level architecture layer diagram (Mermaid flowchart) */
53
+ architectureMermaid?: string;
54
+ /** Project-level relationship diagram (first-level parents + children) */
55
+ relationshipDiagram?: MermaidDiagram;
56
+ }
57
+ interface MermaidDiagram {
58
+ mermaid: string;
59
+ svg?: string;
52
60
  }
53
61
  interface DriftScore {
54
62
  score: number;
@@ -100,6 +108,8 @@ interface ScanArtifact {
100
108
  filesScanned?: number;
101
109
  /** Workspace tree summary (file & directory counts from discovery) */
102
110
  treeSummary?: TreeCount;
111
+ /** Workspace-level relationship diagram */
112
+ relationshipDiagram?: MermaidDiagram;
103
113
  }
104
114
  interface ScanOptions {
105
115
  out?: string;
@@ -117,6 +127,12 @@ interface ScanOptions {
117
127
  strict?: boolean;
118
128
  /** Auto-install missing security tools via Homebrew */
119
129
  installTools?: boolean;
130
+ /** Enable optional UI-purpose evidence extraction (slower, richer context for dashboard) */
131
+ uiPurpose?: boolean;
132
+ /** Fail the run if drift score is above this absolute budget */
133
+ driftBudget?: number;
134
+ /** Fail when drift worsens by more than this percentage vs baseline */
135
+ driftWorseningPercent?: number;
120
136
  }
121
137
  interface ScannerToggle {
122
138
  enabled: boolean;
@@ -140,6 +156,7 @@ interface ScannersConfig {
140
156
  architecture?: ScannerToggle;
141
157
  codeQuality?: ScannerToggle;
142
158
  owaspCategoryMapping?: OwaspScannerConfig;
159
+ uiPurpose?: ScannerToggle;
143
160
  }
144
161
  interface VibgrateConfig {
145
162
  include?: string[];
@@ -363,6 +380,20 @@ interface CodeQualityResult {
363
380
  circularDependencies: number;
364
381
  deadCodePercent: number;
365
382
  }
383
+ interface UiPurposeEvidenceItem {
384
+ kind: 'route' | 'nav' | 'title' | 'heading' | 'cta' | 'copy' | 'dependency' | 'feature_flag';
385
+ value: string;
386
+ file: string;
387
+ weight: number;
388
+ }
389
+ interface UiPurposeResult {
390
+ enabled: boolean;
391
+ detectedFrameworks: string[];
392
+ evidenceCount: number;
393
+ capped: boolean;
394
+ topEvidence: UiPurposeEvidenceItem[];
395
+ unknownSignals: string[];
396
+ }
366
397
  interface ExtendedScanResults {
367
398
  platformMatrix?: PlatformMatrixResult;
368
399
  dependencyRisk?: DependencyRiskResult;
@@ -378,6 +409,7 @@ interface ExtendedScanResults {
378
409
  architecture?: ArchitectureResult;
379
410
  codeQuality?: CodeQualityResult;
380
411
  owaspCategoryMapping?: OwaspCategoryMappingResult;
412
+ uiPurpose?: UiPurposeResult;
381
413
  }
382
414
  interface OwaspFinding {
383
415
  ruleId: string;
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  formatMarkdown
3
- } from "./chunk-GN3IWKSY.js";
3
+ } from "./chunk-PTMLMDZU.js";
4
4
  import {
5
5
  computeDriftScore,
6
6
  formatSarif,
7
7
  formatText,
8
8
  generateFindings,
9
9
  runScan
10
- } from "./chunk-YFJC5JSQ.js";
10
+ } from "./chunk-HEILEAVO.js";
11
11
  import "./chunk-RNVZIZNL.js";
12
12
  export {
13
13
  computeDriftScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.45",
3
+ "version": "1.0.46",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {