@kevinrabun/judges 3.110.0 → 3.112.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 (78) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +126 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/finding-auto-priority.d.ts +2 -0
  6. package/dist/commands/finding-auto-priority.d.ts.map +1 -0
  7. package/dist/commands/finding-auto-priority.js +101 -0
  8. package/dist/commands/finding-auto-priority.js.map +1 -0
  9. package/dist/commands/finding-cluster-summary.d.ts +2 -0
  10. package/dist/commands/finding-cluster-summary.d.ts.map +1 -0
  11. package/dist/commands/finding-cluster-summary.js +86 -0
  12. package/dist/commands/finding-cluster-summary.js.map +1 -0
  13. package/dist/commands/finding-context-link.d.ts +2 -0
  14. package/dist/commands/finding-context-link.d.ts.map +1 -0
  15. package/dist/commands/finding-context-link.js +95 -0
  16. package/dist/commands/finding-context-link.js.map +1 -0
  17. package/dist/commands/finding-dependency-impact.d.ts +2 -0
  18. package/dist/commands/finding-dependency-impact.d.ts.map +1 -0
  19. package/dist/commands/finding-dependency-impact.js +98 -0
  20. package/dist/commands/finding-dependency-impact.js.map +1 -0
  21. package/dist/commands/finding-fix-estimate.d.ts +2 -0
  22. package/dist/commands/finding-fix-estimate.d.ts.map +1 -0
  23. package/dist/commands/finding-fix-estimate.js +96 -0
  24. package/dist/commands/finding-fix-estimate.js.map +1 -0
  25. package/dist/commands/finding-noise-score.d.ts +2 -0
  26. package/dist/commands/finding-noise-score.d.ts.map +1 -0
  27. package/dist/commands/finding-noise-score.js +94 -0
  28. package/dist/commands/finding-noise-score.js.map +1 -0
  29. package/dist/commands/finding-repeat-detect.d.ts +2 -0
  30. package/dist/commands/finding-repeat-detect.d.ts.map +1 -0
  31. package/dist/commands/finding-repeat-detect.js +93 -0
  32. package/dist/commands/finding-repeat-detect.js.map +1 -0
  33. package/dist/commands/finding-resolution-workflow.d.ts +2 -0
  34. package/dist/commands/finding-resolution-workflow.d.ts.map +1 -0
  35. package/dist/commands/finding-resolution-workflow.js +92 -0
  36. package/dist/commands/finding-resolution-workflow.js.map +1 -0
  37. package/dist/commands/finding-scope-impact.d.ts +2 -0
  38. package/dist/commands/finding-scope-impact.d.ts.map +1 -0
  39. package/dist/commands/finding-scope-impact.js +84 -0
  40. package/dist/commands/finding-scope-impact.js.map +1 -0
  41. package/dist/commands/finding-top-offender.d.ts +2 -0
  42. package/dist/commands/finding-top-offender.d.ts.map +1 -0
  43. package/dist/commands/finding-top-offender.js +76 -0
  44. package/dist/commands/finding-top-offender.js.map +1 -0
  45. package/dist/commands/review-health-trend.d.ts +2 -0
  46. package/dist/commands/review-health-trend.d.ts.map +1 -0
  47. package/dist/commands/review-health-trend.js +108 -0
  48. package/dist/commands/review-health-trend.js.map +1 -0
  49. package/dist/commands/review-mentor-suggest.d.ts +2 -0
  50. package/dist/commands/review-mentor-suggest.d.ts.map +1 -0
  51. package/dist/commands/review-mentor-suggest.js +113 -0
  52. package/dist/commands/review-mentor-suggest.js.map +1 -0
  53. package/dist/commands/review-quality-baseline.d.ts +2 -0
  54. package/dist/commands/review-quality-baseline.d.ts.map +1 -0
  55. package/dist/commands/review-quality-baseline.js +135 -0
  56. package/dist/commands/review-quality-baseline.js.map +1 -0
  57. package/dist/commands/review-readiness-check.d.ts +2 -0
  58. package/dist/commands/review-readiness-check.d.ts.map +1 -0
  59. package/dist/commands/review-readiness-check.js +99 -0
  60. package/dist/commands/review-readiness-check.js.map +1 -0
  61. package/dist/commands/review-retrospective.d.ts +2 -0
  62. package/dist/commands/review-retrospective.d.ts.map +1 -0
  63. package/dist/commands/review-retrospective.js +119 -0
  64. package/dist/commands/review-retrospective.js.map +1 -0
  65. package/dist/commands/review-team-skill-map.d.ts +2 -0
  66. package/dist/commands/review-team-skill-map.d.ts.map +1 -0
  67. package/dist/commands/review-team-skill-map.js +103 -0
  68. package/dist/commands/review-team-skill-map.js.map +1 -0
  69. package/dist/commands/review-team-velocity.d.ts +2 -0
  70. package/dist/commands/review-team-velocity.d.ts.map +1 -0
  71. package/dist/commands/review-team-velocity.js +104 -0
  72. package/dist/commands/review-team-velocity.js.map +1 -0
  73. package/dist/commands/review-workflow-suggest.d.ts +2 -0
  74. package/dist/commands/review-workflow-suggest.d.ts.map +1 -0
  75. package/dist/commands/review-workflow-suggest.js +130 -0
  76. package/dist/commands/review-workflow-suggest.js.map +1 -0
  77. package/package.json +1 -1
  78. package/server.json +2 -2
