@kevinrabun/judges 3.65.0 → 3.67.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.
- package/CHANGELOG.md +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/config-lint.d.ts +5 -0
- package/dist/commands/config-lint.d.ts.map +1 -0
- package/dist/commands/config-lint.js +188 -0
- package/dist/commands/config-lint.js.map +1 -0
- package/dist/commands/finding-age.d.ts +5 -0
- package/dist/commands/finding-age.d.ts.map +1 -0
- package/dist/commands/finding-age.js +146 -0
- package/dist/commands/finding-age.js.map +1 -0
- package/dist/commands/finding-rank.d.ts +5 -0
- package/dist/commands/finding-rank.d.ts.map +1 -0
- package/dist/commands/finding-rank.js +139 -0
- package/dist/commands/finding-rank.js.map +1 -0
- package/dist/commands/finding-timeline.d.ts +5 -0
- package/dist/commands/finding-timeline.d.ts.map +1 -0
- package/dist/commands/finding-timeline.js +144 -0
- package/dist/commands/finding-timeline.js.map +1 -0
- package/dist/commands/fix-verify.d.ts +5 -0
- package/dist/commands/fix-verify.d.ts.map +1 -0
- package/dist/commands/fix-verify.js +124 -0
- package/dist/commands/fix-verify.js.map +1 -0
- package/dist/commands/review-comment.d.ts +5 -0
- package/dist/commands/review-comment.d.ts.map +1 -0
- package/dist/commands/review-comment.js +166 -0
- package/dist/commands/review-comment.js.map +1 -0
- package/dist/commands/review-dashboard.d.ts +5 -0
- package/dist/commands/review-dashboard.d.ts.map +1 -0
- package/dist/commands/review-dashboard.js +141 -0
- package/dist/commands/review-dashboard.js.map +1 -0
- package/dist/commands/review-diff-summary.d.ts +5 -0
- package/dist/commands/review-diff-summary.d.ts.map +1 -0
- package/dist/commands/review-diff-summary.js +155 -0
- package/dist/commands/review-diff-summary.js.map +1 -0
- package/dist/commands/review-export.d.ts +5 -0
- package/dist/commands/review-export.d.ts.map +1 -0
- package/dist/commands/review-export.js +180 -0
- package/dist/commands/review-export.js.map +1 -0
- package/dist/commands/review-notify.d.ts +5 -0
- package/dist/commands/review-notify.d.ts.map +1 -0
- package/dist/commands/review-notify.js +144 -0
- package/dist/commands/review-notify.js.map +1 -0
- package/dist/commands/review-offline.d.ts +5 -0
- package/dist/commands/review-offline.d.ts.map +1 -0
- package/dist/commands/review-offline.js +126 -0
- package/dist/commands/review-offline.js.map +1 -0
- package/dist/commands/review-quota.d.ts +5 -0
- package/dist/commands/review-quota.d.ts.map +1 -0
- package/dist/commands/review-quota.js +127 -0
- package/dist/commands/review-quota.js.map +1 -0
- package/dist/commands/review-schedule.d.ts +5 -0
- package/dist/commands/review-schedule.d.ts.map +1 -0
- package/dist/commands/review-schedule.js +170 -0
- package/dist/commands/review-schedule.js.map +1 -0
- package/dist/commands/review-scope.d.ts +5 -0
- package/dist/commands/review-scope.d.ts.map +1 -0
- package/dist/commands/review-scope.js +198 -0
- package/dist/commands/review-scope.js.map +1 -0
- package/dist/commands/rule-catalog.d.ts +5 -0
- package/dist/commands/rule-catalog.d.ts.map +1 -0
- package/dist/commands/rule-catalog.js +129 -0
- package/dist/commands/rule-catalog.js.map +1 -0
- package/dist/commands/setup-wizard.d.ts +5 -0
- package/dist/commands/setup-wizard.d.ts.map +1 -0
- package/dist/commands/setup-wizard.js +175 -0
- package/dist/commands/setup-wizard.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-timeline — Track finding trends across commits over time.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { join, dirname } from "path";
|
|
7
|
+
// ─── Storage ────────────────────────────────────────────────────────────────
|
|
8
|
+
const TIMELINE_FILE = join(".judges", "finding-timeline.json");
|
|
9
|
+
function loadTimeline() {
|
|
10
|
+
if (!existsSync(TIMELINE_FILE))
|
|
11
|
+
return { version: "1.0.0", entries: [] };
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(TIMELINE_FILE, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return { version: "1.0.0", entries: [] };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function saveTimeline(store) {
|
|
20
|
+
mkdirSync(dirname(TIMELINE_FILE), { recursive: true });
|
|
21
|
+
writeFileSync(TIMELINE_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
22
|
+
}
|
|
23
|
+
function countBySeverity(findings) {
|
|
24
|
+
const counts = {};
|
|
25
|
+
for (const f of findings) {
|
|
26
|
+
const sev = f.severity || "unknown";
|
|
27
|
+
counts[sev] = (counts[sev] || 0) + 1;
|
|
28
|
+
}
|
|
29
|
+
return counts;
|
|
30
|
+
}
|
|
31
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
32
|
+
export function runFindingTimeline(argv) {
|
|
33
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
34
|
+
console.log(`
|
|
35
|
+
judges finding-timeline — Track finding trends across commits
|
|
36
|
+
|
|
37
|
+
Usage:
|
|
38
|
+
judges finding-timeline record --file verdict.json --label "v1.0"
|
|
39
|
+
judges finding-timeline show
|
|
40
|
+
judges finding-timeline show --last 10
|
|
41
|
+
judges finding-timeline clear
|
|
42
|
+
|
|
43
|
+
Subcommands:
|
|
44
|
+
record Record a data point from a verdict
|
|
45
|
+
show Show timeline with trend visualization
|
|
46
|
+
clear Clear all timeline data
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--file <path> Verdict JSON (for record)
|
|
50
|
+
--label <text> Label for this data point
|
|
51
|
+
--commit <hash> Git commit hash (auto-detected if omitted)
|
|
52
|
+
--last <n> Show only last N entries
|
|
53
|
+
--format json JSON output
|
|
54
|
+
--help, -h Show this help
|
|
55
|
+
|
|
56
|
+
Tracks findings over time to show improvement trends.
|
|
57
|
+
Data is stored locally in .judges/finding-timeline.json.
|
|
58
|
+
`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
62
|
+
const subcommand = argv.find((a) => ["record", "show", "clear"].includes(a)) || "show";
|
|
63
|
+
const store = loadTimeline();
|
|
64
|
+
if (subcommand === "record") {
|
|
65
|
+
const file = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
66
|
+
if (!file || !existsSync(file)) {
|
|
67
|
+
console.error("Error: --file with a valid verdict JSON is required.");
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let verdict;
|
|
72
|
+
try {
|
|
73
|
+
verdict = JSON.parse(readFileSync(file, "utf-8"));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
console.error(`Error: Could not parse ${file}`);
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const label = argv.find((_a, i) => argv[i - 1] === "--label") || `entry-${store.entries.length + 1}`;
|
|
81
|
+
let commit = argv.find((_a, i) => argv[i - 1] === "--commit") || "";
|
|
82
|
+
if (!commit) {
|
|
83
|
+
try {
|
|
84
|
+
commit = execSync("git rev-parse --short HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
commit = "unknown";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const findings = verdict.findings || [];
|
|
91
|
+
const entry = {
|
|
92
|
+
id: `tl-${Date.now().toString(36)}`,
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
commit,
|
|
95
|
+
label,
|
|
96
|
+
totalFindings: findings.length,
|
|
97
|
+
bySeverity: countBySeverity(findings),
|
|
98
|
+
score: verdict.overallScore || 0,
|
|
99
|
+
};
|
|
100
|
+
store.entries.push(entry);
|
|
101
|
+
saveTimeline(store);
|
|
102
|
+
console.log(`Recorded timeline entry '${label}' — ${findings.length} findings, score ${entry.score}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (subcommand === "clear") {
|
|
106
|
+
saveTimeline({ version: "1.0.0", entries: [] });
|
|
107
|
+
console.log("Timeline cleared.");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Show
|
|
111
|
+
const lastN = parseInt(argv.find((_a, i) => argv[i - 1] === "--last") || "0", 10);
|
|
112
|
+
const entries = lastN > 0 ? store.entries.slice(-lastN) : store.entries;
|
|
113
|
+
if (format === "json") {
|
|
114
|
+
console.log(JSON.stringify({ total: store.entries.length, shown: entries.length, entries }, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(`\n Finding Timeline (${entries.length} entries)\n ═════════════════════════════`);
|
|
118
|
+
if (entries.length === 0) {
|
|
119
|
+
console.log(" No data. Record with: judges finding-timeline record --file verdict.json");
|
|
120
|
+
console.log();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// ASCII chart
|
|
124
|
+
const maxFindings = Math.max(...entries.map((e) => e.totalFindings), 1);
|
|
125
|
+
const barWidth = 30;
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
const barLen = Math.round((entry.totalFindings / maxFindings) * barWidth);
|
|
128
|
+
const bar = "█".repeat(barLen) + "░".repeat(barWidth - barLen);
|
|
129
|
+
const date = entry.timestamp.slice(0, 10);
|
|
130
|
+
console.log(` ${date} ${entry.label.padEnd(15)} ${bar} ${entry.totalFindings} findings (score: ${entry.score})`);
|
|
131
|
+
}
|
|
132
|
+
// Show trend
|
|
133
|
+
if (entries.length >= 2) {
|
|
134
|
+
const first = entries[0];
|
|
135
|
+
const last = entries[entries.length - 1];
|
|
136
|
+
const delta = last.totalFindings - first.totalFindings;
|
|
137
|
+
const scoreDelta = last.score - first.score;
|
|
138
|
+
console.log();
|
|
139
|
+
console.log(` Trend: findings ${delta >= 0 ? "+" : ""}${delta}, score ${scoreDelta >= 0 ? "+" : ""}${scoreDelta}`);
|
|
140
|
+
console.log(` ${delta <= 0 && scoreDelta >= 0 ? "📈 Improving" : delta > 0 ? "📉 Declining" : "➡️ Stable"}`);
|
|
141
|
+
}
|
|
142
|
+
console.log();
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=finding-timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-timeline.js","sourceRoot":"","sources":["../../src/commands/finding-timeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-verify.d.ts","sourceRoot":"","sources":["../../src/commands/fix-verify.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwDH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAoGjD"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix-verify — Re-run review on fixed code to confirm findings are resolved.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { dirname } from "path";
|
|
6
|
+
// ─── Matching ───────────────────────────────────────────────────────────────
|
|
7
|
+
function findingKey(f) {
|
|
8
|
+
return [f.ruleId || "", f.title || "", String(f.severity || "")].join("|").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function compareVerdicts(original, updated) {
|
|
11
|
+
const origFindings = original.findings || [];
|
|
12
|
+
const updFindings = updated.findings || [];
|
|
13
|
+
const origKeys = new Set(origFindings.map(findingKey));
|
|
14
|
+
const updKeys = new Set(updFindings.map(findingKey));
|
|
15
|
+
const resolved = origFindings.filter((f) => !updKeys.has(findingKey(f)));
|
|
16
|
+
const remaining = origFindings.filter((f) => updKeys.has(findingKey(f)));
|
|
17
|
+
const newFindings = updFindings.filter((f) => !origKeys.has(findingKey(f)));
|
|
18
|
+
const resolutionRate = origFindings.length > 0 ? Math.round((resolved.length / origFindings.length) * 100) : 100;
|
|
19
|
+
return {
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
originalFile: "",
|
|
22
|
+
originalCount: origFindings.length,
|
|
23
|
+
resolvedCount: resolved.length,
|
|
24
|
+
remainingCount: remaining.length,
|
|
25
|
+
newCount: newFindings.length,
|
|
26
|
+
resolved,
|
|
27
|
+
remaining,
|
|
28
|
+
newFindings,
|
|
29
|
+
resolutionRate,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
33
|
+
export function runFixVerify(argv) {
|
|
34
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
35
|
+
console.log(`
|
|
36
|
+
judges fix-verify — Verify that fixes resolved findings
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
judges fix-verify --original before.json --updated after.json
|
|
40
|
+
judges fix-verify --original before.json --updated after.json --output report.json
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--original <path> Original verdict JSON (before fixes)
|
|
44
|
+
--updated <path> Updated verdict JSON (after fixes)
|
|
45
|
+
--output <path> Write verification report to file
|
|
46
|
+
--format json JSON output
|
|
47
|
+
--help, -h Show this help
|
|
48
|
+
|
|
49
|
+
Compares two verdict files to show which findings were resolved,
|
|
50
|
+
which remain, and whether new findings were introduced.
|
|
51
|
+
`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
55
|
+
const originalFile = argv.find((_a, i) => argv[i - 1] === "--original");
|
|
56
|
+
const updatedFile = argv.find((_a, i) => argv[i - 1] === "--updated");
|
|
57
|
+
const outputFile = argv.find((_a, i) => argv[i - 1] === "--output");
|
|
58
|
+
if (!originalFile || !updatedFile) {
|
|
59
|
+
console.error("Error: Both --original and --updated are required.");
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!existsSync(originalFile)) {
|
|
64
|
+
console.error(`Error: File not found: ${originalFile}`);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!existsSync(updatedFile)) {
|
|
69
|
+
console.error(`Error: File not found: ${updatedFile}`);
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let original;
|
|
74
|
+
let updated;
|
|
75
|
+
try {
|
|
76
|
+
original = JSON.parse(readFileSync(originalFile, "utf-8"));
|
|
77
|
+
updated = JSON.parse(readFileSync(updatedFile, "utf-8"));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
console.error("Error: Could not parse verdict files.");
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const result = compareVerdicts(original, updated);
|
|
85
|
+
result.originalFile = originalFile;
|
|
86
|
+
if (outputFile) {
|
|
87
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
88
|
+
writeFileSync(outputFile, JSON.stringify(result, null, 2), "utf-8");
|
|
89
|
+
console.log(`Verification report written to ${outputFile}`);
|
|
90
|
+
}
|
|
91
|
+
if (format === "json") {
|
|
92
|
+
console.log(JSON.stringify(result, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`\n Fix Verification Report\n ═════════════════════════════`);
|
|
96
|
+
console.log(` Original findings: ${result.originalCount}`);
|
|
97
|
+
console.log(` Resolved: ${result.resolvedCount} ✅`);
|
|
98
|
+
console.log(` Remaining: ${result.remainingCount} ⚠️`);
|
|
99
|
+
console.log(` New findings: ${result.newCount} ${result.newCount > 0 ? "🆕" : ""}`);
|
|
100
|
+
console.log(` Resolution rate: ${result.resolutionRate}%`);
|
|
101
|
+
console.log();
|
|
102
|
+
if (result.resolved.length > 0) {
|
|
103
|
+
console.log(" Resolved Findings:");
|
|
104
|
+
for (const f of result.resolved) {
|
|
105
|
+
console.log(` ✅ [${(f.severity || "").toUpperCase()}] ${f.title || f.ruleId}`);
|
|
106
|
+
}
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
if (result.remaining.length > 0) {
|
|
110
|
+
console.log(" Remaining Findings:");
|
|
111
|
+
for (const f of result.remaining) {
|
|
112
|
+
console.log(` ⚠️ [${(f.severity || "").toUpperCase()}] ${f.title || f.ruleId}`);
|
|
113
|
+
}
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
|
116
|
+
if (result.newFindings.length > 0) {
|
|
117
|
+
console.log(" New Findings (introduced by fixes):");
|
|
118
|
+
for (const f of result.newFindings) {
|
|
119
|
+
console.log(` 🆕 [${(f.severity || "").toUpperCase()}] ${f.title || f.ruleId}`);
|
|
120
|
+
}
|
|
121
|
+
console.log();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=fix-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-verify.js","sourceRoot":"","sources":["../../src/commands/fix-verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-comment.d.ts","sourceRoot":"","sources":["../../src/commands/review-comment.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8EH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAwHrD"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-comment — Generate inline source code comments from review findings.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
5
|
+
const COMMENT_STYLES = {
|
|
6
|
+
javascript: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
7
|
+
typescript: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
8
|
+
python: { line: "#", blockStart: '"""', blockEnd: '"""' },
|
|
9
|
+
ruby: { line: "#", blockStart: "=begin", blockEnd: "=end" },
|
|
10
|
+
java: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
11
|
+
csharp: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
12
|
+
go: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
13
|
+
rust: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
14
|
+
cpp: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
15
|
+
c: { line: "//", blockStart: "/*", blockEnd: "*/" },
|
|
16
|
+
};
|
|
17
|
+
function getCommentStyle(lang) {
|
|
18
|
+
return COMMENT_STYLES[lang.toLowerCase()] || COMMENT_STYLES["javascript"];
|
|
19
|
+
}
|
|
20
|
+
function formatComment(finding, style, commentStyle) {
|
|
21
|
+
const severity = (finding.severity || "info").toUpperCase();
|
|
22
|
+
const rule = finding.ruleId || "JUDGES";
|
|
23
|
+
const title = finding.title || "Finding";
|
|
24
|
+
if (commentStyle === "line") {
|
|
25
|
+
const lines = [`${style.line} JUDGES [${severity}] ${rule}: ${title}`];
|
|
26
|
+
if (finding.recommendation) {
|
|
27
|
+
lines.push(`${style.line} Fix: ${finding.recommendation}`);
|
|
28
|
+
}
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
if (commentStyle === "jsdoc") {
|
|
32
|
+
const lines = [`/** JUDGES [${severity}] ${rule}: ${title}`];
|
|
33
|
+
if (finding.recommendation) {
|
|
34
|
+
lines.push(` * Fix: ${finding.recommendation}`);
|
|
35
|
+
}
|
|
36
|
+
lines.push(" */");
|
|
37
|
+
return lines.join("\n");
|
|
38
|
+
}
|
|
39
|
+
// block
|
|
40
|
+
const lines = [`/* JUDGES [${severity}] ${rule}: ${title}`];
|
|
41
|
+
if (finding.recommendation) {
|
|
42
|
+
lines.push(` Fix: ${finding.recommendation}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push("*/");
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
function generateComments(findings, lang, style) {
|
|
48
|
+
const cs = getCommentStyle(lang);
|
|
49
|
+
const blocks = [];
|
|
50
|
+
for (const f of findings) {
|
|
51
|
+
const line = f.lineNumbers && f.lineNumbers.length > 0 ? f.lineNumbers[0] : 1;
|
|
52
|
+
blocks.push({ line, text: formatComment(f, cs, style) });
|
|
53
|
+
}
|
|
54
|
+
// Sort by line descending so insertions don't shift subsequent lines
|
|
55
|
+
blocks.sort((a, b) => b.line - a.line);
|
|
56
|
+
return blocks;
|
|
57
|
+
}
|
|
58
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
59
|
+
export function runReviewComment(argv) {
|
|
60
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
61
|
+
console.log(`
|
|
62
|
+
judges review-comment — Generate inline code comments from findings
|
|
63
|
+
|
|
64
|
+
Usage:
|
|
65
|
+
judges review-comment --verdict verdict.json --source app.ts
|
|
66
|
+
judges review-comment --verdict verdict.json --source app.py --lang python
|
|
67
|
+
judges review-comment --verdict verdict.json --preview
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--verdict <path> Verdict JSON file
|
|
71
|
+
--source <path> Source file to annotate (optional; preview without)
|
|
72
|
+
--lang <language> Language (auto-detected from extension)
|
|
73
|
+
--style line|block|jsdoc Comment style (default: line)
|
|
74
|
+
--output <path> Write annotated file to path (default: modifies in-place)
|
|
75
|
+
--preview Show comments without modifying files
|
|
76
|
+
--min-severity <sev> Only include findings at this severity or above
|
|
77
|
+
--format json JSON output
|
|
78
|
+
--help, -h Show this help
|
|
79
|
+
|
|
80
|
+
Generates inline code comments from review findings. Comments include
|
|
81
|
+
severity, rule ID, title, and fix recommendation.
|
|
82
|
+
`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
86
|
+
const verdictFile = argv.find((_a, i) => argv[i - 1] === "--verdict");
|
|
87
|
+
const sourceFile = argv.find((_a, i) => argv[i - 1] === "--source");
|
|
88
|
+
const lang = argv.find((_a, i) => argv[i - 1] === "--lang") || "";
|
|
89
|
+
const styleArg = argv.find((_a, i) => argv[i - 1] === "--style") || "line";
|
|
90
|
+
const outputFile = argv.find((_a, i) => argv[i - 1] === "--output");
|
|
91
|
+
const preview = argv.includes("--preview");
|
|
92
|
+
const minSev = argv.find((_a, i) => argv[i - 1] === "--min-severity");
|
|
93
|
+
if (!verdictFile) {
|
|
94
|
+
console.error("Error: --verdict is required.");
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!existsSync(verdictFile)) {
|
|
99
|
+
console.error(`Error: File not found: ${verdictFile}`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
let verdict;
|
|
104
|
+
try {
|
|
105
|
+
verdict = JSON.parse(readFileSync(verdictFile, "utf-8"));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
console.error(`Error: Could not parse ${verdictFile}`);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let findings = verdict.findings || [];
|
|
113
|
+
// Severity filter
|
|
114
|
+
if (minSev) {
|
|
115
|
+
const sevOrder = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };
|
|
116
|
+
const threshold = sevOrder[minSev.toLowerCase()] ?? 0;
|
|
117
|
+
findings = findings.filter((f) => (sevOrder[(f.severity || "").toLowerCase()] ?? 0) >= threshold);
|
|
118
|
+
}
|
|
119
|
+
// Detect language from source file extension
|
|
120
|
+
const detectedLang = lang || (sourceFile ? sourceFile.split(".").pop() || "ts" : "ts");
|
|
121
|
+
const languageMap = {
|
|
122
|
+
ts: "typescript",
|
|
123
|
+
js: "javascript",
|
|
124
|
+
py: "python",
|
|
125
|
+
rb: "ruby",
|
|
126
|
+
java: "java",
|
|
127
|
+
cs: "csharp",
|
|
128
|
+
go: "go",
|
|
129
|
+
rs: "rust",
|
|
130
|
+
cpp: "cpp",
|
|
131
|
+
c: "c",
|
|
132
|
+
};
|
|
133
|
+
const resolvedLang = languageMap[detectedLang] || detectedLang;
|
|
134
|
+
const commentStyle = (["line", "block", "jsdoc"].includes(styleArg) ? styleArg : "line");
|
|
135
|
+
const comments = generateComments(findings, resolvedLang, commentStyle);
|
|
136
|
+
if (format === "json") {
|
|
137
|
+
console.log(JSON.stringify({ language: resolvedLang, style: commentStyle, comments }, null, 2));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (preview || !sourceFile) {
|
|
141
|
+
console.log(`\n Review Comments Preview (${resolvedLang}, ${commentStyle} style)\n ─────────────────────────────`);
|
|
142
|
+
for (const c of [...comments].reverse()) {
|
|
143
|
+
console.log(`\n Line ${c.line}:`);
|
|
144
|
+
for (const line of c.text.split("\n")) {
|
|
145
|
+
console.log(` ${line}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
console.log(`\n Total: ${comments.length} comment(s)\n`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Insert comments into source file
|
|
152
|
+
if (!existsSync(sourceFile)) {
|
|
153
|
+
console.error(`Error: Source file not found: ${sourceFile}`);
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const sourceLines = readFileSync(sourceFile, "utf-8").split("\n");
|
|
158
|
+
for (const c of comments) {
|
|
159
|
+
const insertAt = Math.max(0, c.line - 1);
|
|
160
|
+
sourceLines.splice(insertAt, 0, c.text);
|
|
161
|
+
}
|
|
162
|
+
const dest = outputFile || sourceFile;
|
|
163
|
+
writeFileSync(dest, sourceLines.join("\n"), "utf-8");
|
|
164
|
+
console.log(`Inserted ${comments.length} comment(s) into ${dest}`);
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=review-comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-comment.js","sourceRoot":"","sources":["../../src/commands/review-comment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-dashboard.d.ts","sourceRoot":"","sources":["../../src/commands/review-dashboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqJvD"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-dashboard — Terminal-based dashboard summary of review health.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
7
|
+
export function runReviewDashboard(argv) {
|
|
8
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
9
|
+
console.log(`
|
|
10
|
+
judges review-dashboard — Terminal dashboard of review health
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
judges review-dashboard Show dashboard
|
|
14
|
+
judges review-dashboard --dir ./results From verdict directory
|
|
15
|
+
judges review-dashboard --file verdict.json From single file
|
|
16
|
+
judges review-dashboard --format json JSON output
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--file <path> Single verdict file
|
|
20
|
+
--dir <directory> Directory with verdict JSON files
|
|
21
|
+
--format json JSON output
|
|
22
|
+
--help, -h Show this help
|
|
23
|
+
|
|
24
|
+
Displays a summary dashboard with key metrics: score,
|
|
25
|
+
findings by severity, trends, and actionable insights.
|
|
26
|
+
`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
30
|
+
const file = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
31
|
+
const dir = argv.find((_a, i) => argv[i - 1] === "--dir");
|
|
32
|
+
const verdicts = [];
|
|
33
|
+
if (file && existsSync(file)) {
|
|
34
|
+
try {
|
|
35
|
+
verdicts.push(JSON.parse(readFileSync(file, "utf-8")));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* skip */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (dir && existsSync(dir)) {
|
|
42
|
+
try {
|
|
43
|
+
const entries = readdirSync(dir);
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (typeof entry === "string" && entry.endsWith(".json")) {
|
|
46
|
+
try {
|
|
47
|
+
verdicts.push(JSON.parse(readFileSync(join(dir, entry), "utf-8")));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* skip */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* skip */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (verdicts.length === 0) {
|
|
60
|
+
console.log("\n No verdict data found. Use --file or --dir to provide verdict JSON files.\n");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Compute metrics
|
|
64
|
+
const scores = verdicts.map((v) => v.overallScore || 0);
|
|
65
|
+
const avgScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
|
|
66
|
+
const totalFindings = verdicts.reduce((sum, v) => sum + (v.findings || []).length, 0);
|
|
67
|
+
const severityCounts = {};
|
|
68
|
+
for (const v of verdicts) {
|
|
69
|
+
for (const f of v.findings || []) {
|
|
70
|
+
const sev = f.severity || "unknown";
|
|
71
|
+
severityCounts[sev] = (severityCounts[sev] || 0) + 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const criticalCount = severityCounts["critical"] || 0;
|
|
75
|
+
const highCount = severityCounts["high"] || 0;
|
|
76
|
+
const mediumCount = severityCounts["medium"] || 0;
|
|
77
|
+
const lowCount = severityCounts["low"] || 0;
|
|
78
|
+
const passCount = verdicts.filter((v) => v.overallVerdict === "pass").length;
|
|
79
|
+
const failCount = verdicts.filter((v) => v.overallVerdict === "fail").length;
|
|
80
|
+
const grade = avgScore >= 90 ? "A" : avgScore >= 80 ? "B" : avgScore >= 70 ? "C" : avgScore >= 60 ? "D" : "F";
|
|
81
|
+
if (format === "json") {
|
|
82
|
+
console.log(JSON.stringify({
|
|
83
|
+
reviewCount: verdicts.length,
|
|
84
|
+
avgScore,
|
|
85
|
+
grade,
|
|
86
|
+
totalFindings,
|
|
87
|
+
severityCounts,
|
|
88
|
+
passCount,
|
|
89
|
+
failCount,
|
|
90
|
+
}, null, 2));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const barLen = Math.round((avgScore / 100) * 20);
|
|
94
|
+
const scoreBar = "█".repeat(barLen) + "░".repeat(20 - barLen);
|
|
95
|
+
console.log(`
|
|
96
|
+
╔═══════════════════════════════════════════╗
|
|
97
|
+
║ JUDGES REVIEW DASHBOARD ║
|
|
98
|
+
╚═══════════════════════════════════════════╝
|
|
99
|
+
|
|
100
|
+
Score: ${scoreBar} ${avgScore}/100 (Grade ${grade})
|
|
101
|
+
|
|
102
|
+
┌─────────────────────────────────────────┐
|
|
103
|
+
│ Reviews: ${String(verdicts.length).padEnd(6)} Pass: ${String(passCount).padEnd(6)} Fail: ${String(failCount).padEnd(4)}│
|
|
104
|
+
│ Findings: ${String(totalFindings).padEnd(30)}│
|
|
105
|
+
└─────────────────────────────────────────┘
|
|
106
|
+
|
|
107
|
+
Severity Distribution:
|
|
108
|
+
🔴 Critical: ${"█".repeat(Math.min(criticalCount, 30))} ${criticalCount}
|
|
109
|
+
🟠 High: ${"█".repeat(Math.min(highCount, 30))} ${highCount}
|
|
110
|
+
🟡 Medium: ${"█".repeat(Math.min(mediumCount, 30))} ${mediumCount}
|
|
111
|
+
🟢 Low: ${"█".repeat(Math.min(lowCount, 30))} ${lowCount}
|
|
112
|
+
`);
|
|
113
|
+
// Top rules
|
|
114
|
+
const ruleCounts = new Map();
|
|
115
|
+
for (const v of verdicts) {
|
|
116
|
+
for (const f of v.findings || []) {
|
|
117
|
+
const rule = f.ruleId || "unknown";
|
|
118
|
+
ruleCounts.set(rule, (ruleCounts.get(rule) || 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const topRules = [...ruleCounts.entries()].sort(([, a], [, b]) => b - a).slice(0, 5);
|
|
122
|
+
if (topRules.length > 0) {
|
|
123
|
+
console.log(" Top Rules:");
|
|
124
|
+
for (const [rule, count] of topRules) {
|
|
125
|
+
console.log(` ${rule.padEnd(30)} ${count} occurrence(s)`);
|
|
126
|
+
}
|
|
127
|
+
console.log();
|
|
128
|
+
}
|
|
129
|
+
// Insights
|
|
130
|
+
console.log(" Insights:");
|
|
131
|
+
if (criticalCount > 0)
|
|
132
|
+
console.log(` ⚠️ ${criticalCount} critical finding(s) require immediate attention`);
|
|
133
|
+
if (failCount > passCount)
|
|
134
|
+
console.log(" ⚠️ More reviews failing than passing — consider reviewing thresholds");
|
|
135
|
+
if (avgScore >= 80)
|
|
136
|
+
console.log(" ✅ Code quality is above target");
|
|
137
|
+
if (totalFindings === 0)
|
|
138
|
+
console.log(" ✅ Clean — no findings detected");
|
|
139
|
+
console.log();
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=review-dashboard.js.map
|