@vibgrate/cli 1.0.50 → 1.0.51

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/DOCS.md CHANGED
@@ -162,12 +162,12 @@ Creates:
162
162
  The primary command. Scans your project for upgrade drift.
163
163
 
164
164
  ```bash
165
- vibgrate scan [path] [--format text|json|sarif] [--out <file>] [--fail-on warn|error] [--offline] [--package-manifest <file>] [--no-local-artifacts] [--max-privacy] [--baseline <file>] [--drift-budget <score>] [--drift-worsening <percent>] [--changed-only] [--concurrency <n>]
165
+ vibgrate scan [path] [--format text|json|sarif|md] [--out <file>] [--fail-on warn|error] [--offline] [--package-manifest <file>] [--no-local-artifacts] [--max-privacy] [--baseline <file>] [--drift-budget <score>] [--drift-worsening <percent>] [--changed-only] [--concurrency <n>]
166
166
  ```
167
167
 
168
168
  | Flag | Default | Description |
169
169
  |------|---------|-------------|
170
- | `--format` | `text` | Output format: `text`, `json`, or `sarif` |
170
+ | `--format` | `text` | Output format: `text`, `json`, `sarif`, or `md` |
171
171
  | `--out <file>` | — | Write output to a file |
172
172
  | `--fail-on <level>` | — | Exit with code 2 if findings at this level exist |
173
173
  | `--baseline <file>` | — | Compare against a previous baseline |
package/README.md CHANGED
@@ -176,7 +176,7 @@ When offline mode runs without a package manifest, package freshness is marked a
176
176
  ## Core commands
177
177
 