@@ -0,0 +1,94 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ function computeNoiseScore(finding) {
4
+ let score = 0;
5
+ const factors = [];
6
+ if (finding.confidence !== undefined && finding.confidence !== null && finding.confidence < 0.5) {
7
+ score += 30;
8
+ factors.push("low confidence");
9
+ }
10
+ if (finding.severity === "info") {
11
+ score += 20;
12
+ factors.push("info-level severity");
13
+ }
14
+ else if (finding.severity === "low") {
15
+ score += 10;
16
+ factors.push("low severity");
17
+ }
18
+ if (finding.patch === undefined || finding.patch === null) {
19
+ score += 15;
20
+ factors.push("no patch available");
21
+ }
22
+ if (finding.recommendation.length < 30) {
23
+ score += 15;
24
+ factors.push("vague recommendation");
25
+ }
26
+ if (factors.length === 0) {
27
+ factors.push("actionable finding");
28
+ }
29
+ return { score: Math.min(100, score), factors };
30
+ }
31
+ export function runFindingNoiseScore(argv) {
32
+ if (argv.includes("--help") || argv.includes("-h")) {
33
+ console.log(`Usage: judges finding-noise-score [options]
34
+
35
+ Score finding noise levels to identify low-signal findings.
36
+
37
+ Options:
38
+ --report <path> Path to verdict JSON
39
+ --threshold <n> Noise score threshold to flag (default: 40)
40
+ --format <fmt> Output format: table (default) or json
41
+ -h, --help Show this help message`);
42
+ return;
43
+ }
44
+ const formatIdx = argv.indexOf("--format");
45
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
46
+ const threshIdx = argv.indexOf("--threshold");
47
+ const threshold = threshIdx !== -1 && argv[threshIdx + 1] ? parseInt(argv[threshIdx + 1], 10) : 40;
48
+ const reportIdx = argv.indexOf("--report");
49
+ const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
50
+ ? join(process.cwd(), argv[reportIdx + 1])
51
+ : join(process.cwd(), ".judges", "last-verdict.json");
52
+ if (!existsSync(reportPath)) {
53
+ console.log(`No report found at: ${reportPath}`);
54
+ return;
55
+ }
56
+ const data = JSON.parse(readFileSync(reportPath, "utf-8"));
57
+ const findings = data.findings ?? [];
58
+ const entries = [];
59
+ for (const f of findings) {
60
+ const result = computeNoiseScore(f);
61
+ let noiseLevel;
62
+ if (result.score >= 60)
63
+ noiseLevel = "high noise";
64
+ else if (result.score >= 30)
65
+ noiseLevel = "moderate noise";
66
+ else
67
+ noiseLevel = "low noise";
68
+ entries.push({
69
+ ruleId: f.ruleId,
70
+ title: f.title,
71
+ severity: f.severity,
72
+ noiseScore: result.score,
73
+ noiseLevel,
74
+ factors: result.factors,
75
+ });
76
+ }
77
+ entries.sort((a, b) => b.noiseScore - a.noiseScore);
78
+ if (format === "json") {
79
+ console.log(JSON.stringify(entries, null, 2));
80
+ return;
81
+ }
82
+ const noisy = entries.filter((e) => e.noiseScore >= threshold);
83
+ console.log(`\n=== Noise Score (${noisy.length}/${entries.length} above threshold ${threshold}) ===\n`);
84
+ if (entries.length === 0) {
85
+ console.log("No findings to score.");
86
+ return;
87
+ }
88
+ for (const e of entries) {
89
+ const flag = e.noiseScore >= threshold ? " ⚠" : "";
90
+ console.log(` ${String(e.noiseScore).padStart(3)} ${e.noiseLevel.padEnd(16)} ${e.ruleId}${flag}`);
91
+ console.log(` ${e.factors.join(", ")}`);
92
+ }
93
+ }
94
+ //# sourceMappingURL=finding-noise-score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-noise-score.js","sourceRoot":"","sources":["../../src/commands/finding-noise-score.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runFindingRepeatDetect(argv: string[]): void;
2
+ //# sourceMappingURL=finding-repeat-detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-repeat-detect.d.ts","sourceRoot":"","sources":["../../src/commands/finding-repeat-detect.ts"],"names":[],"mappings":"AA4FA,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA4C3D"}
@@ -0,0 +1,93 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ function detectRepeats(historyDir) {
4
+ if (!existsSync(historyDir)) {
5
+ return { totalRepeats: 0, uniqueRepeatingRules: 0, entries: [] };
6
+ }
7
+ const files = readdirSync(historyDir);
8
+ const jsonFiles = files.filter((f) => String(f).endsWith(".json")).sort();
9
+ const ruleData = {};
10
+ for (const file of jsonFiles) {
11
+ const raw = readFileSync(join(historyDir, String(file)), "utf-8");
12
+ let verdict;
13
+ try {
14
+ verdict = JSON.parse(raw);
15
+ }
16
+ catch {
17
+ continue;
18
+ }
19
+ const period = String(file).replace(/\.json$/, "");
20
+ const seenInFile = new Set();
21
+ for (const f of verdict.findings ?? []) {
22
+ if (seenInFile.has(f.ruleId))
23
+ continue;
24
+ seenInFile.add(f.ruleId);
25
+ if (!ruleData[f.ruleId]) {
26
+ ruleData[f.ruleId] = {
27
+ occurrences: 0,
28
+ firstSeen: period,
29
+ lastSeen: period,
30
+ severity: f.severity,
31
+ title: f.title,
32
+ };
33
+ }
34
+ ruleData[f.ruleId].occurrences += 1;
35
+ ruleData[f.ruleId].lastSeen = period;
36
+ }
37
+ }
38
+ const entries = [];
39
+ for (const [ruleId, data] of Object.entries(ruleData)) {
40
+ if (data.occurrences > 1) {
41
+ entries.push({
42
+ ruleId,
43
+ occurrences: data.occurrences,
44
+ firstSeen: data.firstSeen,
45
+ lastSeen: data.lastSeen,
46
+ severity: data.severity,
47
+ sampleTitle: data.title,
48
+ });
49
+ }
50
+ }
51
+ entries.sort((a, b) => b.occurrences - a.occurrences);
52
+ return {
53
+ totalRepeats: entries.reduce((sum, e) => sum + e.occurrences, 0),
54
+ uniqueRepeatingRules: entries.length,
55
+ entries,
56
+ };
57
+ }
58
+ export function runFindingRepeatDetect(argv) {
59
+ if (argv.includes("--help") || argv.includes("-h")) {
60
+ console.log(`Usage: judges finding-repeat-detect [options]
61
+
62
+ Detect findings that repeat across multiple reviews.
63
+
64
+ Options:
65
+ --history <dir> Directory with verdict JSON files (default: .judges/history)
66
+ --format <fmt> Output format: table (default) or json
67
+ -h, --help Show this help message`);
68
+ return;
69
+ }
70
+ const formatIdx = argv.indexOf("--format");
71
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
72
+ const histIdx = argv.indexOf("--history");
73
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
74
+ ? join(process.cwd(), argv[histIdx + 1])
75
+ : join(process.cwd(), ".judges", "history");
76
+ const report = detectRepeats(historyDir);
77
+ if (format === "json") {
78
+ console.log(JSON.stringify(report, null, 2));
79
+ return;
80
+ }
81
+ console.log(`\n=== Repeat Detection (${report.uniqueRepeatingRules} repeating rules, ${report.totalRepeats} total occurrences) ===\n`);
82
+ if (report.entries.length === 0) {
83
+ console.log("No repeating findings detected.");
84
+ return;
85
+ }
86
+ for (const e of report.entries) {
87
+ console.log(` ${e.ruleId} — ${e.occurrences} occurrences [${e.severity}]`);
88
+ console.log(` First: ${e.firstSeen} Last: ${e.lastSeen}`);
89
+ console.log(` ${e.sampleTitle}`);
90
+ console.log();
91
+ }
92
+ }
93
+ //# sourceMappingURL=finding-repeat-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-repeat-detect.js","sourceRoot":"","sources":["../../src/commands/finding-repeat-detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runFindingResolutionWorkflow(argv: string[]): void;
2
+ //# sourceMappingURL=finding-resolution-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-resolution-workflow.d.ts","sourceRoot":"","sources":["../../src/commands/finding-resolution-workflow.ts"],"names":[],"mappings":"AA+EA,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6CjE"}
@@ -0,0 +1,92 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ const WORKFLOWS = {
4
+ critical: [
5
+ { step: 1, action: "Acknowledge finding and assign owner", required: true },
6
+ { step: 2, action: "Create incident ticket", required: true },
7
+ { step: 3, action: "Analyze root cause", required: true },
8
+ { step: 4, action: "Implement fix", required: true },
9
+ { step: 5, action: "Peer review the fix", required: true },
10
+ { step: 6, action: "Run regression tests", required: true },
11
+ { step: 7, action: "Deploy hotfix", required: true },
12
+ { step: 8, action: "Post-mortem review", required: false },
13
+ ],
14
+ high: [
15
+ { step: 1, action: "Assign to sprint backlog", required: true },
16
+ { step: 2, action: "Analyze root cause", required: true },
17
+ { step: 3, action: "Implement fix", required: true },
18
+ { step: 4, action: "Code review", required: true },
19
+ { step: 5, action: "Run tests", required: true },
20
+ { step: 6, action: "Deploy in next release", required: true },
21
+ ],
22
+ medium: [
23
+ { step: 1, action: "Add to backlog", required: true },
24
+ { step: 2, action: "Fix during regular development", required: true },
25
+ { step: 3, action: "Verify fix", required: true },
26
+ { step: 4, action: "Update documentation if needed", required: false },
27
+ ],
28
+ low: [
29
+ { step: 1, action: "Log for future cleanup", required: true },
30
+ { step: 2, action: "Fix during refactoring", required: false },
31
+ { step: 3, action: "Verify after fix", required: false },
32
+ ],
33
+ info: [
34
+ { step: 1, action: "Review and acknowledge", required: true },
35
+ { step: 2, action: "Address if convenient", required: false },
36
+ ],
37
+ };
38
+ function generateWorkflows(verdict) {
39
+ const results = [];
40
+ for (const f of verdict.findings ?? []) {
41
+ const steps = WORKFLOWS[f.severity] ?? WORKFLOWS["medium"];
42
+ results.push({
43
+ ruleId: f.ruleId,
44
+ title: f.title,
45
+ severity: f.severity,
46
+ workflow: `${f.severity}-resolution`,
47
+ steps,
48
+ });
49
+ }
50
+ const severityOrder = { critical: 5, high: 4, medium: 3, low: 2, info: 1 };
51
+ results.sort((a, b) => (severityOrder[b.severity] ?? 0) - (severityOrder[a.severity] ?? 0));
52
+ return results;
53
+ }
54
+ export function runFindingResolutionWorkflow(argv) {
55
+ if (argv.includes("--help") || argv.includes("-h")) {
56
+ console.log(`Usage: judges finding-resolution-workflow [options]
57
+
58
+ Generate resolution workflows for findings.
59
+
60
+ Options:
61
+ --report <path> Path to verdict JSON
62
+ --format <fmt> Output format: table (default) or json
63
+ -h, --help Show this help message`);
64
+ return;
65
+ }
66
+ const formatIdx = argv.indexOf("--format");
67
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
68
+ const reportIdx = argv.indexOf("--report");
69
+ const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
70
+ ? join(process.cwd(), argv[reportIdx + 1])
71
+ : join(process.cwd(), ".judges", "last-verdict.json");
72
+ if (!existsSync(reportPath)) {
73
+ console.log(`No report found at: ${reportPath}`);
74
+ return;
75
+ }
76
+ const data = JSON.parse(readFileSync(reportPath, "utf-8"));
77
+ const workflows = generateWorkflows(data);
78
+ if (format === "json") {
79
+ console.log(JSON.stringify(workflows, null, 2));
80
+ return;
81
+ }
82
+ console.log(`\n=== Resolution Workflows (${workflows.length} findings) ===\n`);
83
+ for (const wf of workflows) {
84
+ console.log(` [${wf.severity.toUpperCase()}] ${wf.ruleId}: ${wf.title}`);
85
+ for (const s of wf.steps) {
86
+ const req = s.required ? "*" : " ";
87
+ console.log(` ${req} ${s.step}. ${s.action}`);
88
+ }
89
+ console.log();
90
+ }
91
+ }
92
+ //# sourceMappingURL=finding-resolution-workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-resolution-workflow.js","sourceRoot":"","sources":["../../src/commands/finding-resolution-workflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runFindingScopeImpact(argv: string[]): void;
2
+ //# sourceMappingURL=finding-scope-impact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-scope-impact.d.ts","sourceRoot":"","sources":["../../src/commands/finding-scope-impact.ts"],"names":[],"mappings":"AA0DA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqD1D"}
@@ -0,0 +1,84 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ function extractDomain(ruleId) {
4
+ const parts = ruleId.split("/");
5
+ return parts.length > 1 ? parts[0] : "general";
6
+ }
7
+ function analyseScope(verdict) {
8
+ const findings = verdict.findings ?? [];
9
+ const domainCounts = {};
10
+ for (const f of findings) {
11
+ const domain = extractDomain(f.ruleId);
12
+ domainCounts[domain] = (domainCounts[domain] ?? 0) + 1;
13
+ }
14
+ const entries = [];
15
+ for (const f of findings) {
16
+ const domain = extractDomain(f.ruleId);
17
+ const count = domainCounts[domain] ?? 1;
18
+ let impactScope;
19
+ if (count >= 10)
20
+ impactScope = "systemic";
21
+ else if (count >= 5)
22
+ impactScope = "widespread";
23
+ else if (count >= 2)
24
+ impactScope = "moderate";
25
+ else
26
+ impactScope = "isolated";
27
+ entries.push({
28
+ ruleId: f.ruleId,
29
+ title: f.title,
30
+ severity: f.severity,
31
+ domain,
32
+ domainCount: count,
33
+ impactScope,
34
+ });
35
+ }
36
+ entries.sort((a, b) => b.domainCount - a.domainCount);
37
+ return entries;
38
+ }
39
+ export function runFindingScopeImpact(argv) {
40
+ if (argv.includes("--help") || argv.includes("-h")) {
41
+ console.log(`Usage: judges finding-scope-impact [options]
42
+
43
+ Analyse the scope of finding impact across domains.
44
+
45
+ Options:
46
+ --report <path> Path to verdict JSON
47
+ --format <fmt> Output format: table (default) or json
48
+ -h, --help Show this help message`);
49
+ return;
50
+ }
51
+ const formatIdx = argv.indexOf("--format");
52
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
53
+ const reportIdx = argv.indexOf("--report");
54
+ const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
55
+ ? join(process.cwd(), argv[reportIdx + 1])
56
+ : join(process.cwd(), ".judges", "last-verdict.json");
57
+ if (!existsSync(reportPath)) {
58
+ console.log(`No report found at: ${reportPath}`);
59
+ return;
60
+ }
61
+ const data = JSON.parse(readFileSync(reportPath, "utf-8"));
62
+ const entries = analyseScope(data);
63
+ if (format === "json") {
64
+ console.log(JSON.stringify(entries, null, 2));
65
+ return;
66
+ }
67
+ console.log(`\n=== Finding Scope Impact (${entries.length} findings) ===\n`);
68
+ if (entries.length === 0) {
69
+ console.log("No findings to analyse.");
70
+ return;
71
+ }
72
+ const seen = new Set();
73
+ for (const e of entries) {
74
+ if (!seen.has(e.domain)) {
75
+ seen.add(e.domain);
76
+ console.log(` [${e.domain}] ${e.domainCount} findings — ${e.impactScope} scope`);
77
+ }
78
+ }
79
+ console.log();
80
+ for (const e of entries) {
81
+ console.log(` ${e.impactScope.padEnd(12)} [${e.severity}] ${e.ruleId}: ${e.title}`);
82
+ }
83
+ }
84
+ //# sourceMappingURL=finding-scope-impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-scope-impact.js","sourceRoot":"","sources":["../../src/commands/finding-scope-impact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runFindingTopOffender(argv: string[]): void;
2
+ //# sourceMappingURL=finding-top-offender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-top-offender.d.ts","sourceRoot":"","sources":["../../src/commands/finding-top-offender.ts"],"names":[],"mappings":"AA+CA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAyD1D"}
@@ -0,0 +1,76 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ function findTopOffenders(verdict) {
4
+ const findings = verdict.findings ?? [];
5
+ if (findings.length === 0)
6
+ return [];
7
+ const ruleMap = {};
8
+ for (const f of findings) {
9
+ if (!ruleMap[f.ruleId]) {
10
+ ruleMap[f.ruleId] = { count: 0, severities: {}, sampleTitle: f.title };
11
+ }
12
+ ruleMap[f.ruleId].count += 1;
13
+ ruleMap[f.ruleId].severities[f.severity] = (ruleMap[f.ruleId].severities[f.severity] ?? 0) + 1;
14
+ }
15
+ const entries = [];
16
+ for (const [ruleId, data] of Object.entries(ruleMap)) {
17
+ entries.push({
18
+ ruleId,
19
+ count: data.count,
20
+ severities: data.severities,
21
+ sampleTitle: data.sampleTitle,
22
+ percentage: Math.round((data.count / findings.length) * 100),
23
+ });
24
+ }
25
+ entries.sort((a, b) => b.count - a.count);
26
+ return entries;
27
+ }
28
+ export function runFindingTopOffender(argv) {
29
+ if (argv.includes("--help") || argv.includes("-h")) {
30
+ console.log(`Usage: judges finding-top-offender [options]
31
+
32
+ Identify the most frequently triggered rules.
33
+
34
+ Options:
35
+ --report <path> Path to verdict JSON
36
+ --top <n> Number of top offenders to show (default: 10)
37
+ --format <fmt> Output format: table (default) or json
38
+ -h, --help Show this help message`);
39
+ return;
40
+ }
41
+ const formatIdx = argv.indexOf("--format");
42
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
43
+ const topIdx = argv.indexOf("--top");
44
+ const topN = topIdx !== -1 && argv[topIdx + 1] ? parseInt(argv[topIdx + 1], 10) : 10;
45
+ const reportIdx = argv.indexOf("--report");
46
+ const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
47
+ ? join(process.cwd(), argv[reportIdx + 1])
48
+ : join(process.cwd(), ".judges", "last-verdict.json");
49
+ if (!existsSync(reportPath)) {
50
+ console.log(`No report found at: ${reportPath}`);
51
+ return;
52
+ }
53
+ const data = JSON.parse(readFileSync(reportPath, "utf-8"));
54
+ const offenders = findTopOffenders(data);
55
+ const shown = offenders.slice(0, topN);
56
+ if (format === "json") {
57
+ console.log(JSON.stringify(shown, null, 2));
58
+ return;
59
+ }
60
+ console.log(`\n=== Top Offenders (${shown.length}/${offenders.length} rules) ===\n`);
61
+ if (shown.length === 0) {
62
+ console.log("No findings to analyse.");
63
+ return;
64
+ }
65
+ for (let i = 0; i < shown.length; i++) {
66
+ const e = shown[i];
67
+ const severityStr = Object.entries(e.severities)
68
+ .map(([s, n]) => `${s}:${n}`)
69
+ .join(" ");
70
+ console.log(` #${i + 1} ${e.ruleId} — ${e.count} findings (${e.percentage}%)`);
71
+ console.log(` Severities: ${severityStr}`);
72
+ console.log(` Example: ${e.sampleTitle}`);
73
+ console.log();
74
+ }
75
+ }
76
+ //# sourceMappingURL=finding-top-offender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-top-offender.js","sourceRoot":"","sources":["../../src/commands/finding-top-offender.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewHealthTrend(argv: string[]): void;
2
+ //# sourceMappingURL=review-health-trend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-health-trend.d.ts","sourceRoot":"","sources":["../../src/commands/review-health-trend.ts"],"names":[],"mappings":"AAgGA,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA0CzD"}
@@ -0,0 +1,108 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ function computeHealthScore(verdict) {
4
+ const findings = verdict.findings ?? [];
5
+ if (findings.length === 0)
6
+ return 100;
7
+ let penalty = 0;
8
+ for (const f of findings) {
9
+ if (f.severity === "critical")
10
+ penalty += 15;
11
+ else if (f.severity === "high")
12
+ penalty += 10;
13
+ else if (f.severity === "medium")
14
+ penalty += 5;
15
+ else if (f.severity === "low")
16
+ penalty += 2;
17
+ else
18
+ penalty += 1;
19
+ }
20
+ return Math.max(0, 100 - penalty);
21
+ }
22
+ function buildTrend(historyDir) {
23
+ if (!existsSync(historyDir)) {
24
+ return { snapshots: [], trend: "unknown", currentScore: 0, averageScore: 0 };
25
+ }
26
+ const files = readdirSync(historyDir);
27
+ const jsonFiles = files.filter((f) => String(f).endsWith(".json")).sort();
28
+ const snapshots = [];
29
+ for (const file of jsonFiles) {
30
+ const raw = readFileSync(join(historyDir, String(file)), "utf-8");
31
+ let verdict;
32
+ try {
33
+ verdict = JSON.parse(raw);
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ const score = computeHealthScore(verdict);
39
+ const findings = verdict.findings ?? [];
40
+ const criticals = findings.filter((f) => f.severity === "critical").length;
41
+ const passRate = verdict.overallVerdict === "pass" ? 100 : 0;
42
+ snapshots.push({
43
+ period: String(file).replace(/\.json$/, ""),
44
+ score,
45
+ findingCount: findings.length,
46
+ criticalCount: criticals,
47
+ passRate,
48
+ });
49
+ }
50
+ if (snapshots.length === 0) {
51
+ return { snapshots, trend: "unknown", currentScore: 0, averageScore: 0 };
52
+ }
53
+ const currentScore = snapshots[snapshots.length - 1].score;
54
+ const averageScore = Math.round(snapshots.reduce((sum, s) => sum + s.score, 0) / snapshots.length);
55
+ let trend;
56
+ if (snapshots.length < 3) {
57
+ trend = "insufficient data";
58
+ }
59
+ else {
60
+ const recent = snapshots.slice(-3);
61
+ const first = recent[0].score;
62
+ const last = recent[recent.length - 1].score;
63
+ if (last > first + 5)
64
+ trend = "improving";
65
+ else if (last < first - 5)
66
+ trend = "declining";
67
+ else
68
+ trend = "stable";
69
+ }
70
+ return { snapshots, trend, currentScore, averageScore };
71
+ }
72
+ export function runReviewHealthTrend(argv) {
73
+ if (argv.includes("--help") || argv.includes("-h")) {
74
+ console.log(`Usage: judges review-health-trend [options]
75
+
76
+ Track review health over time with composite scoring.
77
+
78
+ Options:
79
+ --history <dir> Directory with verdict JSON files (default: .judges/history)
80
+ --format <fmt> Output format: table (default) or json
81
+ -h, --help Show this help message`);
82
+ return;
83
+ }
84
+ const formatIdx = argv.indexOf("--format");
85
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
86
+ const histIdx = argv.indexOf("--history");
87
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
88
+ ? join(process.cwd(), argv[histIdx + 1])
89
+ : join(process.cwd(), ".judges", "history");
90
+ const result = buildTrend(historyDir);
91
+ if (format === "json") {
92
+ console.log(JSON.stringify(result, null, 2));
93
+ return;
94
+ }
95
+ console.log(`\n=== Review Health Trend ===\n`);
96
+ console.log(` Current Score: ${result.currentScore}/100`);
97
+ console.log(` Average Score: ${result.averageScore}/100`);
98
+ console.log(` Trend: ${result.trend}`);
99
+ console.log(` Data Points: ${result.snapshots.length}`);
100
+ if (result.snapshots.length > 0) {
101
+ console.log();
102
+ for (const s of result.snapshots) {
103
+ const bar = "█".repeat(Math.round(s.score / 5));
104
+ console.log(` ${s.period.padEnd(20)} ${String(s.score).padStart(3)}/100 ${bar}`);
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=review-health-trend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-health-trend.js","sourceRoot":"","sources":["../../src/commands/review-health-trend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewMentorSuggest(argv: string[]): void;
2
+ //# sourceMappingURL=review-mentor-suggest.d.ts.map