@kevinrabun/judges 3.61.0 → 3.63.0

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 (74) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +112 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/batch-review.d.ts +5 -0
  6. package/dist/commands/batch-review.d.ts.map +1 -0
  7. package/dist/commands/batch-review.js +181 -0
  8. package/dist/commands/batch-review.js.map +1 -0
  9. package/dist/commands/custom-rule.d.ts +5 -0
  10. package/dist/commands/custom-rule.d.ts.map +1 -0
  11. package/dist/commands/custom-rule.js +211 -0
  12. package/dist/commands/custom-rule.js.map +1 -0
  13. package/dist/commands/diff-review.d.ts +5 -0
  14. package/dist/commands/diff-review.d.ts.map +1 -0
  15. package/dist/commands/diff-review.js +191 -0
  16. package/dist/commands/diff-review.js.map +1 -0
  17. package/dist/commands/finding-group.d.ts +16 -0
  18. package/dist/commands/finding-group.d.ts.map +1 -0
  19. package/dist/commands/finding-group.js +165 -0
  20. package/dist/commands/finding-group.js.map +1 -0
  21. package/dist/commands/focus-area.d.ts +6 -0
  22. package/dist/commands/focus-area.d.ts.map +1 -0
  23. package/dist/commands/focus-area.js +193 -0
  24. package/dist/commands/focus-area.js.map +1 -0
  25. package/dist/commands/ignore-list.d.ts +19 -0
  26. package/dist/commands/ignore-list.d.ts.map +1 -0
  27. package/dist/commands/ignore-list.js +166 -0
  28. package/dist/commands/ignore-list.js.map +1 -0
  29. package/dist/commands/incremental-review.d.ts +5 -0
  30. package/dist/commands/incremental-review.d.ts.map +1 -0
  31. package/dist/commands/incremental-review.js +240 -0
  32. package/dist/commands/incremental-review.js.map +1 -0
  33. package/dist/commands/review-cache.d.ts +23 -0
  34. package/dist/commands/review-cache.d.ts.map +1 -0
  35. package/dist/commands/review-cache.js +135 -0
  36. package/dist/commands/review-cache.js.map +1 -0
  37. package/dist/commands/review-compare.d.ts +5 -0
  38. package/dist/commands/review-compare.d.ts.map +1 -0
  39. package/dist/commands/review-compare.js +201 -0
  40. package/dist/commands/review-compare.js.map +1 -0
  41. package/dist/commands/review-explain.d.ts +6 -0
  42. package/dist/commands/review-explain.d.ts.map +1 -0
  43. package/dist/commands/review-explain.js +195 -0
  44. package/dist/commands/review-explain.js.map +1 -0
  45. package/dist/commands/review-gate.d.ts +5 -0
  46. package/dist/commands/review-gate.d.ts.map +1 -0
  47. package/dist/commands/review-gate.js +213 -0
  48. package/dist/commands/review-gate.js.map +1 -0
  49. package/dist/commands/review-log.d.ts +23 -0
  50. package/dist/commands/review-log.d.ts.map +1 -0
  51. package/dist/commands/review-log.js +165 -0
  52. package/dist/commands/review-log.js.map +1 -0
  53. package/dist/commands/review-summary.d.ts +5 -0
  54. package/dist/commands/review-summary.d.ts.map +1 -0
  55. package/dist/commands/review-summary.js +175 -0
  56. package/dist/commands/review-summary.js.map +1 -0
  57. package/dist/commands/rule-test.d.ts +5 -0
  58. package/dist/commands/rule-test.d.ts.map +1 -0
  59. package/dist/commands/rule-test.js +216 -0
  60. package/dist/commands/rule-test.js.map +1 -0
  61. package/dist/commands/severity-tune.d.ts +5 -0
  62. package/dist/commands/severity-tune.d.ts.map +1 -0
  63. package/dist/commands/severity-tune.js +209 -0
  64. package/dist/commands/severity-tune.js.map +1 -0
  65. package/dist/commands/team-config.d.ts +5 -0
  66. package/dist/commands/team-config.d.ts.map +1 -0
  67. package/dist/commands/team-config.js +235 -0
  68. package/dist/commands/team-config.js.map +1 -0
  69. package/dist/commands/trend-report.d.ts +5 -0
  70. package/dist/commands/trend-report.d.ts.map +1 -0
  71. package/dist/commands/trend-report.js +149 -0
  72. package/dist/commands/trend-report.js.map +1 -0
  73. package/package.json +1 -1
  74. package/server.json +2 -2
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Review-summary — Generate PR-ready review summaries with key metrics.
3
+ */
4
+ export declare function runReviewSummary(argv: string[]): void;
5
+ //# sourceMappingURL=review-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-summary.d.ts","sourceRoot":"","sources":["../../src/commands/review-summary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiIH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqFrD"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Review-summary — Generate PR-ready review summaries with key metrics.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ // ─── Metrics extraction ─────────────────────────────────────────────────────
6
+ function extractMetrics(verdict) {
7
+ const findings = verdict.findings || [];
8
+ const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
9
+ const ruleCounts = new Map();
10
+ for (const f of findings) {
11
+ const sev = f.severity || "low";
12
+ severityCounts[sev] = (severityCounts[sev] || 0) + 1;
13
+ const rid = f.ruleId || "unknown";
14
+ ruleCounts.set(rid, (ruleCounts.get(rid) || 0) + 1);
15
+ }
16
+ const topRules = [...ruleCounts.entries()]
17
+ .sort((a, b) => b[1] - a[1])
18
+ .slice(0, 5)
19
+ .map(([ruleId, count]) => ({ ruleId, count }));
20
+ return {
21
+ totalFindings: findings.length,
22
+ criticalCount: severityCounts["critical"],
23
+ highCount: severityCounts["high"],
24
+ mediumCount: severityCounts["medium"],
25
+ lowCount: severityCounts["low"],
26
+ judgeCount: verdict.evaluations?.length || 0,
27
+ overallScore: verdict.overallScore ?? 0,
28
+ overallVerdict: verdict.overallVerdict || "unknown",
29
+ topRules,
30
+ };
31
+ }
32
+ // ─── Formatters ─────────────────────────────────────────────────────────────
33
+ function severityEmoji(count, level) {
34
+ if (count === 0)
35
+ return `✅ 0 ${level}`;
36
+ if (level === "critical")
37
+ return `🔴 ${count} ${level}`;
38
+ if (level === "high")
39
+ return `🟠 ${count} ${level}`;
40
+ if (level === "medium")
41
+ return `🟡 ${count} ${level}`;
42
+ return `🔵 ${count} ${level}`;
43
+ }
44
+ function verdictEmoji(verdict) {
45
+ const map = {
46
+ pass: "✅ PASS",
47
+ fail: "❌ FAIL",
48
+ warning: "⚠️ WARNING",
49
+ };
50
+ return map[verdict] || verdict.toUpperCase();
51
+ }
52
+ function formatMarkdown(metrics) {
53
+ const lines = [];
54
+ lines.push(`## Judges Review Summary`);
55
+ lines.push(``);
56
+ lines.push(`**Verdict:** ${verdictEmoji(metrics.overallVerdict)} | **Score:** ${metrics.overallScore}/100 | **Judges:** ${metrics.judgeCount}`);
57
+ lines.push(``);
58
+ lines.push(`| Severity | Count |`);
59
+ lines.push(`|----------|-------|`);
60
+ lines.push(`| ${severityEmoji(metrics.criticalCount, "critical")} | ${metrics.criticalCount} |`);
61
+ lines.push(`| ${severityEmoji(metrics.highCount, "high")} | ${metrics.highCount} |`);
62
+ lines.push(`| ${severityEmoji(metrics.mediumCount, "medium")} | ${metrics.mediumCount} |`);
63
+ lines.push(`| ${severityEmoji(metrics.lowCount, "low")} | ${metrics.lowCount} |`);
64
+ lines.push(`| **Total** | **${metrics.totalFindings}** |`);
65
+ if (metrics.topRules.length > 0) {
66
+ lines.push(``);
67
+ lines.push(`**Top Rules:**`);
68
+ for (const r of metrics.topRules) {
69
+ lines.push(`- \`${r.ruleId}\` (${r.count})`);
70
+ }
71
+ }
72
+ lines.push(``);
73
+ lines.push(`---`);
74
+ lines.push(`*Generated by [Judges Panel](https://github.com/KevinRabun/judges)*`);
75
+ return lines.join("\n");
76
+ }
77
+ function formatSlack(metrics) {
78
+ const lines = [];
79
+ lines.push(`*Judges Review Summary*`);
80
+ lines.push(`Verdict: ${verdictEmoji(metrics.overallVerdict)} | Score: ${metrics.overallScore}/100 | Judges: ${metrics.judgeCount}`);
81
+ lines.push(``);
82
+ lines.push(`${severityEmoji(metrics.criticalCount, "critical")} | ${severityEmoji(metrics.highCount, "high")} | ${severityEmoji(metrics.mediumCount, "medium")} | ${severityEmoji(metrics.lowCount, "low")}`);
83
+ lines.push(`Total: ${metrics.totalFindings} findings`);
84
+ if (metrics.topRules.length > 0) {
85
+ lines.push(``);
86
+ lines.push(`Top rules: ${metrics.topRules.map((r) => `${r.ruleId} (${r.count})`).join(", ")}`);
87
+ }
88
+ return lines.join("\n");
89
+ }
90
+ function formatOneLiner(metrics) {
91
+ return `${verdictEmoji(metrics.overallVerdict)} Score: ${metrics.overallScore}/100 | ${metrics.totalFindings} findings (${metrics.criticalCount}C/${metrics.highCount}H/${metrics.mediumCount}M/${metrics.lowCount}L) | ${metrics.judgeCount} judges`;
92
+ }
93
+ // ─── CLI ────────────────────────────────────────────────────────────────────
94
+ export function runReviewSummary(argv) {
95
+ if (argv.includes("--help") || argv.includes("-h")) {
96
+ console.log(`
97
+ judges review-summary — Generate PR-ready review summary
98
+
99
+ Usage:
100
+ judges review-summary --input verdict.json
101
+ judges review-summary --input verdict.json --style markdown
102
+ judges review-summary --input verdict.json --style slack
103
+ judges review-summary --format json
104
+
105
+ Options:
106
+ --input <file> JSON file with TribunalVerdict (required)
107
+ --style <type> Output style: text, markdown, slack, oneliner (default: text)
108
+ --format json Raw JSON metrics output
109
+ --help, -h Show this help
110
+
111
+ Styles:
112
+ text Plain text summary (default)
113
+ markdown GitHub PR description format
114
+ slack Slack message format
115
+ oneliner Single-line summary
116
+
117
+ Generates a concise, copy-pasteable review summary suitable for
118
+ PR descriptions, Slack messages, or CI logs.
119
+ `);
120
+ return;
121
+ }
122
+ const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
123
+ const style = argv.find((_a, i) => argv[i - 1] === "--style") || "text";
124
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
125
+ if (!inputPath) {
126
+ console.error("Error: --input is required. Provide a JSON verdict file.");
127
+ process.exitCode = 1;
128
+ return;
129
+ }
130
+ let verdict;
131
+ try {
132
+ const raw = readFileSync(inputPath, "utf-8");
133
+ verdict = JSON.parse(raw);
134
+ }
135
+ catch {
136
+ console.error(`Error: Cannot read or parse ${inputPath}`);
137
+ process.exitCode = 1;
138
+ return;
139
+ }
140
+ const metrics = extractMetrics(verdict);
141
+ if (format === "json") {
142
+ console.log(JSON.stringify(metrics, null, 2));
143
+ return;
144
+ }
145
+ switch (style) {
146
+ case "markdown":
147
+ console.log(formatMarkdown(metrics));
148
+ break;
149
+ case "slack":
150
+ console.log(formatSlack(metrics));
151
+ break;
152
+ case "oneliner":
153
+ console.log(formatOneLiner(metrics));
154
+ break;
155
+ default:
156
+ console.log(`\n Review Summary\n ─────────────────────────────`);
157
+ console.log(` Verdict: ${verdictEmoji(metrics.overallVerdict)}`);
158
+ console.log(` Score: ${metrics.overallScore}/100`);
159
+ console.log(` Judges: ${metrics.judgeCount}`);
160
+ console.log(` Total findings: ${metrics.totalFindings}`);
161
+ console.log(` ${severityEmoji(metrics.criticalCount, "critical")}`);
162
+ console.log(` ${severityEmoji(metrics.highCount, "high")}`);
163
+ console.log(` ${severityEmoji(metrics.mediumCount, "medium")}`);
164
+ console.log(` ${severityEmoji(metrics.lowCount, "low")}`);
165
+ if (metrics.topRules.length > 0) {
166
+ console.log(`\n Top Rules:`);
167
+ for (const r of metrics.topRules) {
168
+ console.log(` ${r.ruleId} (${r.count})`);
169
+ }
170
+ }
171
+ console.log();
172
+ break;
173
+ }
174
+ }
175
+ //# sourceMappingURL=review-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-summary.js","sourceRoot":"","sources":["../../src/commands/review-summary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAiBlC,+EAA+E;AAE/E,SAAS,cAAc,CAAC,OAAwB;IAC9C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;IACxC,MAAM,cAAc,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAE3F,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC;QAChC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC;QAClC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;SACvC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAEjD,OAAO;QACL,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,aAAa,EAAE,cAAc,CAAC,UAAU,CAAC;QACzC,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC;QACjC,WAAW,EAAE,cAAc,CAAC,QAAQ,CAAC;QACrC,QAAQ,EAAE,cAAc,CAAC,KAAK,CAAC;QAC/B,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC;QAC5C,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,CAAC;QACvC,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,SAAS;QACnD,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,SAAS,aAAa,CAAC,KAAa,EAAE,KAAa;IACjD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,OAAO,KAAK,EAAE,CAAC;IACvC,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;IACxD,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;IACpD,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;IACtD,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,GAAG,GAA2B;QAClC,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,YAAY;KACtB,CAAC;IACF,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,cAAc,CAAC,OAAuB;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,gBAAgB,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC,iBAAiB,OAAO,CAAC,YAAY,sBAAsB,OAAO,CAAC,UAAU,EAAE,CACpI,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,MAAM,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;IACjG,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IAC3F,KAAK,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IAClF,KAAK,CAAC,IAAI,CAAC,mBAAmB,OAAO,CAAC,aAAa,MAAM,CAAC,CAAC;IAE3D,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC;IAElF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,WAAW,CAAC,OAAuB;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CACR,YAAY,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC,aAAa,OAAO,CAAC,YAAY,kBAAkB,OAAO,CAAC,UAAU,EAAE,CACxH,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,GAAG,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAClM,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,aAAa,WAAW,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,cAAc,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,OAAuB;IAC7C,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,OAAO,CAAC,YAAY,UAAU,OAAO,CAAC,aAAa,cAAc,OAAO,CAAC,aAAa,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO,CAAC,WAAW,KAAK,OAAO,CAAC,QAAQ,QAAQ,OAAO,CAAC,UAAU,SAAS,CAAC;AACxP,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAuBf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,MAAM,CAAC;IACxF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAE1F,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC1E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,OAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAExC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU;YACb,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;YACrC,MAAM;QACR,KAAK,OAAO;YACV,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YAClC,MAAM;QACR,KAAK,UAAU;YACb,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;YACrC,MAAM;QACR;YACE,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,gBAAgB,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,YAAY,MAAM,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,SAAS,aAAa,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,SAAS,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YACjE,OAAO,CAAC,GAAG,CAAC,SAAS,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YACrE,OAAO,CAAC,GAAG,CAAC,SAAS,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAC/D,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAChC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACjC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,MAAM;IACV,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Rule-test — Test custom rules against sample code before deployment.
3
+ */
4
+ export declare function runRuleTest(argv: string[]): void;
5
+ //# sourceMappingURL=rule-test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-test.d.ts","sourceRoot":"","sources":["../../src/commands/rule-test.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6HH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsIhD"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Rule-test — Test custom rules against sample code before deployment.
3
+ */
4
+ import { readFileSync, existsSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── Rule loading ───────────────────────────────────────────────────────────
7
+ function loadRules(rulesPath) {
8
+ const raw = readFileSync(rulesPath, "utf-8");
9
+ const parsed = JSON.parse(raw);
10
+ const rules = Array.isArray(parsed) ? parsed : parsed.rules || [];
11
+ return rules;
12
+ }
13
+ // ─── File collection ────────────────────────────────────────────────────────
14
+ function collectFiles(dirPath) {
15
+ const results = [];
16
+ if (!existsSync(dirPath))
17
+ return results;
18
+ const entries = readdirSync(dirPath);
19
+ for (const entry of entries) {
20
+ const fullPath = join(dirPath, entry);
21
+ try {
22
+ const st = statSync(fullPath);
23
+ if (st.isDirectory()) {
24
+ results.push(...collectFiles(fullPath));
25
+ }
26
+ else {
27
+ results.push(fullPath);
28
+ }
29
+ }
30
+ catch {
31
+ // skip inaccessible entries
32
+ }
33
+ }
34
+ return results;
35
+ }
36
+ // ─── Language matching ──────────────────────────────────────────────────────
37
+ const EXT_TO_LANG = {
38
+ ".ts": "typescript",
39
+ ".tsx": "typescript",
40
+ ".js": "javascript",
41
+ ".jsx": "javascript",
42
+ ".py": "python",
43
+ ".go": "go",
44
+ ".rs": "rust",
45
+ ".java": "java",
46
+ ".cs": "csharp",
47
+ ".cpp": "cpp",
48
+ ".c": "c",
49
+ ".rb": "ruby",
50
+ ".php": "php",
51
+ };
52
+ function matchesLanguage(filePath, languages) {
53
+ if (!languages || languages.length === 0)
54
+ return true;
55
+ const ext = extname(filePath).toLowerCase();
56
+ const lang = EXT_TO_LANG[ext] || ext.slice(1);
57
+ return languages.includes(lang);
58
+ }
59
+ // ─── Rule testing ───────────────────────────────────────────────────────────
60
+ function testRule(rule, files) {
61
+ const results = [];
62
+ let regex;
63
+ try {
64
+ regex = new RegExp(rule.pattern, "gi");
65
+ }
66
+ catch {
67
+ console.error(` ❌ Rule '${rule.id}' has invalid regex: ${rule.pattern}`);
68
+ return results;
69
+ }
70
+ for (const file of files) {
71
+ if (!matchesLanguage(file, rule.languages))
72
+ continue;
73
+ let content;
74
+ try {
75
+ content = readFileSync(file, "utf-8");
76
+ }
77
+ catch {
78
+ continue;
79
+ }
80
+ const lines = content.split("\n");
81
+ const matches = [];
82
+ for (let i = 0; i < lines.length; i++) {
83
+ regex.lastIndex = 0;
84
+ if (regex.test(lines[i])) {
85
+ matches.push({ line: i + 1, content: lines[i].trim().slice(0, 120) });
86
+ }
87
+ }
88
+ results.push({
89
+ ruleId: rule.id,
90
+ file,
91
+ matches,
92
+ matched: matches.length > 0,
93
+ });
94
+ }
95
+ return results;
96
+ }
97
+ // ─── CLI ────────────────────────────────────────────────────────────────────
98
+ export function runRuleTest(argv) {
99
+ if (argv.includes("--help") || argv.includes("-h")) {
100
+ console.log(`
101
+ judges rule-test — Test custom rules against sample code
102
+
103
+ Usage:
104
+ judges rule-test --rules custom-rules.json --fixtures tests/fixtures
105
+ judges rule-test --rules custom-rules.json --file sample.ts
106
+ judges rule-test --rules custom-rules.json --fixtures src --format json
107
+
108
+ Options:
109
+ --rules <file> JSON file with custom rules (required)
110
+ --fixtures <dir> Directory with test fixture files
111
+ --file <path> Test against a single file
112
+ --rule <id> Test only a specific rule by ID
113
+ --format json JSON output
114
+ --help, -h Show this help
115
+
116
+ Validates custom rules against real code before deployment. Ensures
117
+ patterns match expected findings and catch the intended issues.
118
+ `);
119
+ return;
120
+ }
121
+ const rulesPath = argv.find((_a, i) => argv[i - 1] === "--rules");
122
+ const fixturesDir = argv.find((_a, i) => argv[i - 1] === "--fixtures");
123
+ const singleFile = argv.find((_a, i) => argv[i - 1] === "--file");
124
+ const ruleFilter = argv.find((_a, i) => argv[i - 1] === "--rule");
125
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
126
+ if (!rulesPath) {
127
+ console.error("Error: --rules is required. Provide a custom rules JSON file.");
128
+ process.exitCode = 1;
129
+ return;
130
+ }
131
+ if (!existsSync(rulesPath)) {
132
+ console.error(`Error: Rules file not found: ${rulesPath}`);
133
+ process.exitCode = 1;
134
+ return;
135
+ }
136
+ let rules;
137
+ try {
138
+ rules = loadRules(rulesPath);
139
+ }
140
+ catch {
141
+ console.error(`Error: Cannot parse rules file: ${rulesPath}`);
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+ if (ruleFilter) {
146
+ rules = rules.filter((r) => r.id === ruleFilter);
147
+ if (rules.length === 0) {
148
+ console.error(`Error: Rule '${ruleFilter}' not found in ${rulesPath}`);
149
+ process.exitCode = 1;
150
+ return;
151
+ }
152
+ }
153
+ // Gather target files
154
+ let files;
155
+ if (singleFile) {
156
+ if (!existsSync(singleFile)) {
157
+ console.error(`Error: File not found: ${singleFile}`);
158
+ process.exitCode = 1;
159
+ return;
160
+ }
161
+ files = [singleFile];
162
+ }
163
+ else if (fixturesDir) {
164
+ files = collectFiles(fixturesDir);
165
+ }
166
+ else {
167
+ console.error("Error: Either --fixtures or --file is required.");
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ if (files.length === 0) {
172
+ console.log("No files found to test against.");
173
+ return;
174
+ }
175
+ // Run tests
176
+ const allResults = [];
177
+ for (const rule of rules) {
178
+ allResults.push(...testRule(rule, files));
179
+ }
180
+ const matchedResults = allResults.filter((r) => r.matched);
181
+ const rulesWithMatches = new Set(matchedResults.map((r) => r.ruleId));
182
+ if (format === "json") {
183
+ console.log(JSON.stringify({
184
+ rulesCount: rules.length,
185
+ filesCount: files.length,
186
+ matchedRules: rulesWithMatches.size,
187
+ totalMatches: matchedResults.reduce((s, r) => s + r.matches.length, 0),
188
+ results: matchedResults.map((r) => ({
189
+ ruleId: r.ruleId,
190
+ file: r.file,
191
+ matchCount: r.matches.length,
192
+ matches: r.matches,
193
+ })),
194
+ }, null, 2));
195
+ return;
196
+ }
197
+ console.log(`\n Rule Test Results\n ─────────────────────────────`);
198
+ console.log(` Rules tested: ${rules.length}`);
199
+ console.log(` Files scanned: ${files.length}`);
200
+ console.log(` Rules with matches: ${rulesWithMatches.size}\n`);
201
+ for (const rule of rules) {
202
+ const ruleResults = matchedResults.filter((r) => r.ruleId === rule.id);
203
+ const totalMatches = ruleResults.reduce((s, r) => s + r.matches.length, 0);
204
+ const icon = totalMatches > 0 ? "✅" : "⬜";
205
+ console.log(` ${icon} ${rule.id} — ${totalMatches} match(es) across ${ruleResults.length} file(s)`);
206
+ console.log(` Pattern: ${rule.pattern}`);
207
+ console.log(` Severity: ${rule.severity}`);
208
+ for (const rr of ruleResults.slice(0, 3)) {
209
+ for (const m of rr.matches.slice(0, 3)) {
210
+ console.log(` L${m.line}: ${m.content}`);
211
+ }
212
+ }
213
+ console.log();
214
+ }
215
+ }
216
+ //# sourceMappingURL=rule-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-test.js","sourceRoot":"","sources":["../../src/commands/rule-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Severity-tune — Auto-calibrate severity levels based on project patterns.
3
+ */
4
+ export declare function runSeverityTune(argv: string[]): void;
5
+ //# sourceMappingURL=severity-tune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity-tune.d.ts","sourceRoot":"","sources":["../../src/commands/severity-tune.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+LH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqDpD"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Severity-tune — Auto-calibrate severity levels based on project patterns.
3
+ */
4
+ import { readFileSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── Patterns with default severity ────────────────────────────────────────
7
+ const TUNE_PATTERNS = [
8
+ {
9
+ name: "hardcoded-secret",
10
+ severity: "critical",
11
+ regex: /(?:password|secret|api_key|token)\s*[:=]\s*["'][^"']{8,}/i,
12
+ },
13
+ { name: "eval-usage", severity: "critical", regex: /\beval\s*\(/ },
14
+ { name: "sql-concat", severity: "critical", regex: /(?:query|execute)\s*\(\s*["'`].*\+/ },
15
+ { name: "xss-risk", severity: "high", regex: /innerHTML\s*=|document\.write\s*\(/ },
16
+ { name: "command-injection", severity: "critical", regex: /exec(?:Sync)?\s*\(\s*`[^`]*\$\{/ },
17
+ { name: "empty-catch", severity: "medium", regex: /catch\s*\([^)]*\)\s*\{\s*\}/ },
18
+ { name: "any-type", severity: "medium", regex: /:\s*any\b/ },
19
+ { name: "unsafe-regex", severity: "high", regex: /new\s+RegExp\s*\([^)]*\+/ },
20
+ { name: "deprecated-api", severity: "medium", regex: /new\s+Buffer\s*\(|\.substr\s*\(/ },
21
+ { name: "console-log", severity: "low", regex: /console\.log\s*\(/ },
22
+ { name: "todo-fixme", severity: "low", regex: /\/\/\s*(?:TODO|FIXME|HACK)\b/i },
23
+ { name: "magic-number", severity: "low", regex: /(?:if|return|===?)\s*(?<!\w)\d{3,}(?!\w)/ },
24
+ { name: "long-line", severity: "low", regex: /^.{200,}$/ },
25
+ { name: "nested-ternary", severity: "medium", regex: /[?][^:]*[?]/ },
26
+ { name: "god-function", severity: "medium", regex: /^(?:export\s+)?(?:async\s+)?function\s+\w+/ },
27
+ ];
28
+ // ─── Helpers ────────────────────────────────────────────────────────────────
29
+ function collectSourceFiles(dir) {
30
+ const exts = new Set([".ts", ".js", ".tsx", ".jsx", ".py", ".java", ".go", ".rs", ".cs"]);
31
+ const files = [];
32
+ const skipDirs = new Set(["node_modules", ".git", "dist", "build", "coverage"]);
33
+ function walk(d) {
34
+ let entries;
35
+ try {
36
+ entries = readdirSync(d);
37
+ }
38
+ catch {
39
+ return;
40
+ }
41
+ for (const name of entries) {
42
+ if (skipDirs.has(name))
43
+ continue;
44
+ const full = join(d, name);
45
+ try {
46
+ const st = statSync(full);
47
+ if (st.isDirectory())
48
+ walk(full);
49
+ else if (exts.has(extname(name)))
50
+ files.push(full);
51
+ }
52
+ catch {
53
+ // skip
54
+ }
55
+ }
56
+ }
57
+ walk(dir);
58
+ return files;
59
+ }
60
+ function analyzeSeverity(files) {
61
+ const patternData = new Map();
62
+ for (const pat of TUNE_PATTERNS) {
63
+ patternData.set(pat.name, { occurrences: 0, files: new Set(), severity: pat.severity });
64
+ }
65
+ for (const filePath of files) {
66
+ let content;
67
+ try {
68
+ content = readFileSync(filePath, "utf-8");
69
+ }
70
+ catch {
71
+ continue;
72
+ }
73
+ const lines = content.split("\n");
74
+ for (const pat of TUNE_PATTERNS) {
75
+ // Special handling for god-function: count functions > 80 lines
76
+ if (pat.name === "god-function") {
77
+ let funcCount = 0;
78
+ let inFunc = false;
79
+ let braceDepth = 0;
80
+ let funcLines = 0;
81
+ for (const line of lines) {
82
+ if (pat.regex.test(line) && !inFunc) {
83
+ inFunc = true;
84
+ braceDepth = 0;
85
+ funcLines = 0;
86
+ }
87
+ if (inFunc) {
88
+ funcLines++;
89
+ braceDepth += (line.match(/\{/g) || []).length;
90
+ braceDepth -= (line.match(/\}/g) || []).length;
91
+ if (braceDepth <= 0 && funcLines > 1) {
92
+ if (funcLines > 80)
93
+ funcCount++;
94
+ inFunc = false;
95
+ }
96
+ }
97
+ }
98
+ if (funcCount > 0) {
99
+ const data = patternData.get(pat.name);
100
+ data.occurrences += funcCount;
101
+ data.files.add(filePath);
102
+ }
103
+ continue;
104
+ }
105
+ let hitCount = 0;
106
+ for (const line of lines) {
107
+ if (pat.regex.test(line))
108
+ hitCount++;
109
+ }
110
+ if (hitCount > 0) {
111
+ const data = patternData.get(pat.name);
112
+ data.occurrences += hitCount;
113
+ data.files.add(filePath);
114
+ }
115
+ }
116
+ }
117
+ // Generate tuning recommendations
118
+ const recommendations = [];
119
+ const fileCount = files.length;
120
+ for (const [name, data] of patternData) {
121
+ if (data.occurrences === 0)
122
+ continue;
123
+ const filePercent = fileCount > 0 ? (data.files.size / fileCount) * 100 : 0;
124
+ let suggestedSeverity = data.severity;
125
+ let reason = "Current severity is appropriate";
126
+ // If a pattern appears in >50% of files, it may be a team convention — consider lowering severity
127
+ if (filePercent > 50 && data.severity !== "low") {
128
+ suggestedSeverity = data.severity === "critical" ? "high" : data.severity === "high" ? "medium" : "low";
129
+ reason = `Appears in ${Math.round(filePercent)}% of files — may be a project norm. Consider lowering severity.`;
130
+ }
131
+ // If a critical/high pattern appears very rarely, keep it — it's genuinely concerning
132
+ else if (data.occurrences <= 2 && (data.severity === "critical" || data.severity === "high")) {
133
+ reason = "Rare occurrence — keep current severity to catch regressions";
134
+ }
135
+ // If low-severity pattern is pervasive, suggest ignoring
136
+ else if (data.severity === "low" && data.occurrences > 100) {
137
+ suggestedSeverity = "info";
138
+ reason = `${data.occurrences} occurrences — consider ignoring to reduce noise`;
139
+ }
140
+ if (suggestedSeverity !== data.severity) {
141
+ recommendations.push({
142
+ pattern: name,
143
+ currentSeverity: data.severity,
144
+ occurrences: data.occurrences,
145
+ filesAffected: data.files.size,
146
+ suggestedSeverity,
147
+ reason,
148
+ });
149
+ }
150
+ }
151
+ recommendations.sort((a, b) => b.occurrences - a.occurrences);
152
+ const summary = recommendations.length === 0
153
+ ? "All severity levels appear well-calibrated for this project."
154
+ : `${recommendations.length} severity adjustments recommended to reduce noise and improve signal.`;
155
+ return {
156
+ filesScanned: files.length,
157
+ patternsAnalyzed: TUNE_PATTERNS.length,
158
+ tuningRecommendations: recommendations,
159
+ summary,
160
+ };
161
+ }
162
+ // ─── CLI ────────────────────────────────────────────────────────────────────
163
+ export function runSeverityTune(argv) {
164
+ if (argv.includes("--help") || argv.includes("-h")) {
165
+ console.log(`
166
+ judges severity-tune — Auto-calibrate severity levels for your project
167
+
168
+ Usage:
169
+ judges severity-tune [dir] Analyze and recommend adjustments
170
+ judges severity-tune --format json JSON output
171
+
172
+ Options:
173
+ [dir] Target directory (default: .)
174
+ --format json JSON output
175
+ --help, -h Show this help
176
+
177
+ Analyzes your codebase to determine if default severity levels match your
178
+ project's patterns. Recommends adjustments to reduce alert fatigue for
179
+ pervasive patterns while maintaining sensitivity for genuine issues.
180
+ `);
181
+ return;
182
+ }
183
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
184
+ const dir = argv.find((a) => !a.startsWith("-") && a !== "severity-tune" && argv[argv.indexOf(a) - 1] !== "--format") || ".";
185
+ const files = collectSourceFiles(dir);
186
+ if (files.length === 0) {
187
+ console.log("No source files found.");
188
+ return;
189
+ }
190
+ const result = analyzeSeverity(files);
191
+ if (format === "json") {
192
+ console.log(JSON.stringify(result, null, 2));
193
+ return;
194
+ }
195
+ console.log(`\n Severity Tune\n ─────────────────────────────`);
196
+ console.log(` Files scanned: ${result.filesScanned}`);
197
+ console.log(` Patterns analyzed: ${result.patternsAnalyzed}`);
198
+ console.log(`\n 💡 ${result.summary}`);
199
+ if (result.tuningRecommendations.length > 0) {
200
+ console.log("\n Recommendations:");
201
+ for (const rec of result.tuningRecommendations) {
202
+ console.log(`\n ${rec.pattern}: ${rec.currentSeverity} → ${rec.suggestedSeverity}`);
203
+ console.log(` ${rec.occurrences} occurrences in ${rec.filesAffected} files`);
204
+ console.log(` ${rec.reason}`);
205
+ }
206
+ }
207
+ console.log();
208
+ }
209
+ //# sourceMappingURL=severity-tune.js.map