@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 @@
1
+ {"version":3,"file":"review-mentor-suggest.d.ts","sourceRoot":"","sources":["../../src/commands/review-mentor-suggest.ts"],"names":[],"mappings":"AA2GA,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAgD3D"}
@@ -0,0 +1,113 @@
1
+ import { readFileSync, existsSync, readdirSync } 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 buildProfiles(historyDir) {
8
+ if (!existsSync(historyDir))
9
+ return [];
10
+ const files = readdirSync(historyDir);
11
+ const jsonFiles = files.filter((f) => String(f).endsWith(".json"));
12
+ const domainScores = {};
13
+ for (const file of jsonFiles) {
14
+ const raw = readFileSync(join(historyDir, String(file)), "utf-8");
15
+ let verdict;
16
+ try {
17
+ verdict = JSON.parse(raw);
18
+ }
19
+ catch {
20
+ continue;
21
+ }
22
+ const reviewer = String(file)
23
+ .replace(/\.json$/, "")
24
+ .replace(/[-_]\d+$/, "");
25
+ if (!domainScores[reviewer])
26
+ domainScores[reviewer] = {};
27
+ const domainCounts = {};
28
+ for (const f of verdict.findings ?? []) {
29
+ const domain = extractDomain(f.ruleId);
30
+ domainCounts[domain] = (domainCounts[domain] ?? 0) + 1;
31
+ }
32
+ for (const domain of Object.keys(domainCounts)) {
33
+ if (!domainScores[reviewer][domain]) {
34
+ domainScores[reviewer][domain] = { total: 0, findings: 0 };
35
+ }
36
+ domainScores[reviewer][domain].total += 1;
37
+ domainScores[reviewer][domain].findings += domainCounts[domain];
38
+ }
39
+ }
40
+ const profiles = [];
41
+ for (const [reviewer, domains] of Object.entries(domainScores)) {
42
+ const strengths = [];
43
+ const weaknesses = [];
44
+ for (const [domain, stats] of Object.entries(domains)) {
45
+ const avgFindings = stats.findings / stats.total;
46
+ if (avgFindings <= 1) {
47
+ strengths.push(domain);
48
+ }
49
+ else if (avgFindings >= 3) {
50
+ weaknesses.push(domain);
51
+ }
52
+ }
53
+ profiles.push({ reviewer, strengths, weaknesses });
54
+ }
55
+ return profiles;
56
+ }
57
+ function suggestPairings(profiles) {
58
+ const pairings = [];
59
+ for (const mentee of profiles) {
60
+ for (const weakness of mentee.weaknesses) {
61
+ const mentor = profiles.find((p) => p.reviewer !== mentee.reviewer && p.strengths.includes(weakness));
62
+ if (mentor) {
63
+ pairings.push({
64
+ mentee: mentee.reviewer,
65
+ mentor: mentor.reviewer,
66
+ domain: weakness,
67
+ reason: `${mentor.reviewer} shows strength in "${weakness}" where ${mentee.reviewer} has frequent findings`,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ return pairings;
73
+ }
74
+ export function runReviewMentorSuggest(argv) {
75
+ if (argv.includes("--help") || argv.includes("-h")) {
76
+ console.log(`Usage: judges review-mentor-suggest [options]
77
+
78
+ Suggest mentor pairings based on expertise gaps in review history.
79
+
80
+ Options:
81
+ --history <dir> Directory with verdict JSON files (default: .judges/history)
82
+ --format <fmt> Output format: table (default) or json
83
+ -h, --help Show this help message`);
84
+ return;
85
+ }
86
+ const formatIdx = argv.indexOf("--format");
87
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
88
+ const histIdx = argv.indexOf("--history");
89
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
90
+ ? join(process.cwd(), argv[histIdx + 1])
91
+ : join(process.cwd(), ".judges", "history");
92
+ const profiles = buildProfiles(historyDir);
93
+ if (profiles.length === 0) {
94
+ console.log("No reviewer profiles found. Run some reviews first.");
95
+ return;
96
+ }
97
+ const pairings = suggestPairings(profiles);
98
+ if (format === "json") {
99
+ console.log(JSON.stringify({ profiles, pairings }, null, 2));
100
+ return;
101
+ }
102
+ console.log(`\n=== Mentor Suggestions (${pairings.length} pairings) ===\n`);
103
+ if (pairings.length === 0) {
104
+ console.log("No clear mentor pairings identified — team expertise is well-distributed.");
105
+ return;
106
+ }
107
+ for (const p of pairings) {
108
+ console.log(` ${p.mentee} <-- ${p.mentor} (domain: ${p.domain})`);
109
+ console.log(` ${p.reason}`);
110
+ console.log();
111
+ }
112
+ }
113
+ //# sourceMappingURL=review-mentor-suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-mentor-suggest.js","sourceRoot":"","sources":["../../src/commands/review-mentor-suggest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewQualityBaseline(argv: string[]): void;
2
+ //# sourceMappingURL=review-quality-baseline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-quality-baseline.d.ts","sourceRoot":"","sources":["../../src/commands/review-quality-baseline.ts"],"names":[],"mappings":"AAoHA,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAkE7D"}
@@ -0,0 +1,135 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ function computeBaseline(historyDir) {
4
+ if (!existsSync(historyDir))
5
+ return undefined;
6
+ const files = readdirSync(historyDir)
7
+ .filter((f) => typeof f === "string" && f.endsWith(".json"))
8
+ .sort();
9
+ if (files.length === 0)
10
+ return undefined;
11
+ const scores = [];
12
+ const findingCounts = [];
13
+ const criticalCounts = [];
14
+ const highCounts = [];
15
+ let passes = 0;
16
+ for (const file of files) {
17
+ try {
18
+ const data = JSON.parse(readFileSync(join(historyDir, file), "utf-8"));
19
+ scores.push(data.overallScore ?? 0);
20
+ findingCounts.push((data.findings ?? []).length);
21
+ criticalCounts.push(data.criticalCount ?? 0);
22
+ highCounts.push(data.highCount ?? 0);
23
+ if (data.overallVerdict === "pass")
24
+ passes++;
25
+ }
26
+ catch {
27
+ // Skip
28
+ }
29
+ }
30
+ if (scores.length === 0)
31
+ return undefined;
32
+ const avg = (arr) => Math.round(arr.reduce((a, b) => a + b, 0) / arr.length);
33
+ return {
34
+ avgScore: avg(scores),
35
+ avgFindings: avg(findingCounts),
36
+ avgCritical: avg(criticalCounts),
37
+ avgHigh: avg(highCounts),
38
+ passRate: Math.round((passes / scores.length) * 100),
39
+ };
40
+ }
41
+ function compareToBaseline(baseline, current) {
42
+ const comparisons = [];
43
+ const curScore = current.overallScore ?? 0;
44
+ const curFindings = (current.findings ?? []).length;
45
+ const curCritical = current.criticalCount ?? 0;
46
+ const curHigh = current.highCount ?? 0;
47
+ function status(delta, higherIsBetter) {
48
+ if (delta === 0)
49
+ return "On baseline";
50
+ if (higherIsBetter)
51
+ return delta > 0 ? "Above baseline" : "Below baseline";
52
+ return delta < 0 ? "Better than baseline" : "Worse than baseline";
53
+ }
54
+ comparisons.push({
55
+ metric: "Score",
56
+ baseline: baseline.avgScore,
57
+ current: curScore,
58
+ delta: curScore - baseline.avgScore,
59
+ status: status(curScore - baseline.avgScore, true),
60
+ });
61
+ comparisons.push({
62
+ metric: "Findings",
63
+ baseline: baseline.avgFindings,
64
+ current: curFindings,
65
+ delta: curFindings - baseline.avgFindings,
66
+ status: status(curFindings - baseline.avgFindings, false),
67
+ });
68
+ comparisons.push({
69
+ metric: "Critical",
70
+ baseline: baseline.avgCritical,
71
+ current: curCritical,
72
+ delta: curCritical - baseline.avgCritical,
73
+ status: status(curCritical - baseline.avgCritical, false),
74
+ });
75
+ comparisons.push({
76
+ metric: "High",
77
+ baseline: baseline.avgHigh,
78
+ current: curHigh,
79
+ delta: curHigh - baseline.avgHigh,
80
+ status: status(curHigh - baseline.avgHigh, false),
81
+ });
82
+ return comparisons;
83
+ }
84
+ export function runReviewQualityBaseline(argv) {
85
+ if (argv.includes("--help") || argv.includes("-h")) {
86
+ console.log(`Usage: judges review-quality-baseline [options]
87
+
88
+ Compare current review against quality baseline.
89
+
90
+ Options:
91
+ --report <path> Path to current verdict JSON
92
+ --history <path> Path to review history directory
93
+ --format <fmt> Output format: table (default) or json
94
+ -h, --help Show this help message`);
95
+ return;
96
+ }
97
+ const formatIdx = argv.indexOf("--format");
98
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
99
+ const reportIdx = argv.indexOf("--report");
100
+ const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
101
+ ? join(process.cwd(), argv[reportIdx + 1])
102
+ : join(process.cwd(), ".judges", "last-verdict.json");
103
+ const histIdx = argv.indexOf("--history");
104
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
105
+ ? join(process.cwd(), argv[histIdx + 1])
106
+ : join(process.cwd(), ".judges", "history");
107
+ const baseline = computeBaseline(historyDir);
108
+ if (baseline === undefined) {
109
+ console.log("No review history to compute baseline. Run more reviews first.");
110
+ return;
111
+ }
112
+ if (!existsSync(reportPath)) {
113
+ console.log(`No report found at: ${reportPath}`);
114
+ return;
115
+ }
116
+ const current = JSON.parse(readFileSync(reportPath, "utf-8"));
117
+ const comparisons = compareToBaseline(baseline, current);
118
+ if (format === "json") {
119
+ console.log(JSON.stringify({ baseline, comparisons }, null, 2));
120
+ return;
121
+ }
122
+ console.log(`\n=== Quality Baseline Comparison ===\n`);
123
+ console.log(" " + "Metric".padEnd(12) + "Baseline".padEnd(12) + "Current".padEnd(12) + "Delta".padEnd(10) + "Status");
124
+ console.log(" " + "-".repeat(55));
125
+ for (const c of comparisons) {
126
+ const deltaStr = c.delta >= 0 ? `+${c.delta}` : String(c.delta);
127
+ console.log(" " +
128
+ c.metric.padEnd(12) +
129
+ String(c.baseline).padEnd(12) +
130
+ String(c.current).padEnd(12) +
131
+ deltaStr.padEnd(10) +
132
+ c.status);
133
+ }
134
+ }
135
+ //# sourceMappingURL=review-quality-baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-quality-baseline.js","sourceRoot":"","sources":["../../src/commands/review-quality-baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewReadinessCheck(argv: string[]): void;
2
+ //# sourceMappingURL=review-readiness-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-readiness-check.d.ts","sourceRoot":"","sources":["../../src/commands/review-readiness-check.ts"],"names":[],"mappings":"AA4FA,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuC5D"}
@@ -0,0 +1,99 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ function checkReadiness(baseDir) {
4
+ const items = [];
5
+ const configPath = join(baseDir, ".judgesrc.json");
6
+ const configExists = existsSync(configPath);
7
+ items.push({
8
+ criterion: "Configuration file",
9
+ passed: configExists,
10
+ detail: configExists ? ".judgesrc.json found" : ".judgesrc.json not found — run judges init",
11
+ });
12
+ const baselinePath = join(baseDir, ".judges", "baseline.json");
13
+ const baselineExists = existsSync(baselinePath);
14
+ items.push({
15
+ criterion: "Baseline available",
16
+ passed: baselineExists,
17
+ detail: baselineExists ? "Baseline found — suppressions will work" : "No baseline — run judges baseline first",
18
+ });
19
+ const historyDir = join(baseDir, ".judges", "history");
20
+ let historyCount = 0;
21
+ if (existsSync(historyDir)) {
22
+ const files = readdirSync(historyDir);
23
+ historyCount = files.filter((f) => String(f).endsWith(".json")).length;
24
+ }
25
+ items.push({
26
+ criterion: "Review history",
27
+ passed: historyCount >= 1,
28
+ detail: historyCount >= 1 ? `${historyCount} prior reviews found` : "No review history — first-time setup",
29
+ });
30
+ const lastVerdictPath = join(baseDir, ".judges", "last-verdict.json");
31
+ let lastVerdictFresh = false;
32
+ if (existsSync(lastVerdictPath)) {
33
+ try {
34
+ const raw = readFileSync(lastVerdictPath, "utf-8");
35
+ const verdict = JSON.parse(raw);
36
+ if (verdict.timestamp) {
37
+ const age = Date.now() - new Date(verdict.timestamp).getTime();
38
+ lastVerdictFresh = age < 7 * 24 * 60 * 60 * 1000;
39
+ }
40
+ }
41
+ catch {
42
+ /* ignore parse errors */
43
+ }
44
+ }
45
+ items.push({
46
+ criterion: "Recent verdict",
47
+ passed: lastVerdictFresh,
48
+ detail: lastVerdictFresh ? "Last verdict is less than 7 days old" : "No recent verdict — run judges review",
49
+ });
50
+ const gitDir = join(baseDir, ".git");
51
+ const gitExists = existsSync(gitDir);
52
+ items.push({
53
+ criterion: "Git repository",
54
+ passed: gitExists,
55
+ detail: gitExists ? "Git repo detected" : "Not a git repository — diff features limited",
56
+ });
57
+ const passCount = items.filter((i) => i.passed).length;
58
+ return {
59
+ ready: passCount === items.length,
60
+ passCount,
61
+ totalChecks: items.length,
62
+ items,
63
+ };
64
+ }
65
+ export function runReviewReadinessCheck(argv) {
66
+ if (argv.includes("--help") || argv.includes("-h")) {
67
+ console.log(`Usage: judges review-readiness-check [options]
68
+
69
+ Assess whether a codebase is ready for review.
70
+
71
+ Options:
72
+ --dir <path> Project directory (default: cwd)
73
+ --format <fmt> Output format: table (default) or json
74
+ -h, --help Show this help message`);
75
+ return;
76
+ }
77
+ const formatIdx = argv.indexOf("--format");
78
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
79
+ const dirIdx = argv.indexOf("--dir");
80
+ const baseDir = dirIdx !== -1 && argv[dirIdx + 1] ? join(process.cwd(), argv[dirIdx + 1]) : process.cwd();
81
+ const report = checkReadiness(baseDir);
82
+ if (format === "json") {
83
+ console.log(JSON.stringify(report, null, 2));
84
+ return;
85
+ }
86
+ console.log(`\n=== Review Readiness Check (${report.passCount}/${report.totalChecks}) ===\n`);
87
+ for (const item of report.items) {
88
+ const icon = item.passed ? "✓" : "✗";
89
+ console.log(` ${icon} ${item.criterion.padEnd(24)} ${item.detail}`);
90
+ }
91
+ console.log();
92
+ if (report.ready) {
93
+ console.log(" All checks passed — ready for review.");
94
+ }
95
+ else {
96
+ console.log(" Some checks failed — address issues above before review.");
97
+ }
98
+ }
99
+ //# sourceMappingURL=review-readiness-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-readiness-check.js","sourceRoot":"","sources":["../../src/commands/review-readiness-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewRetrospective(argv: string[]): void;
2
+ //# sourceMappingURL=review-retrospective.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-retrospective.d.ts","sourceRoot":"","sources":["../../src/commands/review-retrospective.ts"],"names":[],"mappings":"AA0GA,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA2C3D"}
@@ -0,0 +1,119 @@
1
+ import { readFileSync, existsSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ function generateRetro(historyDir) {
4
+ const empty = {
5
+ period: "No data",
6
+ reviewCount: 0,
7
+ sections: [],
8
+ };
9
+ if (!existsSync(historyDir))
10
+ return empty;
11
+ const files = readdirSync(historyDir)
12
+ .filter((f) => typeof f === "string" && f.endsWith(".json"))
13
+ .sort();
14
+ if (files.length === 0)
15
+ return empty;
16
+ const verdicts = [];
17
+ for (const file of files) {
18
+ try {
19
+ verdicts.push(JSON.parse(readFileSync(join(historyDir, file), "utf-8")));
20
+ }
21
+ catch {
22
+ // Skip
23
+ }
24
+ }
25
+ if (verdicts.length === 0)
26
+ return empty;
27
+ const scores = verdicts.map((v) => v.overallScore ?? 0);
28
+ const avgScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
29
+ const passes = verdicts.filter((v) => v.overallVerdict === "pass").length;
30
+ const passRate = Math.round((passes / verdicts.length) * 100);
31
+ // Score trend
32
+ const mid = Math.floor(scores.length / 2) || 1;
33
+ const firstAvg = scores.slice(0, mid).reduce((a, b) => a + b, 0) / mid;
34
+ const secondAvg = scores.slice(mid).reduce((a, b) => a + b, 0) / (scores.length - mid);
35
+ const improving = secondAvg >= firstAvg;
36
+ // Most common rules
37
+ const ruleFreq = new Map();
38
+ for (const v of verdicts) {
39
+ for (const f of v.findings ?? []) {
40
+ ruleFreq.set(f.ruleId, (ruleFreq.get(f.ruleId) ?? 0) + 1);
41
+ }
42
+ }
43
+ const topRules = [...ruleFreq.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
44
+ // Build sections
45
+ const wentWell = [];
46
+ const needsImprovement = [];
47
+ const actionItems = [];
48
+ if (passRate >= 80)
49
+ wentWell.push(`High pass rate: ${passRate}%`);
50
+ else
51
+ needsImprovement.push(`Pass rate below threshold: ${passRate}%`);
52
+ if (improving)
53
+ wentWell.push(`Scores trending upward (${Math.round(firstAvg)} → ${Math.round(secondAvg)})`);
54
+ else
55
+ needsImprovement.push(`Scores trending downward (${Math.round(firstAvg)} → ${Math.round(secondAvg)})`);
56
+ if (avgScore >= 70)
57
+ wentWell.push(`Good average score: ${avgScore}/100`);
58
+ else
59
+ needsImprovement.push(`Average score needs work: ${avgScore}/100`);
60
+ if (topRules.length > 0) {
61
+ needsImprovement.push(`Most recurring rules: ${topRules.map(([r, c]) => `${r} (${c}x)`).join(", ")}`);
62
+ actionItems.push(`Address top recurring rule: ${topRules[0][0]}`);
63
+ }
64
+ if (!improving)
65
+ actionItems.push("Investigate declining score trend");
66
+ if (passRate < 80)
67
+ actionItems.push("Focus on reducing critical/high findings to improve pass rate");
68
+ actionItems.push("Review and update severity thresholds if too noisy");
69
+ const sections = [
70
+ { category: "What Went Well", items: wentWell },
71
+ { category: "Needs Improvement", items: needsImprovement },
72
+ { category: "Action Items", items: actionItems },
73
+ ];
74
+ const firstDate = verdicts[0].timestamp ?? "unknown";
75
+ const lastDate = verdicts[verdicts.length - 1].timestamp ?? "unknown";
76
+ return {
77
+ period: `${firstDate.slice(0, 10)} to ${lastDate.slice(0, 10)}`,
78
+ reviewCount: verdicts.length,
79
+ sections,
80
+ };
81
+ }
82
+ export function runReviewRetrospective(argv) {
83
+ if (argv.includes("--help") || argv.includes("-h")) {
84
+ console.log(`Usage: judges review-retrospective [options]
85
+
86
+ Generate review retrospective summary.
87
+
88
+ Options:
89
+ --history <path> Path to review history directory
90
+ --format <fmt> Output format: table (default) or json
91
+ -h, --help Show this help message`);
92
+ return;
93
+ }
94
+ const formatIdx = argv.indexOf("--format");
95
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
96
+ const histIdx = argv.indexOf("--history");
97
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
98
+ ? join(process.cwd(), argv[histIdx + 1])
99
+ : join(process.cwd(), ".judges", "history");
100
+ const retro = generateRetro(historyDir);
101
+ if (format === "json") {
102
+ console.log(JSON.stringify(retro, null, 2));
103
+ return;
104
+ }
105
+ console.log(`\n=== Review Retrospective ===`);
106
+ console.log(` Period: ${retro.period}`);
107
+ console.log(` Reviews: ${retro.reviewCount}\n`);
108
+ for (const section of retro.sections) {
109
+ console.log(` ${section.category}:`);
110
+ if (section.items.length === 0) {
111
+ console.log(" (none)");
112
+ }
113
+ for (const item of section.items) {
114
+ console.log(` • ${item}`);
115
+ }
116
+ console.log();
117
+ }
118
+ }
119
+ //# sourceMappingURL=review-retrospective.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-retrospective.js","sourceRoot":"","sources":["../../src/commands/review-retrospective.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runReviewTeamSkillMap(argv: string[]): void;
2
+ //# sourceMappingURL=review-team-skill-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-team-skill-map.d.ts","sourceRoot":"","sources":["../../src/commands/review-team-skill-map.ts"],"names":[],"mappings":"AAmGA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8C1D"}
@@ -0,0 +1,103 @@
1
+ import { readFileSync, existsSync, readdirSync } 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 buildSkillMaps(historyDir) {
8
+ if (!existsSync(historyDir))
9
+ return [];
10
+ const files = readdirSync(historyDir);
11
+ const jsonFiles = files.filter((f) => String(f).endsWith(".json"));
12
+ const memberData = {};
13
+ for (const file of jsonFiles) {
14
+ const member = String(file)
15
+ .replace(/\.json$/, "")
16
+ .replace(/[-_]\d+$/, "");
17
+ const raw = readFileSync(join(historyDir, String(file)), "utf-8");
18
+ let verdict;
19
+ try {
20
+ verdict = JSON.parse(raw);
21
+ }
22
+ catch {
23
+ continue;
24
+ }
25
+ if (!memberData[member])
26
+ memberData[member] = {};
27
+ const domainCounts = {};
28
+ for (const f of verdict.findings ?? []) {
29
+ const domain = extractDomain(f.ruleId);
30
+ domainCounts[domain] = (domainCounts[domain] ?? 0) + 1;
31
+ }
32
+ const allDomains = new Set(Object.keys(domainCounts));
33
+ if (allDomains.size === 0)
34
+ allDomains.add("general");
35
+ for (const domain of allDomains) {
36
+ if (!memberData[member][domain]) {
37
+ memberData[member][domain] = { reviews: 0, findings: 0 };
38
+ }
39
+ memberData[member][domain].reviews += 1;
40
+ memberData[member][domain].findings += domainCounts[domain] ?? 0;
41
+ }
42
+ }
43
+ const maps = [];
44
+ for (const [member, domains] of Object.entries(memberData)) {
45
+ const skills = [];
46
+ for (const [domain, stats] of Object.entries(domains)) {
47
+ const rate = stats.reviews > 0 ? stats.findings / stats.reviews : 0;
48
+ let level;
49
+ if (rate <= 0.5)
50
+ level = "expert";
51
+ else if (rate <= 1.5)
52
+ level = "proficient";
53
+ else if (rate <= 3)
54
+ level = "developing";
55
+ else
56
+ level = "beginner";
57
+ skills.push({ domain, level, findingRate: Math.round(rate * 10) / 10, reviewCount: stats.reviews });
58
+ }
59
+ skills.sort((a, b) => a.findingRate - b.findingRate);
60
+ const strongest = skills.length > 0 ? skills[0].domain : "none";
61
+ const weakest = skills.length > 0 ? skills[skills.length - 1].domain : "none";
62
+ maps.push({ member, skills, strongestDomain: strongest, weakestDomain: weakest });
63
+ }
64
+ return maps;
65
+ }
66
+ export function runReviewTeamSkillMap(argv) {
67
+ if (argv.includes("--help") || argv.includes("-h")) {
68
+ console.log(`Usage: judges review-team-skill-map [options]
69
+
70
+ Build a team skill map from review history.
71
+
72
+ Options:
73
+ --history <dir> Directory with verdict JSON files (default: .judges/history)
74
+ --format <fmt> Output format: table (default) or json
75
+ -h, --help Show this help message`);
76
+ return;
77
+ }
78
+ const formatIdx = argv.indexOf("--format");
79
+ const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
80
+ const histIdx = argv.indexOf("--history");
81
+ const historyDir = histIdx !== -1 && argv[histIdx + 1]
82
+ ? join(process.cwd(), argv[histIdx + 1])
83
+ : join(process.cwd(), ".judges", "history");
84
+ const maps = buildSkillMaps(historyDir);
85
+ if (format === "json") {
86
+ console.log(JSON.stringify(maps, null, 2));
87
+ return;
88
+ }
89
+ console.log(`\n=== Team Skill Map (${maps.length} members) ===\n`);
90
+ if (maps.length === 0) {
91
+ console.log("No review history found. Run some reviews first.");
92
+ return;
93
+ }
94
+ for (const m of maps) {
95
+ console.log(` ${m.member}`);
96
+ console.log(` Strongest: ${m.strongestDomain} | Weakest: ${m.weakestDomain}`);
97
+ for (const s of m.skills) {
98
+ console.log(` ${s.domain.padEnd(20)} ${s.level.padEnd(12)} (rate: ${s.findingRate}, reviews: ${s.reviewCount})`);
99
+ }
100
+ console.log();
101
+ }
102
+ }
103
+ //# sourceMappingURL=review-team-skill-map.js.map