178
178
  ```bash
179
- vibgrate scan [path] [--format text|json|sarif] [--out <file>] [--fail-on warn|error] [--offline] [--package-manifest <file>] [--no-local-artifacts] [--max-privacy]
179
+ vibgrate scan [path] [--format text|json|sarif|md] [--out <file>] [--fail-on warn|error] [--offline] [--package-manifest <file>] [--no-local-artifacts] [--max-privacy]
180
180
  vibgrate baseline [path]
181
181
  vibgrate report [--in <artifact.json>] [--format md|text|json]
182
182
  vibgrate push [--dsn <dsn>] [--file <artifact.json>] [--strict]
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  baselineCommand,
3
3
  runBaseline
4
- } from "./chunk-PL6UM3Z3.js";
5
- import "./chunk-6RMNQ6MQ.js";
4
+ } from "./chunk-TCYJSLL2.js";
5
+ import "./chunk-TYAGUEXG.js";
6
6
  import "./chunk-RNVZIZNL.js";
7
7
  export {
8
8
  baselineCommand,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-6RMNQ6MQ.js";
3
+ } from "./chunk-TYAGUEXG.js";
4
4
  import {
5
5
  writeJsonFile
6
6
  } from "./chunk-RNVZIZNL.js";
@@ -965,6 +965,98 @@ function toSarifResult(finding) {
965
965
  };
966
966
  }
967
967
 
968
+ // src/formatters/markdown.ts
969
+ function formatMarkdown(artifact) {
970
+ const lines = [];
971
+ lines.push("# Vibgrate Drift Report");
972
+ lines.push("");
973
+ lines.push(`| Metric | Value |`);
974
+ lines.push(`|--------|-------|`);
975
+ lines.push(`| **Drift Score** | ${artifact.drift.score}/100 |`);
976
+ lines.push(`| **Risk Level** | ${artifact.drift.riskLevel.toUpperCase()} |`);
977
+ lines.push(`| **Projects** | ${artifact.projects.length} |`);
978
+ const scannedMeta = [artifact.timestamp];
979
+ if (artifact.durationMs !== void 0) scannedMeta.push(`${(artifact.durationMs / 1e3).toFixed(1)}s`);
980
+ if (artifact.filesScanned !== void 0) scannedMeta.push(`${artifact.filesScanned} files`);
981
+ if (artifact.treeSummary) scannedMeta.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files \xB7 ${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
982
+ lines.push(`| **Scanned** | ${scannedMeta.join(" \xB7 ")} |`);
983
+ if (artifact.vcs) {
984
+ lines.push(`| **VCS** | ${artifact.vcs.type} |`);
985
+ if (artifact.vcs.branch) lines.push(`| **Branch** | ${artifact.vcs.branch} |`);
986
+ if (artifact.vcs.sha) lines.push(`| **Commit** | \`${artifact.vcs.shortSha}\` |`);
987
+ }
988
+ lines.push("");
989
+ lines.push("## Score Breakdown");
990
+ lines.push("");
991
+ lines.push(`| Component | Score |`);
992
+ lines.push(`|-----------|-------|`);
993
+ lines.push(`| Runtime | ${artifact.drift.components.runtimeScore} |`);
994
+ lines.push(`| Frameworks | ${artifact.drift.components.frameworkScore} |`);
995
+ lines.push(`| Dependencies | ${artifact.drift.components.dependencyScore} |`);
996
+ lines.push(`| EOL Risk | ${artifact.drift.components.eolScore} |`);
997
+ lines.push("");
998
+ lines.push("## Projects");
999
+ lines.push("");
1000
+ for (const project of artifact.projects) {
1001
+ lines.push(`### ${project.name} (${project.type})`);
1002
+ lines.push("");
1003
+ if (project.runtime) {
1004
+ const lag = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? ` \u2014 ${project.runtimeMajorsBehind} major(s) behind` : " \u2014 current";
1005
+ lines.push(`- **Runtime:** ${project.runtime}${lag}`);
1006
+ }
1007
+ if (project.frameworks.length > 0) {
1008
+ lines.push("- **Frameworks:**");
1009
+ for (const fw of project.frameworks) {
1010
+ const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? "current" : `${fw.majorsBehind} behind` : "unknown";
1011
+ lines.push(` - ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
1012
+ }
1013
+ }
1014
+ const b = project.dependencyAgeBuckets;
1015
+ const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
1016
+ if (total > 0) {
1017
+ lines.push(`- **Dependencies:** ${b.current} current, ${b.oneBehind} 1-behind, ${b.twoPlusBehind} 2+ behind, ${b.unknown} unknown`);
1018
+ }
1019
+ lines.push("");
1020
+ }
1021
+ if (artifact.extended?.uiPurpose) {
1022
+ const up = artifact.extended.uiPurpose;
1023
+ lines.push("## Product Purpose Signals");
1024
+ lines.push("");
1025
+ lines.push(`- **Frameworks:** ${up.detectedFrameworks.length > 0 ? up.detectedFrameworks.join(", ") : "unknown"}`);
1026
+ lines.push(`- **Evidence Items:** ${up.topEvidence.length}${up.capped ? ` (capped from ${up.evidenceCount})` : ""}`);
1027
+ if (up.topEvidence.length > 0) {
1028
+ lines.push("- **Top Evidence:**");
1029
+ for (const item of up.topEvidence.slice(0, 10)) {
1030
+ lines.push(` - [${item.kind}] ${item.value} (${item.file})`);
1031
+ }
1032
+ }
1033
+ if (up.unknownSignals.length > 0) {
1034
+ lines.push("- **Unknowns:**");
1035
+ for (const u of up.unknownSignals.slice(0, 5)) {
1036
+ lines.push(` - ${u}`);
1037
+ }
1038
+ }
1039
+ lines.push("");
1040
+ }
1041
+ if (artifact.findings.length > 0) {
1042
+ lines.push("## Findings");
1043
+ lines.push("");
1044
+ lines.push(`| Level | Rule | Message | Location |`);
1045
+ lines.push(`|-------|------|---------|----------|`);
1046
+ for (const f of artifact.findings) {
1047
+ const emoji = f.level === "error" ? "\u{1F534}" : f.level === "warning" ? "\u{1F7E1}" : "\u{1F535}";
1048
+ lines.push(`| ${emoji} ${f.level} | ${f.ruleId} | ${f.message} | ${f.location} |`);
1049
+ }
1050
+ lines.push("");
1051
+ }
1052
+ if (artifact.delta !== void 0) {
1053
+ const dir = artifact.delta > 0 ? "\u{1F4C8}" : artifact.delta < 0 ? "\u{1F4C9}" : "\u27A1\uFE0F";
1054
+ lines.push(`## Drift Delta: ${dir} ${artifact.delta > 0 ? "+" : ""}${artifact.delta} vs baseline`);
1055
+ lines.push("");
1056
+ }
1057
+ return lines.join("\n");
1058
+ }
1059
+
968
1060
  // src/commands/dsn.ts
969
1061
  import * as crypto2 from "crypto";
970
1062
  import * as path from "path";
@@ -7880,6 +7972,12 @@ async function runScan(rootDir, opts) {
7880
7972
  } else {
7881
7973
  console.log(sarifStr);
7882
7974
  }
7975
+ } else if (opts.format === "md") {
7976
+ const markdown = formatMarkdown(artifact);
7977
+ console.log(markdown);
7978
+ if (opts.out) {
7979
+ await writeTextFile(path22.resolve(opts.out), markdown);
7980
+ }
7883
7981
  } else {
7884
7982
  const text = formatText(artifact);
7885
7983
  console.log(text);
@@ -7982,7 +8080,7 @@ function parseNonNegativeNumber(value, label) {
7982
8080
  }
7983
8081
  return parsed;
7984
8082
  }
7985
- var scanCommand = new Command3("scan").description("Scan a project for upgrade drift").argument("[path]", "Path to scan", ".").option("--out <file>", "Output file path").option("--format <format>", "Output format (text|json|sarif)", "text").option("--fail-on <level>", "Fail on warn or error").option("--baseline <file>", "Compare against baseline").option("--changed-only", "Only scan changed files").option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--install-tools", "Auto-install missing security scanners via Homebrew").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("--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) => {
8083
+ var scanCommand = new Command3("scan").description("Scan a project for upgrade drift").argument("[path]", "Path to scan", ".").option("--out <file>", "Output file path").option("--format <format>", "Output format (text|json|sarif|md)", "text").option("--fail-on <level>", "Fail on warn or error").option("--baseline <file>", "Compare against baseline").option("--changed-only", "Only scan changed files").option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--install-tools", "Auto-install missing security scanners via Homebrew").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("--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) => {
7986
8084
  const rootDir = path22.resolve(targetPath);
7987
8085
  if (!await pathExists(rootDir)) {
7988
8086
  console.error(chalk6.red(`Path does not exist: ${rootDir}`));
@@ -8057,6 +8155,7 @@ export {
8057
8155
  VERSION,
8058
8156
  formatText,
8059
8157
  formatSarif,
8158
+ formatMarkdown,
8060
8159
  dsnCommand,
8061
8160
  pushCommand,
8062
8161
  runScan,
package/dist/cli.js CHANGED
@@ -1,18 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- formatMarkdown
4
- } from "./chunk-PTMLMDZU.js";
5
2
  import {
6
3
  baselineCommand
7
- } from "./chunk-PL6UM3Z3.js";
4
+ } from "./chunk-TCYJSLL2.js";
8
5
  import {
9
6
  VERSION,
10
7
  dsnCommand,
8
+ formatMarkdown,
11
9
  formatText,
12
10
  pushCommand,
13
11
  scanCommand,
14
12
  writeDefaultConfig
15
- } from "./chunk-6RMNQ6MQ.js";
13
+ } from "./chunk-TYAGUEXG.js";
16
14
  import {
17
15
  ensureDir,
18
16
  pathExists,
@@ -41,7 +39,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
41
39
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
42
40
  }
