@kevinrabun/judges 3.70.0 → 3.72.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 (70) hide show
  1. package/CHANGELOG.md +24 -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/finding-auto-fix.d.ts +5 -0
  6. package/dist/commands/finding-auto-fix.d.ts.map +1 -0
  7. package/dist/commands/finding-auto-fix.js +189 -0
  8. package/dist/commands/finding-auto-fix.js.map +1 -0
  9. package/dist/commands/finding-context.d.ts +5 -0
  10. package/dist/commands/finding-context.d.ts.map +1 -0
  11. package/dist/commands/finding-context.js +141 -0
  12. package/dist/commands/finding-context.js.map +1 -0
  13. package/dist/commands/finding-deduplicate.d.ts +5 -0
  14. package/dist/commands/finding-deduplicate.d.ts.map +1 -0
  15. package/dist/commands/finding-deduplicate.js +142 -0
  16. package/dist/commands/finding-deduplicate.js.map +1 -0
  17. package/dist/commands/finding-severity-override.d.ts +5 -0
  18. package/dist/commands/finding-severity-override.d.ts.map +1 -0
  19. package/dist/commands/finding-severity-override.js +132 -0
  20. package/dist/commands/finding-severity-override.js.map +1 -0
  21. package/dist/commands/review-approval.d.ts +5 -0
  22. package/dist/commands/review-approval.d.ts.map +1 -0
  23. package/dist/commands/review-approval.js +134 -0
  24. package/dist/commands/review-approval.js.map +1 -0
  25. package/dist/commands/review-config-export.d.ts +5 -0
  26. package/dist/commands/review-config-export.d.ts.map +1 -0
  27. package/dist/commands/review-config-export.js +125 -0
  28. package/dist/commands/review-config-export.js.map +1 -0
  29. package/dist/commands/review-coverage-map.d.ts +5 -0
  30. package/dist/commands/review-coverage-map.d.ts.map +1 -0
  31. package/dist/commands/review-coverage-map.js +195 -0
  32. package/dist/commands/review-coverage-map.js.map +1 -0
  33. package/dist/commands/review-feedback.d.ts +5 -0
  34. package/dist/commands/review-feedback.d.ts.map +1 -0
  35. package/dist/commands/review-feedback.js +146 -0
  36. package/dist/commands/review-feedback.js.map +1 -0
  37. package/dist/commands/review-history-search.d.ts +5 -0
  38. package/dist/commands/review-history-search.d.ts.map +1 -0
  39. package/dist/commands/review-history-search.js +215 -0
  40. package/dist/commands/review-history-search.js.map +1 -0
  41. package/dist/commands/review-ignore-path.d.ts +5 -0
  42. package/dist/commands/review-ignore-path.d.ts.map +1 -0
  43. package/dist/commands/review-ignore-path.js +148 -0
  44. package/dist/commands/review-ignore-path.js.map +1 -0
  45. package/dist/commands/review-language-stats.d.ts +5 -0
  46. package/dist/commands/review-language-stats.d.ts.map +1 -0
  47. package/dist/commands/review-language-stats.js +153 -0
  48. package/dist/commands/review-language-stats.js.map +1 -0
  49. package/dist/commands/review-onboard.d.ts +5 -0
  50. package/dist/commands/review-onboard.d.ts.map +1 -0
  51. package/dist/commands/review-onboard.js +155 -0
  52. package/dist/commands/review-onboard.js.map +1 -0
  53. package/dist/commands/review-parallel.d.ts +5 -0
  54. package/dist/commands/review-parallel.d.ts.map +1 -0
  55. package/dist/commands/review-parallel.js +183 -0
  56. package/dist/commands/review-parallel.js.map +1 -0
  57. package/dist/commands/review-pr-comment.d.ts +5 -0
  58. package/dist/commands/review-pr-comment.d.ts.map +1 -0
  59. package/dist/commands/review-pr-comment.js +107 -0
  60. package/dist/commands/review-pr-comment.js.map +1 -0
  61. package/dist/commands/review-rollback.d.ts +5 -0
  62. package/dist/commands/review-rollback.d.ts.map +1 -0
  63. package/dist/commands/review-rollback.js +172 -0
  64. package/dist/commands/review-rollback.js.map +1 -0
  65. package/dist/commands/review-score-history.d.ts +5 -0
  66. package/dist/commands/review-score-history.d.ts.map +1 -0
  67. package/dist/commands/review-score-history.js +138 -0
  68. package/dist/commands/review-score-history.js.map +1 -0
  69. package/package.json +1 -1
  70. package/server.json +2 -2
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Finding-auto-fix — Auto-generate fix suggestions for common finding patterns.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ // ─── Fix Patterns ───────────────────────────────────────────────────────────
7
+ const FIX_PATTERNS = [
8
+ {
9
+ match: /sql.?inject/i,
10
+ description: "Use parameterized queries instead of string concatenation",
11
+ snippet: "// Before: db.query(`SELECT * FROM users WHERE id = ${id}`)\n// After: db.query('SELECT * FROM users WHERE id = ?', [id])",
12
+ confidence: "high",
13
+ },
14
+ {
15
+ match: /xss|cross.?site/i,
16
+ description: "Sanitize user input before rendering in HTML",
17
+ snippet: "// Use a sanitization library or framework escaping\n// e.g., DOMPurify.sanitize(userInput) or template auto-escaping",
18
+ confidence: "high",
19
+ },
20
+ {
21
+ match: /hardcoded.?(secret|password|key|token|credential)/i,
22
+ description: "Move secrets to environment variables or a secret manager",
23
+ snippet: "// Before: const apiKey = 'sk-abc123'\n// After: const apiKey = process.env.API_KEY",
24
+ confidence: "high",
25
+ },
26
+ {
27
+ match: /eval|code.?inject/i,
28
+ description: "Avoid eval() and dynamic code execution",
29
+ snippet: "// Before: eval(userInput)\n// After: Use a safe parser or mapped function calls",
30
+ confidence: "high",
31
+ },
32
+ {
33
+ match: /path.?traversal/i,
34
+ description: "Validate and sanitize file paths",
35
+ snippet: "// Use path.resolve() and check against allowed base directory\n// const safePath = path.resolve(baseDir, userPath)\n// if (!safePath.startsWith(baseDir)) throw new Error('Invalid path')",
36
+ confidence: "high",
37
+ },
38
+ {
39
+ match: /insecure.?random/i,
40
+ description: "Use cryptographically secure random number generation",
41
+ snippet: "// Before: Math.random()\n// After: crypto.randomBytes(32) or crypto.getRandomValues()",
42
+ confidence: "high",
43
+ },
44
+ {
45
+ match: /missing.?(auth|authorization)/i,
46
+ description: "Add authentication/authorization checks",
47
+ snippet: "// Add middleware or guard to verify user identity and permissions\n// e.g., if (!req.user || !req.user.hasPermission('resource')) return res.status(403)",
48
+ confidence: "medium",
49
+ },
50
+ {
51
+ match: /error.?handling|unhandled/i,
52
+ description: "Add proper error handling with try/catch",
53
+ snippet: "// Wrap in try/catch and handle errors appropriately\n// try { await riskyOperation() } catch (err) { logger.error(err); throw new AppError('...') }",
54
+ confidence: "medium",
55
+ },
56
+ {
57
+ match: /memory.?leak/i,
58
+ description: "Clean up resources and remove event listeners",
59
+ snippet: "// Ensure cleanup in finally blocks or useEffect return\n// listener references should be stored and removed on cleanup",
60
+ confidence: "medium",
61
+ },
62
+ {
63
+ match: /n[+]1|n\+1/i,
64
+ description: "Batch database queries or use eager loading",
65
+ snippet: "// Before: for (const id of ids) { await db.find(id) }\n// After: await db.findMany({ where: { id: { in: ids } } })",
66
+ confidence: "medium",
67
+ },
68
+ ];
69
+ function generateFix(finding) {
70
+ const ruleId = finding.ruleId || "unknown";
71
+ const title = finding.title || "";
72
+ const combined = `${ruleId} ${title} ${finding.description || ""}`;
73
+ for (const pattern of FIX_PATTERNS) {
74
+ if (pattern.match.test(combined)) {
75
+ return {
76
+ ruleId,
77
+ title,
78
+ severity: finding.severity || "medium",
79
+ fixAvailable: true,
80
+ fixDescription: pattern.description,
81
+ fixSnippet: pattern.snippet,
82
+ confidence: pattern.confidence,
83
+ };
84
+ }
85
+ }
86
+ // Use the finding's own recommendation if available
87
+ if (finding.recommendation) {
88
+ return {
89
+ ruleId,
90
+ title,
91
+ severity: finding.severity || "medium",
92
+ fixAvailable: true,
93
+ fixDescription: finding.recommendation,
94
+ fixSnippet: finding.suggestedFix || "",
95
+ confidence: "low",
96
+ };
97
+ }
98
+ return {
99
+ ruleId,
100
+ title,
101
+ severity: finding.severity || "medium",
102
+ fixAvailable: false,
103
+ fixDescription: "No auto-fix available. Manual review recommended.",
104
+ fixSnippet: "",
105
+ confidence: "low",
106
+ };
107
+ }
108
+ // ─── CLI ────────────────────────────────────────────────────────────────────
109
+ export function runFindingAutoFix(argv) {
110
+ if (argv.includes("--help") || argv.includes("-h")) {
111
+ console.log(`
112
+ judges finding-auto-fix — Auto-generate fix suggestions
113
+
114
+ Usage:
115
+ judges finding-auto-fix --file report.json
116
+ judges finding-auto-fix --file report.json --format json
117
+
118
+ Options:
119
+ --file <path> Path to a tribunal verdict JSON file
120
+ --min-confidence <l> Minimum confidence (high|medium|low, default: low)
121
+ --format json JSON output
122
+ --help, -h Show this help
123
+
124
+ Analyzes findings and generates fix suggestions for common patterns
125
+ including SQL injection, XSS, hardcoded secrets, and more.
126
+
127
+ Report saved to .judges/auto-fixes.json.
128
+ `);
129
+ return;
130
+ }
131
+ const filePath = argv.find((_a, i) => argv[i - 1] === "--file");
132
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
133
+ const minConf = argv.find((_a, i) => argv[i - 1] === "--min-confidence") || "low";
134
+ if (!filePath || !existsSync(filePath)) {
135
+ console.error("Error: --file is required and must exist.");
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ let verdict;
140
+ try {
141
+ verdict = JSON.parse(readFileSync(filePath, "utf-8"));
142
+ }
143
+ catch {
144
+ console.error("Error: Could not parse verdict file.");
145
+ process.exitCode = 1;
146
+ return;
147
+ }
148
+ const findings = verdict.findings || [];
149
+ if (findings.length === 0) {
150
+ console.log("No findings to fix.");
151
+ return;
152
+ }
153
+ const confLevels = ["low", "medium", "high"];
154
+ const minIdx = confLevels.indexOf(minConf);
155
+ const fixes = findings.map(generateFix).filter((f) => confLevels.indexOf(f.confidence) >= minIdx);
156
+ const fixableCount = fixes.filter((f) => f.fixAvailable).length;
157
+ const report = {
158
+ timestamp: new Date().toISOString(),
159
+ totalFindings: fixes.length,
160
+ fixableCount,
161
+ fixes,
162
+ };
163
+ const outPath = join(".judges", "auto-fixes.json");
164
+ mkdirSync(dirname(outPath), { recursive: true });
165
+ writeFileSync(outPath, JSON.stringify(report, null, 2), "utf-8");
166
+ if (format === "json") {
167
+ console.log(JSON.stringify(report, null, 2));
168
+ return;
169
+ }
170
+ console.log("\nAuto-Fix Suggestions:");
171
+ console.log("═".repeat(70));
172
+ console.log(` Total findings: ${fixes.length} Fixable: ${fixableCount} (${fixes.length > 0 ? ((fixableCount / fixes.length) * 100).toFixed(0) : 0}%)`);
173
+ console.log("═".repeat(70));
174
+ for (const f of fixes) {
175
+ const icon = f.fixAvailable ? "✓" : "✗";
176
+ console.log(`\n ${icon} [${f.severity.toUpperCase()}] ${f.ruleId}`);
177
+ console.log(` ${f.title}`);
178
+ console.log(` Fix: ${f.fixDescription}`);
179
+ if (f.fixSnippet) {
180
+ console.log(` Confidence: ${f.confidence}`);
181
+ for (const line of f.fixSnippet.split("\n")) {
182
+ console.log(` ${line}`);
183
+ }
184
+ }
185
+ }
186
+ console.log("\n" + "═".repeat(70));
187
+ console.log(` Report saved to ${outPath}`);
188
+ }
189
+ //# sourceMappingURL=finding-auto-fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-auto-fix.js","sourceRoot":"","sources":["../../src/commands/finding-auto-fix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-context — Enrich findings with surrounding code context.
3
+ */
4
+ export declare function runFindingContext(argv: string[]): void;
5
+ //# sourceMappingURL=finding-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-context.d.ts","sourceRoot":"","sources":["../../src/commands/finding-context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuEH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoHtD"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Finding-context — Enrich findings with surrounding code context.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ // ─── Context Extraction ─────────────────────────────────────────────────────
7
+ function extractContext(fileContent, lineNumbers, contextSize) {
8
+ const lines = fileContent.split("\n");
9
+ if (lineNumbers.length === 0) {
10
+ return { before: [], after: [], atLine: [] };
11
+ }
12
+ const minLine = Math.min(...lineNumbers);
13
+ const maxLine = Math.max(...lineNumbers);
14
+ const beforeStart = Math.max(0, minLine - 1 - contextSize);
15
+ const beforeEnd = Math.max(0, minLine - 1);
16
+ const afterStart = Math.min(lines.length, maxLine);
17
+ const afterEnd = Math.min(lines.length, maxLine + contextSize);
18
+ return {
19
+ before: lines.slice(beforeStart, beforeEnd),
20
+ after: lines.slice(afterStart, afterEnd),
21
+ atLine: lineNumbers.map((n) => lines[n - 1] || "").filter(Boolean),
22
+ };
23
+ }
24
+ function enrichFinding(finding, fileContent, contextSize) {
25
+ const lineNumbers = finding.lineNumbers || [];
26
+ const ctx = extractContext(fileContent, lineNumbers, contextSize);
27
+ return {
28
+ ruleId: finding.ruleId || "unknown",
29
+ title: finding.title || "",
30
+ severity: finding.severity || "medium",
31
+ lineNumbers,
32
+ contextBefore: ctx.before,
33
+ contextAfter: ctx.after,
34
+ codeAtLine: ctx.atLine,
35
+ recommendation: finding.recommendation || "",
36
+ };
37
+ }
38
+ // ─── CLI ────────────────────────────────────────────────────────────────────
39
+ export function runFindingContext(argv) {
40
+ if (argv.includes("--help") || argv.includes("-h")) {
41
+ console.log(`
42
+ judges finding-context — Enrich findings with surrounding code context
43
+
44
+ Usage:
45
+ judges finding-context --verdict report.json --source src/api.ts
46
+ judges finding-context --verdict report.json --source src/api.ts --context 10
47
+
48
+ Options:
49
+ --verdict <path> Path to a tribunal verdict JSON file
50
+ --source <path> Source file to extract context from
51
+ --context <n> Number of context lines (default: 5)
52
+ --format json JSON output
53
+ --help, -h Show this help
54
+
55
+ Shows each finding alongside the surrounding source code, making it
56
+ easier to understand the issue without switching to an editor.
57
+
58
+ Report saved to .judges/finding-context.json.
59
+ `);
60
+ return;
61
+ }
62
+ const verdictPath = argv.find((_a, i) => argv[i - 1] === "--verdict");
63
+ const sourcePath = argv.find((_a, i) => argv[i - 1] === "--source");
64
+ const contextSize = parseInt(argv.find((_a, i) => argv[i - 1] === "--context") || "5", 10);
65
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
66
+ if (!verdictPath || !existsSync(verdictPath)) {
67
+ console.error("Error: --verdict is required and must exist.");
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ if (!sourcePath || !existsSync(sourcePath)) {
72
+ console.error("Error: --source is required and must exist.");
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ let verdict;
77
+ try {
78
+ verdict = JSON.parse(readFileSync(verdictPath, "utf-8"));
79
+ }
80
+ catch {
81
+ console.error("Error: Could not parse verdict file.");
82
+ process.exitCode = 1;
83
+ return;
84
+ }
85
+ const fileContent = readFileSync(sourcePath, "utf-8");
86
+ const findings = verdict.findings || [];
87
+ if (findings.length === 0) {
88
+ console.log("No findings to enrich.");
89
+ return;
90
+ }
91
+ const enriched = findings.map((f) => enrichFinding(f, fileContent, contextSize));
92
+ const report = {
93
+ timestamp: new Date().toISOString(),
94
+ sourceFile: sourcePath,
95
+ totalFindings: enriched.length,
96
+ enriched,
97
+ };
98
+ const outPath = join(".judges", "finding-context.json");
99
+ mkdirSync(dirname(outPath), { recursive: true });
100
+ writeFileSync(outPath, JSON.stringify(report, null, 2), "utf-8");
101
+ if (format === "json") {
102
+ console.log(JSON.stringify(report, null, 2));
103
+ return;
104
+ }
105
+ console.log(`\nFinding Context (±${contextSize} lines):`);
106
+ console.log("═".repeat(70));
107
+ console.log(` Source: ${sourcePath} Findings: ${enriched.length}`);
108
+ console.log("═".repeat(70));
109
+ for (const ef of enriched) {
110
+ console.log(`\n [${ef.severity.toUpperCase()}] ${ef.ruleId}`);
111
+ console.log(` ${ef.title}`);
112
+ if (ef.lineNumbers.length > 0) {
113
+ console.log(` Lines: ${ef.lineNumbers.join(", ")}`);
114
+ }
115
+ if (ef.contextBefore.length > 0) {
116
+ console.log(" ┌─ context before ─");
117
+ for (const l of ef.contextBefore) {
118
+ console.log(` │ ${l}`);
119
+ }
120
+ }
121
+ if (ef.codeAtLine.length > 0) {
122
+ console.log(" ├─ finding ─");
123
+ for (const l of ef.codeAtLine) {
124
+ console.log(` │ ➤ ${l}`);
125
+ }
126
+ }
127
+ if (ef.contextAfter.length > 0) {
128
+ console.log(" ├─ context after ─");
129
+ for (const l of ef.contextAfter) {
130
+ console.log(` │ ${l}`);
131
+ }
132
+ console.log(" └─");
133
+ }
134
+ if (ef.recommendation) {
135
+ console.log(` Fix: ${ef.recommendation}`);
136
+ }
137
+ }
138
+ console.log("\n" + "═".repeat(70));
139
+ console.log(` Report saved to ${outPath}`);
140
+ }
141
+ //# sourceMappingURL=finding-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-context.js","sourceRoot":"","sources":["../../src/commands/finding-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-deduplicate — Detect and deduplicate similar findings.
3
+ */
4
+ export declare function runFindingDeduplicate(argv: string[]): void;
5
+ //# sourceMappingURL=finding-deduplicate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-deduplicate.d.ts","sourceRoot":"","sources":["../../src/commands/finding-deduplicate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkFH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAwF1D"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Finding-deduplicate — Detect and deduplicate similar findings.
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { join, dirname } from "path";
6
+ // ─── Dedup Logic ────────────────────────────────────────────────────────────
7
+ function normalizeText(text) {
8
+ return text
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]/g, " ")
11
+ .replace(/\s+/g, " ")
12
+ .trim();
13
+ }
14
+ function areSimilar(a, b) {
15
+ // Same rule ID is a strong signal
16
+ if (a.ruleId && b.ruleId && a.ruleId === b.ruleId) {
17
+ // Check if titles are similar
18
+ const titleA = normalizeText(a.title || "");
19
+ const titleB = normalizeText(b.title || "");
20
+ if (titleA === titleB)
21
+ return true;
22
+ // Check Jaccard similarity on words
23
+ const wordsA = new Set(titleA.split(" "));
24
+ const wordsB = new Set(titleB.split(" "));
25
+ const intersection = new Set([...wordsA].filter((w) => wordsB.has(w)));
26
+ const union = new Set([...wordsA, ...wordsB]);
27
+ if (union.size > 0 && intersection.size / union.size > 0.5)
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ function groupFindings(findings) {
33
+ const groups = [];
34
+ const assigned = new Set();
35
+ for (let i = 0; i < findings.length; i++) {
36
+ if (assigned.has(i))
37
+ continue;
38
+ const group = {
39
+ canonical: {
40
+ ruleId: findings[i].ruleId || "unknown",
41
+ title: findings[i].title || "",
42
+ severity: findings[i].severity || "medium",
43
+ },
44
+ count: 1,
45
+ indices: [i],
46
+ };
47
+ assigned.add(i);
48
+ for (let j = i + 1; j < findings.length; j++) {
49
+ if (assigned.has(j))
50
+ continue;
51
+ if (areSimilar(findings[i], findings[j])) {
52
+ group.count++;
53
+ group.indices.push(j);
54
+ assigned.add(j);
55
+ }
56
+ }
57
+ groups.push(group);
58
+ }
59
+ return groups;
60
+ }
61
+ // ─── CLI ────────────────────────────────────────────────────────────────────
62
+ export function runFindingDeduplicate(argv) {
63
+ if (argv.includes("--help") || argv.includes("-h")) {
64
+ console.log(`
65
+ judges finding-deduplicate — Detect and deduplicate similar findings
66
+
67
+ Usage:
68
+ judges finding-deduplicate --file report.json
69
+ judges finding-deduplicate --file report.json --format json
70
+
71
+ Options:
72
+ --file <path> Path to a tribunal verdict JSON file
73
+ --format json JSON output
74
+ --help, -h Show this help
75
+
76
+ Groups similar findings together and reports duplicates.
77
+ Uses rule ID matching and title similarity (Jaccard) to detect dupes.
78
+
79
+ Report saved to .judges/dedup-report.json.
80
+ `);
81
+ return;
82
+ }
83
+ const filePath = argv.find((_a, i) => argv[i - 1] === "--file");
84
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
85
+ if (!filePath || !existsSync(filePath)) {
86
+ console.error("Error: --file is required and must exist.");
87
+ process.exitCode = 1;
88
+ return;
89
+ }
90
+ let verdict;
91
+ try {
92
+ verdict = JSON.parse(readFileSync(filePath, "utf-8"));
93
+ }
94
+ catch {
95
+ console.error("Error: Could not parse verdict file.");
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+ const findings = verdict.findings || [];
100
+ if (findings.length === 0) {
101
+ console.log("No findings to deduplicate.");
102
+ return;
103
+ }
104
+ const groups = groupFindings(findings);
105
+ const duplicateCount = findings.length - groups.length;
106
+ const report = {
107
+ timestamp: new Date().toISOString(),
108
+ originalCount: findings.length,
109
+ uniqueCount: groups.length,
110
+ duplicateCount,
111
+ groups,
112
+ };
113
+ const outPath = join(".judges", "dedup-report.json");
114
+ mkdirSync(dirname(outPath), { recursive: true });
115
+ writeFileSync(outPath, JSON.stringify(report, null, 2), "utf-8");
116
+ if (format === "json") {
117
+ console.log(JSON.stringify(report, null, 2));
118
+ return;
119
+ }
120
+ console.log("\nFinding Deduplication Report:");
121
+ console.log("═".repeat(60));
122
+ console.log(` Original: ${findings.length} Unique: ${groups.length} Duplicates: ${duplicateCount}`);
123
+ if (findings.length > 0) {
124
+ console.log(` Dedup ratio: ${((duplicateCount / findings.length) * 100).toFixed(1)}%`);
125
+ }
126
+ console.log("═".repeat(60));
127
+ const dupeGroups = groups.filter((g) => g.count > 1);
128
+ if (dupeGroups.length > 0) {
129
+ console.log("\n Duplicate Groups:");
130
+ for (const g of dupeGroups) {
131
+ console.log(`\n [${g.canonical.severity.toUpperCase()}] ${g.canonical.ruleId} (${g.count}x)`);
132
+ console.log(` ${g.canonical.title}`);
133
+ console.log(` Indices: ${g.indices.join(", ")}`);
134
+ }
135
+ }
136
+ else {
137
+ console.log("\n No duplicates found — all findings are unique.");
138
+ }
139
+ console.log("\n" + "═".repeat(60));
140
+ console.log(` Report saved to ${outPath}`);
141
+ }
142
+ //# sourceMappingURL=finding-deduplicate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-deduplicate.js","sourceRoot":"","sources":["../../src/commands/finding-deduplicate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Finding-severity-override — Override finding severity per project.
3
+ */
4
+ export declare function runFindingSeverityOverride(argv: string[]): void;
5
+ //# sourceMappingURL=finding-severity-override.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-severity-override.d.ts","sourceRoot":"","sources":["../../src/commands/finding-severity-override.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0CH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoH/D"}