@vibecheckai/cli 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/package.json +9 -1
  2. package/bin/cli-hygiene.js +0 -241
  3. package/bin/guardrail.js +0 -834
  4. package/bin/runners/cli-utils.js +0 -1070
  5. package/bin/runners/context/ai-task-decomposer.js +0 -337
  6. package/bin/runners/context/analyzer.js +0 -462
  7. package/bin/runners/context/api-contracts.js +0 -427
  8. package/bin/runners/context/context-diff.js +0 -342
  9. package/bin/runners/context/context-pruner.js +0 -291
  10. package/bin/runners/context/dependency-graph.js +0 -414
  11. package/bin/runners/context/generators/claude.js +0 -107
  12. package/bin/runners/context/generators/codex.js +0 -108
  13. package/bin/runners/context/generators/copilot.js +0 -119
  14. package/bin/runners/context/generators/cursor.js +0 -514
  15. package/bin/runners/context/generators/mcp.js +0 -151
  16. package/bin/runners/context/generators/windsurf.js +0 -180
  17. package/bin/runners/context/git-context.js +0 -302
  18. package/bin/runners/context/index.js +0 -1042
  19. package/bin/runners/context/insights.js +0 -173
  20. package/bin/runners/context/mcp-server/generate-rules.js +0 -337
  21. package/bin/runners/context/mcp-server/index.js +0 -1176
  22. package/bin/runners/context/mcp-server/package.json +0 -24
  23. package/bin/runners/context/memory.js +0 -200
  24. package/bin/runners/context/monorepo.js +0 -215
  25. package/bin/runners/context/multi-repo-federation.js +0 -404
  26. package/bin/runners/context/patterns.js +0 -253
  27. package/bin/runners/context/proof-context.js +0 -972
  28. package/bin/runners/context/security-scanner.js +0 -303
  29. package/bin/runners/context/semantic-search.js +0 -350
  30. package/bin/runners/context/shared.js +0 -264
  31. package/bin/runners/context/team-conventions.js +0 -310
  32. package/bin/runners/lib/ai-bridge.js +0 -416
  33. package/bin/runners/lib/analysis-core.js +0 -271
  34. package/bin/runners/lib/analyzers.js +0 -541
  35. package/bin/runners/lib/audit-bridge.js +0 -391
  36. package/bin/runners/lib/auth-truth.js +0 -193
  37. package/bin/runners/lib/auth.js +0 -215
  38. package/bin/runners/lib/backup.js +0 -62
  39. package/bin/runners/lib/billing.js +0 -107
  40. package/bin/runners/lib/claims.js +0 -118
  41. package/bin/runners/lib/cli-ui.js +0 -540
  42. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  43. package/bin/runners/lib/compliance-bridge.js +0 -165
  44. package/bin/runners/lib/contracts/auth-contract.js +0 -194
  45. package/bin/runners/lib/contracts/env-contract.js +0 -178
  46. package/bin/runners/lib/contracts/external-contract.js +0 -198
  47. package/bin/runners/lib/contracts/guard.js +0 -168
  48. package/bin/runners/lib/contracts/index.js +0 -89
  49. package/bin/runners/lib/contracts/plan-validator.js +0 -311
  50. package/bin/runners/lib/contracts/route-contract.js +0 -192
  51. package/bin/runners/lib/detect.js +0 -89
  52. package/bin/runners/lib/doctor/autofix.js +0 -254
  53. package/bin/runners/lib/doctor/index.js +0 -37
  54. package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
  55. package/bin/runners/lib/doctor/modules/index.js +0 -46
  56. package/bin/runners/lib/doctor/modules/network.js +0 -250
  57. package/bin/runners/lib/doctor/modules/project.js +0 -312
  58. package/bin/runners/lib/doctor/modules/runtime.js +0 -224
  59. package/bin/runners/lib/doctor/modules/security.js +0 -348
  60. package/bin/runners/lib/doctor/modules/system.js +0 -213
  61. package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
  62. package/bin/runners/lib/doctor/reporter.js +0 -262
  63. package/bin/runners/lib/doctor/service.js +0 -262
  64. package/bin/runners/lib/doctor/types.js +0 -113
  65. package/bin/runners/lib/doctor/ui.js +0 -263
  66. package/bin/runners/lib/doctor-enhanced.js +0 -233
  67. package/bin/runners/lib/doctor-v2.js +0 -608
  68. package/bin/runners/lib/enforcement.js +0 -72