43
41
  if (opts.baseline) {
44
- const { runBaseline } = await import("./baseline-UOF24CBK.js");
42
+ const { runBaseline } = await import("./baseline-FRBISJ66.js");
45
43
  await runBaseline(rootDir);
46
44
  }
47
45
  console.log("");
@@ -104,11 +102,16 @@ async function checkForUpdate() {
104
102
  }
105
103
  const controller = new AbortController();
106
104
  const timeout = setTimeout(() => controller.abort(), 5e3);
107
- const response = await fetch(REGISTRY_URL, {
108
- headers: { Accept: "application/json" },
109
- signal: controller.signal
110
- });
111
- clearTimeout(timeout);
105
+ timeout.unref?.();
106
+ let response;
107
+ try {
108
+ response = await fetch(REGISTRY_URL, {
109
+ headers: { Accept: "application/json" },
110
+ signal: controller.signal
111
+ });
112
+ } finally {
113
+ clearTimeout(timeout);
114
+ }
112
115
  if (!response.ok) return null;
113
116
  const data = await response.json();
114
117
  const latest = data.version;
@@ -127,11 +130,16 @@ async function fetchLatestVersion() {
127
130
  try {
128
131
  const controller = new AbortController();
129
132
  const timeout = setTimeout(() => controller.abort(), 1e4);
130
- const response = await fetch(REGISTRY_URL, {
131
- headers: { Accept: "application/json" },
132
- signal: controller.signal
133
- });
134
- clearTimeout(timeout);
133
+ timeout.unref?.();
134
+ let response;
135
+ try {
136
+ response = await fetch(REGISTRY_URL, {
137
+ headers: { Accept: "application/json" },
138
+ signal: controller.signal
139
+ });
140
+ } finally {
141
+ clearTimeout(timeout);
142
+ }
135
143
  if (!response.ok) return null;
136
144
  const data = await response.json();
137
145
  const latest = data.version;
@@ -412,14 +420,18 @@ program.addCommand(dsnCommand);
412
420
  program.addCommand(pushCommand);
413
421
  program.addCommand(updateCommand);
414
422
  program.addCommand(sbomCommand);
415
- program.parseAsync().then(async () => {
416
- const update = await checkForUpdate();
417
- if (update?.updateAvailable) {
423
+ function notifyIfUpdateAvailable() {
424
+ void checkForUpdate().then((update) => {
425
+ if (!update?.updateAvailable) return;
418
426
  console.error("");
419
427
  console.error(chalk5.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
420
428
  console.error(chalk5.dim(' Run "vibgrate update" to install the latest version.'));
421
429
  console.error("");
422
- }
430
+ }).catch(() => {
431
+ });
432
+ }
433
+ program.parseAsync().then(() => {
434
+ notifyIfUpdateAvailable();
423
435
  }).catch((err) => {
424
436
  console.error(err);
425
437
  process.exit(1);
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  type DepSection = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies';
2
2
  type RiskLevel = 'low' | 'moderate' | 'high';
3
3
  type ProjectType = 'node' | 'dotnet' | 'python' | 'java' | 'go' | 'rust' | 'php' | 'typescript' | 'ruby' | 'swift' | 'kotlin' | 'dart' | 'scala' | 'r' | 'objective-c' | 'elixir' | 'haskell' | 'lua' | 'perl' | 'julia' | 'shell' | 'clojure' | 'groovy' | 'c' | 'cpp' | 'cobol' | 'fortran' | 'visual-basic' | 'pascal' | 'ada' | 'assembly' | 'rpg';
4
- type OutputFormat = 'text' | 'json' | 'sarif';
4
+ type OutputFormat = 'text' | 'json' | 'sarif' | 'md';
5
5
  interface DependencyRow {
6
6
  package: string;
7
7
  section: DepSection;
package/dist/index.js CHANGED
@@ -1,13 +1,11 @@
1
- import {
2
- formatMarkdown
3
- } from "./chunk-PTMLMDZU.js";
4
1
  import {
5
2
  computeDriftScore,
3
+ formatMarkdown,
6
4
  formatSarif,
7
5
  formatText,
8
6
  generateFindings,
9
7
  runScan
10
- } from "./chunk-6RMNQ6MQ.js";
8
+ } from "./chunk-TYAGUEXG.js";
11
9
  import "./chunk-RNVZIZNL.js";
12
10
  export {
13
11
  computeDriftScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.50",
3
+ "version": "1.0.51",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,95 +0,0 @@
1
- // src/formatters/markdown.ts
2
- function formatMarkdown(artifact) {
3
- const lines = [];
4
- lines.push("# Vibgrate Drift Report");
5
- lines.push("");
6
- lines.push(`| Metric | Value |`);
7
- lines.push(`|--------|-------|`);
8
- lines.push(`| **Drift Score** | ${artifact.drift.score}/100 |`);
9
- lines.push(`| **Risk Level** | ${artifact.drift.riskLevel.toUpperCase()} |`);
10
- lines.push(`| **Projects** | ${artifact.projects.length} |`);
11
- const scannedMeta = [artifact.timestamp];
12
- if (artifact.durationMs !== void 0) scannedMeta.push(`${(artifact.durationMs / 1e3).toFixed(1)}s`);
13
- if (artifact.filesScanned !== void 0) scannedMeta.push(`${artifact.filesScanned} files`);
14
- if (artifact.treeSummary) scannedMeta.push(`${artifact.treeSummary.totalFiles.toLocaleString()} workspace files \xB7 ${artifact.treeSummary.totalDirs.toLocaleString()} dirs`);
15
- lines.push(`| **Scanned** | ${scannedMeta.join(" \xB7 ")} |`);
16
- if (artifact.vcs) {
17
- lines.push(`| **VCS** | ${artifact.vcs.type} |`);
18
- if (artifact.vcs.branch) lines.push(`| **Branch** | ${artifact.vcs.branch} |`);
19
- if (artifact.vcs.sha) lines.push(`| **Commit** | \`${artifact.vcs.shortSha}\` |`);
20
- }
21
- lines.push("");
22
- lines.push("## Score Breakdown");
23
- lines.push("");
24
- lines.push(`| Component | Score |`);
25
- lines.push(`|-----------|-------|`);
26
- lines.push(`| Runtime | ${artifact.drift.components.runtimeScore} |`);
27
- lines.push(`| Frameworks | ${artifact.drift.components.frameworkScore} |`);
28
- lines.push(`| Dependencies | ${artifact.drift.components.dependencyScore} |`);
29
- lines.push(`| EOL Risk | ${artifact.drift.components.eolScore} |`);
30
- lines.push("");
31
- lines.push("## Projects");
32
- lines.push("");
33
- for (const project of artifact.projects) {
34
- lines.push(`### ${project.name} (${project.type})`);
35
- lines.push("");
36
- if (project.runtime) {
37
- const lag = project.runtimeMajorsBehind !== void 0 && project.runtimeMajorsBehind > 0 ? ` \u2014 ${project.runtimeMajorsBehind} major(s) behind` : " \u2014 current";
38
- lines.push(`- **Runtime:** ${project.runtime}${lag}`);
39
- }
40
- if (project.frameworks.length > 0) {
41
- lines.push("- **Frameworks:**");
42
- for (const fw of project.frameworks) {
43
- const lag = fw.majorsBehind !== null ? fw.majorsBehind === 0 ? "current" : `${fw.majorsBehind} behind` : "unknown";
44
- lines.push(` - ${fw.name}: ${fw.currentVersion ?? "?"} \u2192 ${fw.latestVersion ?? "?"} (${lag})`);
45
- }
46
- }
47
- const b = project.dependencyAgeBuckets;
48
- const total = b.current + b.oneBehind + b.twoPlusBehind + b.unknown;
49
- if (total > 0) {
50
- lines.push(`- **Dependencies:** ${b.current} current, ${b.oneBehind} 1-behind, ${b.twoPlusBehind} 2+ behind, ${b.unknown} unknown`);
51
- }
52
- lines.push("");
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
- }
74
- if (artifact.findings.length > 0) {
75
- lines.push("## Findings");
76
- lines.push("");
77
- lines.push(`| Level | Rule | Message | Location |`);
78
- lines.push(`|-------|------|---------|----------|`);
79
- for (const f of artifact.findings) {
80
- const emoji = f.level === "error" ? "\u{1F534}" : f.level === "warning" ? "\u{1F7E1}" : "\u{1F535}";
81
- lines.push(`| ${emoji} ${f.level} | ${f.ruleId} | ${f.message} | ${f.location} |`);
82
- }
83
- lines.push("");
84
- }
85
- if (artifact.delta !== void 0) {
86
- const dir = artifact.delta > 0 ? "\u{1F4C8}" : artifact.delta < 0 ? "\u{1F4C9}" : "\u27A1\uFE0F";
87
- lines.push(`## Drift Delta: ${dir} ${artifact.delta > 0 ? "+" : ""}${artifact.delta} vs baseline`);
88
- lines.push("");
89
- }
90
- return lines.join("\n");
91
- }
92
-
93
- export {
94
- formatMarkdown
95
- };