guardvibe 3.0.7 → 3.0.8

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.
@@ -9,13 +9,18 @@ import { runFullAudit, formatAuditResult } from "../tools/full-audit.js";
9
9
  export async function runAudit(args) {
10
10
  const { flags, positional } = parseArgs(args);
11
11
  const targetPath = resolve(positional[0] ?? ".");
12
- const format = validateFormat(flags);
12
+ const rawFormat = validateFormat(flags);
13
13
  const outputFile = getOutputPath(flags);
14
14
  const failOn = getStringFlag(flags, "fail-on") ?? "critical";
15
15
  const skipDeps = flags["skip-deps"] === true;
16
16
  const skipSecrets = flags["skip-secrets"] === true;
17
+ // Terminal format by default when outputting to TTY, unless --format is specified
18
+ const isTerminal = !outputFile && process.stdout.isTTY && !flags["format"];
19
+ const format = isTerminal ? "terminal" : rawFormat;
17
20
  const result = await runFullAudit(targetPath, { skipDeps, skipSecrets });
18
21
  const output = formatAuditResult(result, format);
22
+ // For shouldFail, always use JSON-parseable format
23
+ const failCheckOutput = formatAuditResult(result, "json");
19
24
  if (outputFile) {
20
25
  const dir = dirname(outputFile);
21
26
  if (!existsSync(dir)) {
@@ -29,6 +34,6 @@ export async function runAudit(args) {
29
34
  }
30
35
  // Print result hash to stderr for CI piping
31
36
  console.error(`result-hash: ${result.resultHash}`);
32
- if (shouldFail(output, failOn))
37
+ if (shouldFail(failCheckOutput, failOn))
33
38
  process.exit(1);
34
39
  }
@@ -76,6 +76,6 @@ export declare function runFullAudit(path: string, options?: {
76
76
  skipSecrets?: boolean;
77
77
  }): Promise<AuditResult>;
78
78
  /**
79
- * Format audit result as markdown or JSON.
79
+ * Format audit result as markdown, JSON, or terminal-friendly output.
80
80
  */
81
- export declare function formatAuditResult(result: AuditResult, format: "markdown" | "json"): string;
81
+ export declare function formatAuditResult(result: AuditResult, format: "markdown" | "json" | "terminal"): string;
@@ -293,12 +293,15 @@ export async function runFullAudit(path, options) {
293
293
  }
294
294
  // --- Formatter ---
295
295
  /**
296
- * Format audit result as markdown or JSON.
296
+ * Format audit result as markdown, JSON, or terminal-friendly output.
297
297
  */
298
298
  export function formatAuditResult(result, format) {
299
299
  if (format === "json") {
300
300
  return JSON.stringify(result);
301
301
  }
302
+ if (format === "terminal") {
303
+ return formatTerminal(result);
304
+ }
302
305
  const verdictLabel = {
303
306
  PASS: "PASS — Project verified secure",
304
307
  WARN: "WARN — High severity issues found",
@@ -363,3 +366,54 @@ export function formatAuditResult(result, format) {
363
366
  lines.push(`Result hash: \`${result.resultHash}\` (same code + same GuardVibe version = same hash)`);
364
367
  return lines.join("\n");
365
368
  }
369
+ // --- Terminal-friendly formatter ---
370
+ function formatTerminal(result) {
371
+ const R = "\x1b[31m"; // red
372
+ const G = "\x1b[32m"; // green
373
+ const Y = "\x1b[33m"; // yellow
374
+ const B = "\x1b[1m"; // bold
375
+ const D = "\x1b[2m"; // dim
376
+ const X = "\x1b[0m"; // reset
377
+ const verdictColor = result.verdict === "PASS" ? G : result.verdict === "WARN" ? Y : R;
378
+ const scoreBar = (() => {
379
+ const width = 20;
380
+ const filled = Math.round((result.score / 100) * width);
381
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
382
+ const color = result.score >= 75 ? G : result.score >= 50 ? Y : R;
383
+ return `${color}${bar}${X}`;
384
+ })();
385
+ const lines = [
386
+ ``,
387
+ ` ${B}GuardVibe Full Audit Report${X}`,
388
+ ``,
389
+ ` ${verdictColor}${B}${result.verdict}${X} ${verdictColor}${result.verdict === "PASS" ? "Project verified secure" : result.verdict === "WARN" ? "High severity issues found" : "Critical security issues detected"}${X}`,
390
+ ``,
391
+ ` Score ${scoreBar} ${B}${result.grade}${X} ${D}(${result.score}/100)${X}`,
392
+ ``,
393
+ ` ${B}Findings${X}`,
394
+ ` ${R}${B}${result.summary.critical}${X} critical ${Y}${B}${result.summary.high}${X} high ${D}${result.summary.medium} medium${X} ${D}(${result.summary.totalFindings} total)${X}`,
395
+ ``,
396
+ ` ${B}Sections${X}`,
397
+ ];
398
+ const sectionIcon = { ok: `${G}\u2714${X}`, error: `${R}\u2718${X}`, skipped: `${D}\u2500${X}` };
399
+ for (const s of result.sections) {
400
+ const icon = sectionIcon[s.status] ?? s.status;
401
+ const count = s.findings > 0 ? `${s.findings}` : `${D}0${X}`;
402
+ lines.push(` ${icon} ${s.name.padEnd(14)} ${count.padStart(4)} ${D}${s.details}${X}`);
403
+ }
404
+ lines.push(``);
405
+ lines.push(` ${B}Coverage${X}`);
406
+ lines.push(` ${result.coverage.filesScanned} files scanned ${D}${result.coverage.coveragePercent}% coverage ${result.coverage.rulesApplied} rules${X}`);
407
+ if (result.actionItems.length > 0) {
408
+ lines.push(``);
409
+ lines.push(` ${B}Action Items${X}`);
410
+ for (const item of result.actionItems) {
411
+ const color = item.includes("critical") ? R : item.includes("high") ? Y : D;
412
+ lines.push(` ${color}\u25B8${X} ${item}`);
413
+ }
414
+ }
415
+ lines.push(``);
416
+ lines.push(` ${D}Hash: ${result.resultHash} | ${result.timestamp.slice(0, 19)}${X}`);
417
+ lines.push(``);
418
+ return lines.join("\n");
419
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 334 rules, 34 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. Plus Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
6
6
  "type": "module",