@@ -1,540 +0,0 @@
1
- /**
2
- * CLI UI Library — Beautiful Interactive Experience
3
- *
4
- * Features:
5
- * - Stunning ASCII art headers
6
- * - Beautiful bordered tables
7
- * - Interactive menus
8
- * - Progress bars and spinners
9
- * - Color gradients
10
- * - Box drawing characters
11
- */
12
-
13
- const readline = require("readline");
14
-
15
- // Extended ANSI color palette
16
- const colors = {
17
- reset: "\x1b[0m",
18
- bold: "\x1b[1m",
19
- dim: "\x1b[2m",
20
- italic: "\x1b[3m",
21
- underline: "\x1b[4m",
22
- blink: "\x1b[5m",
23
- inverse: "\x1b[7m",
24
- hidden: "\x1b[8m",
25
- strikethrough: "\x1b[9m",
26
-
27
- // Foreground colors
28
- black: "\x1b[30m",
29
- red: "\x1b[31m",
30
- green: "\x1b[32m",
31
- yellow: "\x1b[33m",
32
- blue: "\x1b[34m",
33
- magenta: "\x1b[35m",
34
- cyan: "\x1b[36m",
35
- white: "\x1b[37m",
36
-
37
- // Bright foreground
38
- brightBlack: "\x1b[90m",
39
- brightRed: "\x1b[91m",
40
- brightGreen: "\x1b[92m",
41
- brightYellow: "\x1b[93m",
42
- brightBlue: "\x1b[94m",
43
- brightMagenta: "\x1b[95m",
44
- brightCyan: "\x1b[96m",
45
- brightWhite: "\x1b[97m",
46
-
47
- // Background colors
48
- bgBlack: "\x1b[40m",
49
- bgRed: "\x1b[41m",
50
- bgGreen: "\x1b[42m",
51
- bgYellow: "\x1b[43m",
52
- bgBlue: "\x1b[44m",
53
- bgMagenta: "\x1b[45m",
54
- bgCyan: "\x1b[46m",
55
- bgWhite: "\x1b[47m",
56
-
57
- // Bright background
58
- bgBrightBlack: "\x1b[100m",
59
- bgBrightRed: "\x1b[101m",
60
- bgBrightGreen: "\x1b[102m",
61
- bgBrightYellow: "\x1b[103m",
62
- bgBrightBlue: "\x1b[104m",
63
- bgBrightMagenta: "\x1b[105m",
64
- bgBrightCyan: "\x1b[106m",
65
- bgBrightWhite: "\x1b[107m",
66
- };
67
-
68
- const c = colors;
69
-
70
- // Box drawing characters
71
- const box = {
72
- // Single line
73
- topLeft: "┌",
74
- topRight: "┐",
75
- bottomLeft: "└",
76
- bottomRight: "┘",
77
- horizontal: "─",
78
- vertical: "│",
79
- leftT: "├",
80
- rightT: "┤",
81
- topT: "┬",
82
- bottomT: "┴",
83
- cross: "┼",
84
-
85
- // Double line
86
- dTopLeft: "╔",
87
- dTopRight: "╗",
88
- dBottomLeft: "╚",
89
- dBottomRight: "╝",
90
- dHorizontal: "═",
91
- dVertical: "║",
92
- dLeftT: "╠",
93
- dRightT: "╣",
94
- dTopT: "╦",
95
- dBottomT: "╩",
96
- dCross: "╬",
97
-
98
- // Rounded
99
- rTopLeft: "╭",
100
- rTopRight: "╮",
101
- rBottomLeft: "╰",
102
- rBottomRight: "╯",
103
- };
104
-
105
- // Spinner frames
106
- const spinners = {
107
- dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
108
- line: ["-", "\\", "|", "/"],
109
- circle: ["◐", "◓", "◑", "◒"],
110
- arc: ["◜", "◠", "◝", "◞", "◡", "◟"],
111
- box: ["▖", "▘", "▝", "▗"],
112
- bounce: ["⠁", "⠂", "⠄", "⠂"],
113
- pulse: ["█", "▓", "▒", "░", "▒", "▓"],
114
- arrows: ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
115
- star: ["✶", "✷", "✸", "✹", "✺", "✹", "✸", "✷"],
116
- };
117
-
118
- // Progress bar characters
119
- const progressChars = {
120
- filled: "█",
121
- empty: "░",
122
- gradient: ["░", "▒", "▓", "█"],
123
- smooth: ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"],
124
- };
125
-
126
- /**
127
- * Create a beautiful header with ASCII art border
128
- */
129
- function createHeader(title, subtitle = "", options = {}) {
130
- const width = options.width || 70;
131
- const style = options.style || "double"; // single, double, rounded
132
- const color = options.color || c.cyan;
133
-
134
- const chars = style === "double" ? {
135
- tl: box.dTopLeft, tr: box.dTopRight, bl: box.dBottomLeft, br: box.dBottomRight,
136
- h: box.dHorizontal, v: box.dVertical
137
- } : style === "rounded" ? {
138
- tl: box.rTopLeft, tr: box.rTopRight, bl: box.rBottomLeft, br: box.rBottomRight,
139
- h: box.horizontal, v: box.vertical
140
- } : {
141
- tl: box.topLeft, tr: box.topRight, bl: box.bottomLeft, br: box.bottomRight,
142
- h: box.horizontal, v: box.vertical
143
- };
144
-
145
- const innerWidth = width - 2;
146
- const titlePad = Math.floor((innerWidth - title.length) / 2);
147
- const subtitlePad = Math.floor((innerWidth - subtitle.length) / 2);
148
-
149
- let output = "";
150
- output += `${color}${chars.tl}${chars.h.repeat(innerWidth)}${chars.tr}${c.reset}\n`;
151
- output += `${color}${chars.v}${c.reset}${" ".repeat(innerWidth)}${color}${chars.v}${c.reset}\n`;
152
- output += `${color}${chars.v}${c.reset}${" ".repeat(titlePad)}${c.bold}${c.brightWhite}${title}${c.reset}${" ".repeat(innerWidth - titlePad - title.length)}${color}${chars.v}${c.reset}\n`;
153
-
154
- if (subtitle) {
155
- output += `${color}${chars.v}${c.reset}${" ".repeat(subtitlePad)}${c.dim}${subtitle}${c.reset}${" ".repeat(innerWidth - subtitlePad - subtitle.length)}${color}${chars.v}${c.reset}\n`;
156
- }
157
-
158
- output += `${color}${chars.v}${c.reset}${" ".repeat(innerWidth)}${color}${chars.v}${c.reset}\n`;
159
- output += `${color}${chars.bl}${chars.h.repeat(innerWidth)}${chars.br}${c.reset}`;
160
-
161
- return output;
162
- }
163
-
164
- /**
165
- * Create a beautiful table
166
- */
167
- function createTable(headers, rows, options = {}) {
168
- const style = options.style || "rounded";
169
- const headerColor = options.headerColor || c.cyan;
170
- const borderColor = options.borderColor || c.dim;
171
-
172
- // Calculate column widths
173
- const colWidths = headers.map((h, i) => {
174
- const headerLen = stripAnsi(h).length;
175
- const maxRowLen = Math.max(...rows.map(r => stripAnsi(String(r[i] || "")).length));
176
- return Math.max(headerLen, maxRowLen) + 2;
177
- });
178
-
179
- const chars = style === "rounded" ? {
180
- tl: box.rTopLeft, tr: box.rTopRight, bl: box.rBottomLeft, br: box.rBottomRight,
181
- h: box.horizontal, v: box.vertical, lt: box.leftT, rt: box.rightT,
182
- tt: box.topT, bt: box.bottomT, cr: box.cross
183
- } : {
184
- tl: box.topLeft, tr: box.topRight, bl: box.bottomLeft, br: box.bottomRight,
185
- h: box.horizontal, v: box.vertical, lt: box.leftT, rt: box.rightT,
186
- tt: box.topT, bt: box.bottomT, cr: box.cross
187
- };
188
-
189
- let output = "";
190
-
191
- // Top border
192
- output += borderColor + chars.tl;
193
- output += colWidths.map(w => chars.h.repeat(w)).join(chars.tt);
194
- output += chars.tr + c.reset + "\n";
195
-
196
- // Header row
197
- output += borderColor + chars.v + c.reset;
198
- headers.forEach((h, i) => {
199
- const padded = padCenter(h, colWidths[i]);
200
- output += `${headerColor}${c.bold}${padded}${c.reset}${borderColor}${chars.v}${c.reset}`;
201
- });
202
- output += "\n";
203
-
204
- // Header separator
205
- output += borderColor + chars.lt;
206
- output += colWidths.map(w => chars.h.repeat(w)).join(chars.cr);
207
- output += chars.rt + c.reset + "\n";
208
-
209
- // Data rows
210
- rows.forEach((row, rowIdx) => {
211
- output += borderColor + chars.v + c.reset;
212
- row.forEach((cell, i) => {
213
- const cellStr = String(cell || "");
214
- const padded = padCenter(cellStr, colWidths[i]);
215
- output += `${padded}${borderColor}${chars.v}${c.reset}`;
216
- });
217
- output += "\n";
218
- });
219
-
220
- // Bottom border
221
- output += borderColor + chars.bl;
222
- output += colWidths.map(w => chars.h.repeat(w)).join(chars.bt);
223
- output += chars.br + c.reset;
224
-
225
- return output;
226
- }
227
-
228
- /**
229
- * Create a beautiful progress bar
230
- */
231
- function createProgressBar(percent, options = {}) {
232
- const width = options.width || 40;
233
- const showPercent = options.showPercent !== false;
234
- const showLabel = options.label || "";
235
- const color = options.color || c.green;
236
-
237
- const filled = Math.round((percent / 100) * width);
238
- const empty = width - filled;
239
-
240
- let bar = "";
241
-
242
- // Gradient effect
243
- for (let i = 0; i < filled; i++) {
244
- const charIdx = Math.min(3, Math.floor((i / width) * 4));
245
- bar += color + progressChars.gradient[charIdx];
246
- }
247
- bar += c.dim;
248
- for (let i = 0; i < empty; i++) {
249
- bar += progressChars.empty;
250
- }
251
- bar += c.reset;
252
-
253
- let output = "";
254
- if (showLabel) {
255
- output += `${showLabel} `;
256
- }
257
- output += `${c.dim}[${c.reset}${bar}${c.dim}]${c.reset}`;
258
- if (showPercent) {
259
- output += ` ${color}${percent.toFixed(0)}%${c.reset}`;
260
- }
261
-
262
- return output;
263
- }
264
-
265
- /**
266
- * Create a category score display with visual bars
267
- */
268
- function createScoreCard(categories, options = {}) {
269
- const width = options.width || 50;
270
- const barWidth = options.barWidth || 25;
271
-
272
- let output = "";
273
-
274
- for (const [name, score] of Object.entries(categories)) {
275
- const color = score >= 80 ? c.green : score >= 60 ? c.yellow : c.red;
276
- const icon = score >= 80 ? "✓" : score >= 60 ? "!" : "✗";
277
-
278
- const filled = Math.round((score / 100) * barWidth);
279
- const empty = barWidth - filled;
280
- const bar = color + progressChars.filled.repeat(filled) + c.dim + progressChars.empty.repeat(empty) + c.reset;
281
-
282
- const nameStr = name.padEnd(15);
283
- const scoreStr = `${score}%`.padStart(4);
284
-
285
- output += ` ${c.dim}${icon}${c.reset} ${nameStr} ${bar} ${color}${scoreStr}${c.reset}\n`;
286
- }
287
-
288
- return output;
289
- }
290
-
291
- /**
292
- * Create an interactive menu
293
- */
294
- async function createMenu(title, options, config = {}) {
295
- const menuColor = config.color || c.cyan;
296
-
297
- return new Promise((resolve) => {
298
- console.log("");
299
- console.log(createHeader(title, config.subtitle || "", { color: menuColor, style: "rounded" }));
300
- console.log("");
301
-
302
- options.forEach((opt, i) => {
303
- const num = `${i + 1}`.padStart(2);
304
- const icon = opt.icon || "›";
305
- console.log(` ${menuColor}${num}${c.reset} ${icon} ${c.bold}${opt.label}${c.reset}`);
306
- if (opt.description) {
307
- console.log(` ${c.dim}${opt.description}${c.reset}`);
308
- }
309
- });
310
-
311
- console.log("");
312
- console.log(` ${c.dim}${options.length + 1}. Exit${c.reset}`);
313
- console.log("");
314
-
315
- const rl = readline.createInterface({
316
- input: process.stdin,
317
- output: process.stdout,
318
- });
319
-
320
- rl.question(` ${menuColor}›${c.reset} Select an option: `, (answer) => {
321
- rl.close();
322
- const choice = parseInt(answer, 10);
323
-
324
- if (choice >= 1 && choice <= options.length) {
325
- resolve(options[choice - 1]);
326
- } else {
327
- resolve(null);
328
- }
329
- });
330
- });
331
- }
332
-
333
- /**
334
- * Create a spinner
335
- */
336
- function createSpinner(message, type = "dots") {
337
- const frames = spinners[type] || spinners.dots;
338
- let frameIdx = 0;
339
- let interval = null;
340
-
341
- const spinner = {
342
- start() {
343
- process.stdout.write(`\n ${frames[0]} ${message}`);
344
- interval = setInterval(() => {
345
- frameIdx = (frameIdx + 1) % frames.length;
346
- process.stdout.write(`\r ${c.cyan}${frames[frameIdx]}${c.reset} ${message}`);
347
- }, 80);
348
- return this;
349
- },
350
-
351
- update(newMessage) {
352
- message = newMessage;
353
- return this;
354
- },
355
-
356
- success(finalMessage) {
357
- if (interval) clearInterval(interval);
358
- process.stdout.write(`\r ${c.green}✓${c.reset} ${finalMessage || message}\n`);
359
- return this;
360
- },
361
-
362
- error(finalMessage) {
363
- if (interval) clearInterval(interval);
364
- process.stdout.write(`\r ${c.red}✗${c.reset} ${finalMessage || message}\n`);
365
- return this;
366
- },
367
-
368
- stop() {
369
- if (interval) clearInterval(interval);
370
- process.stdout.write("\r" + " ".repeat(message.length + 10) + "\r");
371
- return this;
372
- },
373
- };
374
-
375
- return spinner;
376
- }
377
-
378
- /**
379
- * Create a beautiful verdict display
380
- */
381
- function createVerdictDisplay(verdict, score, options = {}) {
382
- const width = options.width || 60;
383
-
384
- const configs = {
385
- SHIP: {
386
- color: c.green,
387
- bgColor: c.bgGreen,
388
- icon: "🚀",
389
- ascii: [
390
- " ███████╗██╗ ██╗██╗██████╗ ",
391
- " ██╔════╝██║ ██║██║██╔══██╗",
392
- " ███████╗███████║██║██████╔╝",
393
- " ╚════██║██╔══██║██║██╔═══╝ ",
394
- " ███████║██║ ██║██║██║ ",
395
- " ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ",
396
- ],
397
- message: "Ready to ship!",
398
- tagline: "All systems go. Deploy with confidence.",
399
- },
400
- WARN: {
401
- color: c.yellow,
402
- bgColor: c.bgYellow,
403
- icon: "⚠️",
404
- ascii: [
405
- " ██╗ ██╗ █████╗ ██████╗ ███╗ ██╗",
406
- " ██║ ██║██╔══██╗██╔══██╗████╗ ██║",
407
- " ██║ █╗ ██║███████║██████╔╝██╔██╗ ██║",
408
- " ██║███╗██║██╔══██║██╔══██╗██║╚██╗██║",
409
- " ╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║",
410
- " ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝",
411
- ],
412
- message: "Ship with caution",
413
- tagline: "Minor issues found. Review before deploy.",
414
- },
415
- BLOCK: {
416
- color: c.red,
417
- bgColor: c.bgRed,
418
- icon: "🚫",
419
- ascii: [
420
- " ██████╗ ██╗ ██████╗ ██████╗██╗ ██╗",
421
- " ██╔══██╗██║ ██╔═══██╗██╔════╝██║ ██╔╝",
422
- " ██████╔╝██║ ██║ ██║██║ █████╔╝ ",
423
- " ██╔══██╗██║ ██║ ██║██║ ██╔═██╗ ",
424
- " ██████╔╝███████╗╚██████╔╝╚██████╗██║ ██╗",
425
- " ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝",
426
- ],
427
- message: "Do not ship!",
428
- tagline: "Critical issues must be fixed first.",
429
- },
430
- };
431
-
432
- const config = configs[verdict] || configs.BLOCK;
433
-
434
- let output = "\n";
435
-
436
- // ASCII art verdict
437
- for (const line of config.ascii) {
438
- output += `${config.color}${line}${c.reset}\n`;
439
- }
440
-
441
- output += "\n";
442
-
443
- // Score display
444
- const scoreDisplay = `SCORE: ${score}`;
445
- output += ` ${config.color}${c.bold}${config.icon} ${scoreDisplay}${c.reset}\n`;
446
- output += ` ${c.dim}${config.tagline}${c.reset}\n`;
447
-
448
- return output;
449
- }
450
-
451
- /**
452
- * Create a badge display (the stunning one)
453
- */
454
- function createBadgeDisplay(projectId, score, verdict) {
455
- const config = {
456
- SHIP: { color: c.green, bg: c.bgGreen, icon: "✓", glow: "🌟" },
457
- WARN: { color: c.yellow, bg: c.bgYellow, icon: "!", glow: "⚡" },
458
- BLOCK: { color: c.red, bg: c.bgRed, icon: "✗", glow: "🔥" },
459
- };
460
-
461
- const cfg = config[verdict] || config.BLOCK;
462
-
463
- const badge = `
464
- ${cfg.color}${c.bold}
465
- ╔══════════════════════════════════════════════════════════════╗
466
- ║ ║
467
- ║ ${cfg.glow} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ${cfg.glow} ║
468
- ║ ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝ ║
469
- ║ ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ║
470
- ║ ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ║
471
- ║ ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗ ║
472
- ║ ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ║
473
- ║ ║
474
- ║ ┌────────────────────────────────────────────────────────┐ ║
475
- ║ │ │ ║
476
- ║ │ ${cfg.icon} ${verdict} │ SCORE: ${String(score).padStart(3)} │ ${projectId.substring(0, 20).padEnd(20)} │ ║
477
- ║ │ │ ║
478
- ║ └────────────────────────────────────────────────────────┘ ║
479
- ║ ║
480
- ║ ════════════════════════════════════════════ ║
481
- ║ VERIFIED BY VIBECHECK ║
482
- ║ ════════════════════════════════════════════ ║
483
- ║ ║
484
- ╚══════════════════════════════════════════════════════════════╝
485
- ${c.reset}`;
486
-
487
- return badge;
488
- }
489
-
490
- /**
491
- * Strip ANSI codes from string
492
- */
493
- function stripAnsi(str) {
494
- return str.replace(/\x1b\[[0-9;]*m/g, "");
495
- }
496
-
497
- /**
498
- * Pad string to center
499
- */
500
- function padCenter(str, width) {
501
- const len = stripAnsi(str).length;
502
- const pad = Math.max(0, width - len);
503
- const padLeft = Math.floor(pad / 2);
504
- const padRight = pad - padLeft;
505
- return " ".repeat(padLeft) + str + " ".repeat(padRight);
506
- }
507
-
508
- /**
509
- * Create a divider line
510
- */
511
- function divider(width = 60, char = "─", color = c.dim) {
512
- return `${color}${char.repeat(width)}${c.reset}`;
513
- }
514
-
515
- /**
516
- * Create a section header
517
- */
518
- function sectionHeader(title, icon = "›") {
519
- return `\n${c.cyan}${icon}${c.reset} ${c.bold}${title}${c.reset}\n${divider(40)}`;
520
- }
521
-
522
- module.exports = {
523
- colors,
524
- c,
525
- box,
526
- spinners,
527
- progressChars,
528
- createHeader,
529
- createTable,
530
- createProgressBar,
531
- createScoreCard,
532
- createMenu,
533
- createSpinner,
534
- createVerdictDisplay,
535
- createBadgeDisplay,
536
- stripAnsi,
537
- padCenter,
538
- divider,
539
- sectionHeader,
540
- };
File without changes
@@ -1,165 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- // Import from the built TypeScript package
5
- const {
6
- IaCSecurityScanner,
7
- PIIDetector,
8
- } = require("../../../../packages/compliance/dist/index.js");
9
-
10
- function walkDir(dir, fileList = []) {
11
- if (!fs.existsSync(dir)) return fileList;
12
- const files = fs.readdirSync(dir);
13
- for (const file of files) {
14
- if (["node_modules", ".git", "dist", "build", ".vibecheck"].includes(file))
15
- continue;
16
- const filePath = path.join(dir, file);
17
- const stat = fs.statSync(filePath);
18
- if (stat.isDirectory()) {
19
- walkDir(filePath, fileList);
20
- } else {
21
- fileList.push(filePath);
22
- }
23
- }
24
- return fileList;
25
- }
26
-
27
- async function runComplianceScan(projectPath) {
28
- const results = {
29
- iac: [],
30
- pii: [],
31
- summary: { score: 100, risk: "low" },
32
- };
33
-
34
- try {
35
- // Use the IaCSecurityScanner from TypeScript package
36
- const iacScanner = new IaCSecurityScanner();
37
- const projectId = "local-scan"; // Required parameter for the scanner
38
- const iacResult = await iacScanner.scan(projectPath, projectId);
39
-
40
- // Transform IaC findings to match expected format
41
- if (iacResult.findings) {
42
- results.iac = iacResult.findings.map((finding) => ({
43
- severity: finding.severity,
44
- title: finding.title,
45
- filePath: finding.filePath,
46
- recommendation: finding.recommendation,
47
- rule: finding.ruleId,
48
- category: finding.category,
49
- description: finding.description,
50
- }));
51
- }
52
-
53
- // Use the PIIDetector from TypeScript package
54
- const piiDetector = new PIIDetector();
55
- const piiResult = await piiDetector.detectPII(projectPath, projectId);
56
-
57
- // Transform PII findings to match expected format
58
- if (piiResult.findings) {
59
- results.pii = piiResult.findings.map((finding) => ({
60
- severity: finding.severity || "medium",
61
- category: finding.category,
62
- location: {
63
- file: finding.filePath,
64
- line: finding.line,
65
- column: finding.column,
66
- },
67
- value: finding.value,
68
- context: finding.context,
69
- }));
70
- }
71
-
72
- // Calculate score based on findings
73
- const criticalIssues = results.iac.filter(
74
- (f) => f.severity === "critical",
75
- ).length;
76
- const highIssues = results.iac.filter((f) => f.severity === "high").length;
77
- const mediumIssues = results.iac.filter(
78
- (f) => f.severity === "medium",
79
- ).length;
80
- const piiHighRisk = results.pii.filter(
81
- (f) => f.category === "SSN" || f.category === "Credit Card",
82
- ).length;
83
-
84
- // Score calculation: Critical -20, High -10, Medium -5, PII High Risk -10
85
- const deduction =
86
- criticalIssues * 20 +
87
- highIssues * 10 +
88
- mediumIssues * 5 +
89
- piiHighRisk * 10;
90
- results.summary.score = Math.max(0, 100 - deduction);
91
- results.summary.risk =
92
- results.summary.score < 50
93
- ? "high"
94
- : results.summary.score < 80
95
- ? "medium"
96
- : "low";
97
- } catch (e) {
98
- console.error("Compliance Bridge Error:", e.message);
99
- // Fallback to basic scanning if TypeScript classes fail
100
- console.warn("Falling back to basic compliance scanning...");
101
-
102
- try {
103
- const files = walkDir(projectPath);
104
-
105
- // Basic IaC checks
106
- const tfFiles = files.filter((f) => f.endsWith(".tf"));
107
- for (const f of tfFiles) {
108
- const content = fs.readFileSync(f, "utf8");
109
- if (
110
- content.includes('acl = "public-read"') ||
111
- content.includes('acl = "public-write"')
112
- ) {
113
- results.iac.push({
114
- severity: "high",
115
- title: "Public S3 bucket detected",
116
- filePath: path.relative(projectPath, f),
117
- recommendation: "Remove public ACL from S3 bucket",
118
- rule: "TF-S3-001",
119
- });
120
- }
121
- }
122
-
123
- // Basic PII checks
124
- const sourceFiles = files.filter((f) =>
125
- /\.(js|ts|tsx|jsx|json)$/.test(f),
126
- );
127
- const piiPatterns = [
128
- {
129
- regex: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/,
130
- category: "Credit Card",
131
- },
132
- { regex: /\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/, category: "SSN" },
133
- {
134
- regex: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
135
- category: "Email",
136
- },
137
- ];
138
-
139
- for (const f of sourceFiles) {
140
- const content = fs.readFileSync(f, "utf8");
141
- for (const pattern of piiPatterns) {
142
- if (pattern.regex.test(content)) {
143
- results.pii.push({
144
- severity: "medium",
145
- category: pattern.category,
146
- location: { file: path.relative(projectPath, f) },
147
- });
148
- }
149
- }
150
- }
151
-
152
- // Update score
153
- const issueCount = results.iac.length + results.pii.length;
154
- results.summary.score = Math.max(0, 100 - issueCount * 5);
155
- results.summary.risk =
156
- issueCount > 5 ? "high" : issueCount > 0 ? "medium" : "low";
157
- } catch (fallbackError) {
158
- console.error("Fallback scanning also failed:", fallbackError.message);
159
- }
160
- }
161
-
162
- return results;
163
- }
164
-
165
- module.exports = { runComplianceScan };