@kevinrabun/judges 3.62.0 → 3.64.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 +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/auto-approve.d.ts +5 -0
  6. package/dist/commands/auto-approve.d.ts.map +1 -0
  7. package/dist/commands/auto-approve.js +189 -0
  8. package/dist/commands/auto-approve.js.map +1 -0
  9. package/dist/commands/diff-explain.d.ts +5 -0
  10. package/dist/commands/diff-explain.d.ts.map +1 -0
  11. package/dist/commands/diff-explain.js +143 -0
  12. package/dist/commands/diff-explain.js.map +1 -0
  13. package/dist/commands/finding-group.d.ts +16 -0
  14. package/dist/commands/finding-group.d.ts.map +1 -0
  15. package/dist/commands/finding-group.js +165 -0
  16. package/dist/commands/finding-group.js.map +1 -0
  17. package/dist/commands/fix-suggest.d.ts +5 -0
  18. package/dist/commands/fix-suggest.d.ts.map +1 -0
  19. package/dist/commands/fix-suggest.js +172 -0
  20. package/dist/commands/fix-suggest.js.map +1 -0
  21. package/dist/commands/ignore-list.d.ts +19 -0
  22. package/dist/commands/ignore-list.d.ts.map +1 -0
  23. package/dist/commands/ignore-list.js +166 -0
  24. package/dist/commands/ignore-list.js.map +1 -0
  25. package/dist/commands/incremental-review.d.ts +5 -0
  26. package/dist/commands/incremental-review.d.ts.map +1 -0
  27. package/dist/commands/incremental-review.js +240 -0
  28. package/dist/commands/incremental-review.js.map +1 -0
  29. package/dist/commands/multi-lang-review.d.ts +5 -0
  30. package/dist/commands/multi-lang-review.d.ts.map +1 -0
  31. package/dist/commands/multi-lang-review.js +231 -0
  32. package/dist/commands/multi-lang-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-log.d.ts +23 -0
  38. package/dist/commands/review-log.d.ts.map +1 -0
  39. package/dist/commands/review-log.js +165 -0
  40. package/dist/commands/review-log.js.map +1 -0
  41. package/dist/commands/review-priority.d.ts +5 -0
  42. package/dist/commands/review-priority.d.ts.map +1 -0
  43. package/dist/commands/review-priority.js +158 -0
  44. package/dist/commands/review-priority.js.map +1 -0
  45. package/dist/commands/review-profile.d.ts +5 -0
  46. package/dist/commands/review-profile.d.ts.map +1 -0
  47. package/dist/commands/review-profile.js +169 -0
  48. package/dist/commands/review-profile.js.map +1 -0
  49. package/dist/commands/review-stats.d.ts +5 -0
  50. package/dist/commands/review-stats.d.ts.map +1 -0
  51. package/dist/commands/review-stats.js +176 -0
  52. package/dist/commands/review-stats.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/review-template.d.ts +5 -0
  58. package/dist/commands/review-template.d.ts.map +1 -0
  59. package/dist/commands/review-template.js +213 -0
  60. package/dist/commands/review-template.js.map +1 -0
  61. package/dist/commands/rule-test.d.ts +5 -0
  62. package/dist/commands/rule-test.d.ts.map +1 -0
  63. package/dist/commands/rule-test.js +216 -0
  64. package/dist/commands/rule-test.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/package.json +1 -1
  70. package/server.json +2 -2
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Auto-approve — Auto-approve findings below a configurable threshold.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ // ─── Default policy ────────────────────────────────────────────────────────
6
+ function defaultPolicy() {
7
+ return {
8
+ maxLowFindings: 10,
9
+ maxMediumFindings: 3,
10
+ allowedRules: [],
11
+ requireMinScore: 70,
12
+ blockOnSecurity: true,
13
+ autoApproveClean: true,
14
+ };
15
+ }
16
+ // ─── Severity classification ────────────────────────────────────────────────
17
+ const SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
18
+ function isSecurity(finding) {
19
+ const rid = (finding.ruleId || "").toLowerCase();
20
+ return /sql|inject|xss|csrf|traversal|auth|secret|crypt|ssrf|deserial/i.test(rid);
21
+ }
22
+ // ─── Approval engine ───────────────────────────────────────────────────────
23
+ function evaluateApproval(verdict, policy) {
24
+ const findings = verdict.findings || [];
25
+ const violations = [];
26
+ const approved = [];
27
+ const flagged = [];
28
+ // Check score threshold
29
+ if ((verdict.overallScore ?? 0) < policy.requireMinScore) {
30
+ violations.push(`Score ${verdict.overallScore} is below minimum ${policy.requireMinScore}`);
31
+ }
32
+ // Classify findings
33
+ const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
34
+ for (const f of findings) {
35
+ const sev = f.severity || "low";
36
+ severityCounts[sev] = (severityCounts[sev] || 0) + 1;
37
+ const sevLevel = SEVERITY_ORDER[sev] ?? 3;
38
+ // Critical and high are always flagged
39
+ if (sevLevel <= 1) {
40
+ flagged.push(f);
41
+ continue;
42
+ }
43
+ // Security findings blocked if policy says so
44
+ if (policy.blockOnSecurity && isSecurity(f)) {
45
+ flagged.push(f);
46
+ continue;
47
+ }
48
+ // Low findings auto-approved
49
+ if (sev === "low") {
50
+ approved.push(f);
51
+ continue;
52
+ }
53
+ // Medium finding — check against allowed rules
54
+ if (policy.allowedRules.length > 0 && policy.allowedRules.includes(f.ruleId)) {
55
+ approved.push(f);
56
+ }
57
+ else {
58
+ flagged.push(f);
59
+ }
60
+ }
61
+ // Check threshold violations
62
+ if (severityCounts["critical"] > 0) {
63
+ violations.push(`${severityCounts["critical"]} critical finding(s) detected`);
64
+ }
65
+ if (severityCounts["high"] > 0) {
66
+ violations.push(`${severityCounts["high"]} high finding(s) detected`);
67
+ }
68
+ if (severityCounts["medium"] > policy.maxMediumFindings) {
69
+ violations.push(`${severityCounts["medium"]} medium findings exceed max of ${policy.maxMediumFindings}`);
70
+ }
71
+ if (severityCounts["low"] > policy.maxLowFindings) {
72
+ violations.push(`${severityCounts["low"]} low findings exceed max of ${policy.maxLowFindings}`);
73
+ }
74
+ const isApproved = violations.length === 0 || (findings.length === 0 && policy.autoApproveClean);
75
+ let reason;
76
+ if (isApproved && findings.length === 0) {
77
+ reason = "Clean review — no findings";
78
+ }
79
+ else if (isApproved) {
80
+ reason = "All findings within policy thresholds";
81
+ }
82
+ else {
83
+ reason = violations.join("; ");
84
+ }
85
+ return {
86
+ approved: isApproved,
87
+ reason,
88
+ autoApproved: approved.length,
89
+ manualRequired: flagged.length,
90
+ policyViolations: violations,
91
+ findings: { approved, flagged },
92
+ };
93
+ }
94
+ // ─── CLI ────────────────────────────────────────────────────────────────────
95
+ export function runAutoApprove(argv) {
96
+ if (argv.includes("--help") || argv.includes("-h")) {
97
+ console.log(`
98
+ judges auto-approve — Auto-approve findings below threshold
99
+
100
+ Usage:
101
+ judges auto-approve --input verdict.json
102
+ judges auto-approve --input verdict.json --policy policy.json
103
+ judges auto-approve --input verdict.json --format json
104
+
105
+ Options:
106
+ --input <file> TribunalVerdict JSON file (required)
107
+ --policy <file> Approval policy JSON file (optional, uses defaults)
108
+ --min-score <n> Minimum score for approval (default: 70)
109
+ --format json JSON output
110
+ --help, -h Show this help
111
+
112
+ Policy defaults:
113
+ maxLowFindings: 10, maxMediumFindings: 3, requireMinScore: 70,
114
+ blockOnSecurity: true, autoApproveClean: true
115
+
116
+ Auto-approves low-severity findings and flags critical/high findings
117
+ for manual review. Reduces noise while maintaining safety.
118
+ `);
119
+ return;
120
+ }
121
+ const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
122
+ const policyPath = argv.find((_a, i) => argv[i - 1] === "--policy");
123
+ const minScoreStr = argv.find((_a, i) => argv[i - 1] === "--min-score");
124
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
125
+ if (!inputPath) {
126
+ console.error("Error: --input is required. Provide a verdict JSON file.");
127
+ process.exitCode = 1;
128
+ return;
129
+ }
130
+ let verdict;
131
+ try {
132
+ verdict = JSON.parse(readFileSync(inputPath, "utf-8"));
133
+ }
134
+ catch {
135
+ console.error(`Error: Cannot read or parse ${inputPath}`);
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+ let policy = defaultPolicy();
140
+ if (policyPath) {
141
+ try {
142
+ const custom = JSON.parse(readFileSync(policyPath, "utf-8"));
143
+ policy = { ...policy, ...custom };
144
+ }
145
+ catch {
146
+ console.error(`Error: Cannot read policy file ${policyPath}`);
147
+ process.exitCode = 1;
148
+ return;
149
+ }
150
+ }
151
+ if (minScoreStr) {
152
+ policy.requireMinScore = parseInt(minScoreStr, 10);
153
+ }
154
+ const result = evaluateApproval(verdict, policy);
155
+ if (format === "json") {
156
+ console.log(JSON.stringify({
157
+ ...result,
158
+ findings: { autoApproved: result.findings.approved.length, flagged: result.findings.flagged.length },
159
+ }, null, 2));
160
+ if (!result.approved)
161
+ process.exitCode = 1;
162
+ return;
163
+ }
164
+ const icon = result.approved ? "✅" : "❌";
165
+ console.log(`\n Auto-Approval Result: ${icon} ${result.approved ? "APPROVED" : "REQUIRES REVIEW"}\n ─────────────────────────────`);
166
+ console.log(` Reason: ${result.reason}`);
167
+ console.log(` Score: ${verdict.overallScore ?? 0}/100`);
168
+ console.log(` Auto-approved: ${result.autoApproved} finding(s)`);
169
+ console.log(` Manual review: ${result.manualRequired} finding(s)`);
170
+ if (result.policyViolations.length > 0) {
171
+ console.log(`\n Policy Violations:`);
172
+ for (const v of result.policyViolations) {
173
+ console.log(` ❌ ${v}`);
174
+ }
175
+ }
176
+ if (result.findings.flagged.length > 0) {
177
+ console.log(`\n Flagged Findings:`);
178
+ for (const f of result.findings.flagged.slice(0, 10)) {
179
+ console.log(` 🔴 [${f.severity}] ${f.ruleId}: ${f.title}`);
180
+ }
181
+ if (result.findings.flagged.length > 10) {
182
+ console.log(` ... and ${result.findings.flagged.length - 10} more`);
183
+ }
184
+ }
185
+ console.log();
186
+ if (!result.approved)
187
+ process.exitCode = 1;
188
+ }
189
+ //# sourceMappingURL=auto-approve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-approve.js","sourceRoot":"","sources":["../../src/commands/auto-approve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Diff-explain — Explain why specific diff changes were flagged.
3
+ */
4
+ export declare function runDiffExplain(argv: string[]): void;
5
+ //# sourceMappingURL=diff-explain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-explain.d.ts","sourceRoot":"","sources":["../../src/commands/diff-explain.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyFH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoFnD"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Diff-explain — Explain why specific diff changes were flagged.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ // ─── Knowledge base for common rules ────────────────────────────────────────
6
+ const RULE_EXPLANATIONS = {
7
+ "sql-injection": {
8
+ risk: "Attacker can manipulate database queries to extract, modify, or delete data",
9
+ whyFlagged: "User input is concatenated into SQL queries without parameterization",
10
+ action: "Use parameterized queries or prepared statements instead of string concatenation",
11
+ },
12
+ "xss-vulnerability": {
13
+ risk: "Attacker can inject malicious scripts that execute in other users' browsers",
14
+ whyFlagged: "User-supplied data is rendered in HTML without proper escaping",
15
+ action: "Sanitize and escape all user input before rendering in HTML context",
16
+ },
17
+ "hardcoded-secret": {
18
+ risk: "Credentials exposed in source code can be harvested from version control",
19
+ whyFlagged: "String patterns matching API keys, passwords, or tokens detected in code",
20
+ action: "Move secrets to environment variables or a secrets manager",
21
+ },
22
+ "path-traversal": {
23
+ risk: "Attacker can access files outside intended directories",
24
+ whyFlagged: "File paths constructed from user input without validation",
25
+ action: "Validate and normalize file paths, restrict to allowed directories",
26
+ },
27
+ "insecure-random": {
28
+ risk: "Predictable random values weaken security mechanisms",
29
+ whyFlagged: "Math.random() or similar used for security-sensitive operations",
30
+ action: "Use crypto.getRandomValues() or crypto.randomBytes() for security",
31
+ },
32
+ "error-info-leak": {
33
+ risk: "Internal error details can reveal system architecture to attackers",
34
+ whyFlagged: "Error messages expose stack traces, file paths, or internal details",
35
+ action: "Return generic error messages to users, log details server-side",
36
+ },
37
+ "missing-auth": {
38
+ risk: "Unauthorized access to protected resources or operations",
39
+ whyFlagged: "Endpoint or function lacks authentication/authorization checks",
40
+ action: "Add authentication middleware and verify user permissions",
41
+ },
42
+ "unsafe-deserialization": {
43
+ risk: "Attacker can execute arbitrary code via crafted serialized data",
44
+ whyFlagged: "Deserialization of untrusted data detected",
45
+ action: "Validate and whitelist types before deserialization, use safe formats like JSON",
46
+ },
47
+ };
48
+ // ─── Explanation engine ─────────────────────────────────────────────────────
49
+ function explainFinding(finding) {
50
+ const ruleId = finding.ruleId || "unknown";
51
+ const known = RULE_EXPLANATIONS[ruleId];
52
+ const explanation = known
53
+ ? known.whyFlagged
54
+ : `This code was flagged by the '${ruleId}' rule. ${finding.description || "Review the identified pattern for potential issues."}`;
55
+ const risk = known
56
+ ? known.risk
57
+ : `Potential ${finding.severity || "medium"}-severity issue that may affect code quality or security.`;
58
+ const action = known
59
+ ? known.action
60
+ : finding.recommendation || "Review and address the finding according to best practices.";
61
+ return {
62
+ finding: { ruleId, severity: finding.severity || "medium", title: finding.title },
63
+ context: finding.description || "",
64
+ explanation,
65
+ risk,
66
+ suggestedAction: action,
67
+ };
68
+ }
69
+ // ─── CLI ────────────────────────────────────────────────────────────────────
70
+ export function runDiffExplain(argv) {
71
+ if (argv.includes("--help") || argv.includes("-h")) {
72
+ console.log(`
73
+ judges diff-explain — Explain why changes were flagged
74
+
75
+ Usage:
76
+ judges diff-explain --input verdict.json
77
+ judges diff-explain --input verdict.json --rule sql-injection
78
+ judges diff-explain --input verdict.json --severity critical
79
+ judges diff-explain --format json
80
+
81
+ Options:
82
+ --input <file> TribunalVerdict JSON file (required)
83
+ --rule <id> Explain only findings for a specific rule
84
+ --severity <level> Filter by severity: critical, high, medium, low
85
+ --limit <n> Maximum findings to explain (default: 20)
86
+ --format json JSON output
87
+ --help, -h Show this help
88
+
89
+ Provides plain-language explanations of why code was flagged,
90
+ what the risk is, and what action to take. Useful for developers
91
+ unfamiliar with specific security or quality patterns.
92
+ `);
93
+ return;
94
+ }
95
+ const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
96
+ const ruleFilter = argv.find((_a, i) => argv[i - 1] === "--rule");
97
+ const sevFilter = argv.find((_a, i) => argv[i - 1] === "--severity");
98
+ const limitStr = argv.find((_a, i) => argv[i - 1] === "--limit");
99
+ const limit = limitStr ? parseInt(limitStr, 10) : 20;
100
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
101
+ if (!inputPath) {
102
+ console.error("Error: --input is required. Provide a verdict JSON file.");
103
+ process.exitCode = 1;
104
+ return;
105
+ }
106
+ let verdict;
107
+ try {
108
+ verdict = JSON.parse(readFileSync(inputPath, "utf-8"));
109
+ }
110
+ catch {
111
+ console.error(`Error: Cannot read or parse ${inputPath}`);
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ let findings = verdict.findings || [];
116
+ if (ruleFilter) {
117
+ findings = findings.filter((f) => f.ruleId === ruleFilter);
118
+ }
119
+ if (sevFilter) {
120
+ findings = findings.filter((f) => f.severity === sevFilter);
121
+ }
122
+ findings = findings.slice(0, limit);
123
+ if (findings.length === 0) {
124
+ console.log("No findings to explain.");
125
+ return;
126
+ }
127
+ const explanations = findings.map(explainFinding);
128
+ if (format === "json") {
129
+ console.log(JSON.stringify({ count: explanations.length, explanations }, null, 2));
130
+ return;
131
+ }
132
+ console.log(`\n Diff Explanations (${explanations.length} finding(s))\n ─────────────────────────────`);
133
+ for (const exp of explanations) {
134
+ const sevIcon = { critical: "🔴", high: "🟠", medium: "🟡", low: "🔵" };
135
+ const icon = sevIcon[exp.finding.severity] || "⬜";
136
+ console.log(`\n ${icon} [${exp.finding.severity}] ${exp.finding.ruleId}: ${exp.finding.title}`);
137
+ console.log(` Why flagged: ${exp.explanation}`);
138
+ console.log(` Risk: ${exp.risk}`);
139
+ console.log(` Action: ${exp.suggestedAction}`);
140
+ }
141
+ console.log();
142
+ }
143
+ //# sourceMappingURL=diff-explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-explain.js","sourceRoot":"","sources":["../../src/commands/diff-explain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Finding-group — Group related findings into actionable clusters.
3
+ */
4
+ import type { Finding } from "../types.js";
5
+ export interface FindingCluster {
6
+ id: string;
7
+ label: string;
8
+ pattern: string;
9
+ count: number;
10
+ severities: Record<string, number>;
11
+ findings: Finding[];
12
+ files: string[];
13
+ recommendation: string;
14
+ }
15
+ export declare function runFindingGroup(argv: string[]): void;
16
+ //# sourceMappingURL=finding-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-group.d.ts","sourceRoot":"","sources":["../../src/commands/finding-group.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAiFD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoGpD"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Finding-group — Group related findings into actionable clusters.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ // ─── Grouping strategies ────────────────────────────────────────────────────
6
+ function groupByRule(findings) {
7
+ const groups = new Map();
8
+ for (const f of findings) {
9
+ const key = f.ruleId || "unknown";
10
+ const existing = groups.get(key) || [];
11
+ existing.push(f);
12
+ groups.set(key, existing);
13
+ }
14
+ return groups;
15
+ }
16
+ function groupBySeverity(findings) {
17
+ const groups = new Map();
18
+ for (const f of findings) {
19
+ const key = f.severity || "unknown";
20
+ const existing = groups.get(key) || [];
21
+ existing.push(f);
22
+ groups.set(key, existing);
23
+ }
24
+ return groups;
25
+ }
26
+ function groupByCategory(findings) {
27
+ const groups = new Map();
28
+ for (const f of findings) {
29
+ const ruleId = f.ruleId || "";
30
+ let category = "other";
31
+ if (/sql|inject|xss|csrf|traversal|auth|secret|crypt/i.test(ruleId))
32
+ category = "security";
33
+ else if (/perf|cache|memory|leak|optim/i.test(ruleId))
34
+ category = "performance";
35
+ else if (/error|exception|throw|catch/i.test(ruleId))
36
+ category = "error-handling";
37
+ else if (/doc|comment|jsdoc|readme/i.test(ruleId))
38
+ category = "documentation";
39
+ else if (/test|assert|mock|coverage/i.test(ruleId))
40
+ category = "testing";
41
+ else if (/lint|format|naming|style/i.test(ruleId))
42
+ category = "code-style";
43
+ const existing = groups.get(category) || [];
44
+ existing.push(f);
45
+ groups.set(category, existing);
46
+ }
47
+ return groups;
48
+ }
49
+ // ─── Build clusters ─────────────────────────────────────────────────────────
50
+ function buildClusters(groups, strategy) {
51
+ const clusters = [];
52
+ let idx = 0;
53
+ for (const [key, findings] of groups) {
54
+ idx++;
55
+ const severities = {};
56
+ for (const f of findings) {
57
+ const s = f.severity || "unknown";
58
+ severities[s] = (severities[s] || 0) + 1;
59
+ }
60
+ const topFinding = findings.sort((a, b) => {
61
+ const order = { critical: 0, high: 1, medium: 2, low: 3 };
62
+ return (order[a.severity] ?? 4) - (order[b.severity] ?? 4);
63
+ })[0];
64
+ clusters.push({
65
+ id: `${strategy}-${idx}`,
66
+ label: key,
67
+ pattern: strategy,
68
+ count: findings.length,
69
+ severities,
70
+ findings,
71
+ files: [],
72
+ recommendation: topFinding?.recommendation || "Review grouped findings",
73
+ });
74
+ }
75
+ return clusters.sort((a, b) => b.count - a.count);
76
+ }
77
+ // ─── CLI ────────────────────────────────────────────────────────────────────
78
+ export function runFindingGroup(argv) {
79
+ if (argv.includes("--help") || argv.includes("-h")) {
80
+ console.log(`
81
+ judges finding-group — Group related findings into actionable clusters
82
+
83
+ Usage:
84
+ judges finding-group --input findings.json
85
+ judges finding-group --input findings.json --strategy severity
86
+ judges finding-group --input findings.json --format json
87
+
88
+ Options:
89
+ --input <file> JSON file with findings array (required)
90
+ --strategy <type> Grouping strategy: rule, severity, category (default: rule)
91
+ --format json JSON output
92
+ --min-count <n> Minimum findings to form a cluster (default: 1)
93
+ --help, -h Show this help
94
+
95
+ Strategies:
96
+ rule Group by rule ID (most specific)
97
+ severity Group by severity level
98
+ category Group by inferred category (security, performance, etc.)
99
+
100
+ Groups related findings to help you fix issues systematically rather
101
+ than one by one.
102
+ `);
103
+ return;
104
+ }
105
+ const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
106
+ const strategy = argv.find((_a, i) => argv[i - 1] === "--strategy") || "rule";
107
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
108
+ const minCountStr = argv.find((_a, i) => argv[i - 1] === "--min-count");
109
+ const minCount = minCountStr ? parseInt(minCountStr, 10) : 1;
110
+ if (!inputPath) {
111
+ console.error("Error: --input is required. Provide a JSON file with findings.");
112
+ process.exitCode = 1;
113
+ return;
114
+ }
115
+ let findings;
116
+ try {
117
+ const raw = readFileSync(inputPath, "utf-8");
118
+ const parsed = JSON.parse(raw);
119
+ findings = Array.isArray(parsed) ? parsed : parsed.findings || [];
120
+ }
121
+ catch {
122
+ console.error(`Error: Cannot read or parse ${inputPath}`);
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ if (findings.length === 0) {
127
+ console.log("No findings to group.");
128
+ return;
129
+ }
130
+ let groups;
131
+ switch (strategy) {
132
+ case "severity":
133
+ groups = groupBySeverity(findings);
134
+ break;
135
+ case "category":
136
+ groups = groupByCategory(findings);
137
+ break;
138
+ default:
139
+ groups = groupByRule(findings);
140
+ }
141
+ let clusters = buildClusters(groups, strategy);
142
+ clusters = clusters.filter((c) => c.count >= minCount);
143
+ if (format === "json") {
144
+ console.log(JSON.stringify({
145
+ strategy,
146
+ totalFindings: findings.length,
147
+ clusterCount: clusters.length,
148
+ clusters: clusters.map((c) => ({ ...c, findings: undefined })),
149
+ }, null, 2));
150
+ return;
151
+ }
152
+ console.log(`\n Finding Groups (strategy: ${strategy})\n ─────────────────────────────`);
153
+ console.log(` Total findings: ${findings.length}`);
154
+ console.log(` Clusters: ${clusters.length}\n`);
155
+ for (const cluster of clusters) {
156
+ const severitySummary = Object.entries(cluster.severities)
157
+ .map(([s, n]) => `${n} ${s}`)
158
+ .join(", ");
159
+ console.log(` [${cluster.id}] ${cluster.label} — ${cluster.count} finding(s)`);
160
+ console.log(` Severities: ${severitySummary}`);
161
+ console.log(` Recommendation: ${cluster.recommendation}`);
162
+ console.log();
163
+ }
164
+ }
165
+ //# sourceMappingURL=finding-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-group.js","sourceRoot":"","sources":["../../src/commands/finding-group.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Fix-suggest — Generate concrete code fix suggestions for findings.
3
+ */
4
+ export declare function runFixSuggest(argv: string[]): void;
5
+ //# sourceMappingURL=fix-suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-suggest.d.ts","sourceRoot":"","sources":["../../src/commands/fix-suggest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuHH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoFlD"}