@kevinrabun/judges 3.68.0 → 3.70.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 +19 -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/finding-fix-rate.d.ts +5 -0
- package/dist/commands/finding-fix-rate.d.ts.map +1 -0
- package/dist/commands/finding-fix-rate.js +142 -0
- package/dist/commands/finding-fix-rate.js.map +1 -0
- package/dist/commands/finding-impact.d.ts +5 -0
- package/dist/commands/finding-impact.d.ts.map +1 -0
- package/dist/commands/finding-impact.js +136 -0
- package/dist/commands/finding-impact.js.map +1 -0
- package/dist/commands/finding-recurrence.d.ts +5 -0
- package/dist/commands/finding-recurrence.d.ts.map +1 -0
- package/dist/commands/finding-recurrence.js +136 -0
- package/dist/commands/finding-recurrence.js.map +1 -0
- package/dist/commands/review-archive.d.ts +5 -0
- package/dist/commands/review-archive.d.ts.map +1 -0
- package/dist/commands/review-archive.js +136 -0
- package/dist/commands/review-archive.js.map +1 -0
- package/dist/commands/review-benchmark-self.d.ts +5 -0
- package/dist/commands/review-benchmark-self.d.ts.map +1 -0
- package/dist/commands/review-benchmark-self.js +141 -0
- package/dist/commands/review-benchmark-self.js.map +1 -0
- package/dist/commands/review-changelog-gen.d.ts +5 -0
- package/dist/commands/review-changelog-gen.d.ts.map +1 -0
- package/dist/commands/review-changelog-gen.js +118 -0
- package/dist/commands/review-changelog-gen.js.map +1 -0
- package/dist/commands/review-ci-status.d.ts +5 -0
- package/dist/commands/review-ci-status.d.ts.map +1 -0
- package/dist/commands/review-ci-status.js +201 -0
- package/dist/commands/review-ci-status.js.map +1 -0
- package/dist/commands/review-custom-prompt.d.ts +5 -0
- package/dist/commands/review-custom-prompt.d.ts.map +1 -0
- package/dist/commands/review-custom-prompt.js +171 -0
- package/dist/commands/review-custom-prompt.js.map +1 -0
- package/dist/commands/review-diff-context.d.ts +5 -0
- package/dist/commands/review-diff-context.d.ts.map +1 -0
- package/dist/commands/review-diff-context.js +159 -0
- package/dist/commands/review-diff-context.js.map +1 -0
- package/dist/commands/review-milestone.d.ts +5 -0
- package/dist/commands/review-milestone.d.ts.map +1 -0
- package/dist/commands/review-milestone.js +137 -0
- package/dist/commands/review-milestone.js.map +1 -0
- package/dist/commands/review-report-pdf.d.ts +5 -0
- package/dist/commands/review-report-pdf.d.ts.map +1 -0
- package/dist/commands/review-report-pdf.js +164 -0
- package/dist/commands/review-report-pdf.js.map +1 -0
- package/dist/commands/review-risk-score.d.ts +5 -0
- package/dist/commands/review-risk-score.d.ts.map +1 -0
- package/dist/commands/review-risk-score.js +157 -0
- package/dist/commands/review-risk-score.js.map +1 -0
- package/dist/commands/review-standup.d.ts +5 -0
- package/dist/commands/review-standup.d.ts.map +1 -0
- package/dist/commands/review-standup.js +96 -0
- package/dist/commands/review-standup.js.map +1 -0
- package/dist/commands/review-tag.d.ts +5 -0
- package/dist/commands/review-tag.d.ts.map +1 -0
- package/dist/commands/review-tag.js +137 -0
- package/dist/commands/review-tag.js.map +1 -0
- package/dist/commands/review-team-summary.d.ts +5 -0
- package/dist/commands/review-team-summary.d.ts.map +1 -0
- package/dist/commands/review-team-summary.js +156 -0
- package/dist/commands/review-team-summary.js.map +1 -0
- package/dist/commands/review-whitelist.d.ts +5 -0
- package/dist/commands/review-whitelist.d.ts.map +1 -0
- package/dist/commands/review-whitelist.js +159 -0
- package/dist/commands/review-whitelist.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-fix-rate — Track how quickly findings are being resolved over time.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
// ─── Storage ────────────────────────────────────────────────────────────────
|
|
7
|
+
const FIX_RATE_FILE = join(".judges", "fix-rate.json");
|
|
8
|
+
function loadStore() {
|
|
9
|
+
if (!existsSync(FIX_RATE_FILE))
|
|
10
|
+
return { version: "1.0.0", events: [] };
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(FIX_RATE_FILE, "utf-8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { version: "1.0.0", events: [] };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function saveStore(store) {
|
|
19
|
+
mkdirSync(dirname(FIX_RATE_FILE), { recursive: true });
|
|
20
|
+
writeFileSync(FIX_RATE_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
21
|
+
}
|
|
22
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
23
|
+
export function runFindingFixRate(argv) {
|
|
24
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
25
|
+
console.log(`
|
|
26
|
+
judges finding-fix-rate — Track finding resolution speed
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
judges finding-fix-rate show Show fix rate metrics
|
|
30
|
+
judges finding-fix-rate record --rule SEC-001 --severity high --days 3
|
|
31
|
+
judges finding-fix-rate trend Show trend over time
|
|
32
|
+
judges finding-fix-rate clear Clear all data
|
|
33
|
+
|
|
34
|
+
Subcommands:
|
|
35
|
+
show Show fix rate summary
|
|
36
|
+
record Record a fix event
|
|
37
|
+
trend Show fix rate trends
|
|
38
|
+
clear Clear all data
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--rule <ruleId> Rule ID of the fixed finding
|
|
42
|
+
--severity <level> Severity (critical, high, medium, low)
|
|
43
|
+
--days <n> Days to fix
|
|
44
|
+
--last <n> Show last N events (default: 20)
|
|
45
|
+
--format json JSON output
|
|
46
|
+
--help, -h Show this help
|
|
47
|
+
|
|
48
|
+
Tracks how quickly findings are resolved. Data in .judges/fix-rate.json.
|
|
49
|
+
`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
53
|
+
const subcommand = argv.find((a) => ["show", "record", "trend", "clear"].includes(a)) || "show";
|
|
54
|
+
const store = loadStore();
|
|
55
|
+
if (subcommand === "record") {
|
|
56
|
+
const ruleId = argv.find((_a, i) => argv[i - 1] === "--rule") || "UNKNOWN";
|
|
57
|
+
const severity = argv.find((_a, i) => argv[i - 1] === "--severity") || "medium";
|
|
58
|
+
const days = parseInt(argv.find((_a, i) => argv[i - 1] === "--days") || "1", 10);
|
|
59
|
+
const now = new Date();
|
|
60
|
+
const foundDate = new Date(now.getTime() - days * 86400000);
|
|
61
|
+
store.events.push({
|
|
62
|
+
ruleId,
|
|
63
|
+
severity,
|
|
64
|
+
foundAt: foundDate.toISOString(),
|
|
65
|
+
fixedAt: now.toISOString(),
|
|
66
|
+
daysToFix: days,
|
|
67
|
+
});
|
|
68
|
+
saveStore(store);
|
|
69
|
+
console.log(`Recorded fix: ${ruleId} (${severity}) resolved in ${days} day(s).`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (subcommand === "clear") {
|
|
73
|
+
saveStore({ version: "1.0.0", events: [] });
|
|
74
|
+
console.log("Fix rate data cleared.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (subcommand === "trend") {
|
|
78
|
+
if (store.events.length === 0) {
|
|
79
|
+
console.log("No fix events recorded yet.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Group by month
|
|
83
|
+
const monthly = new Map();
|
|
84
|
+
for (const e of store.events) {
|
|
85
|
+
const month = e.fixedAt.slice(0, 7);
|
|
86
|
+
const list = monthly.get(month) || [];
|
|
87
|
+
list.push(e.daysToFix);
|
|
88
|
+
monthly.set(month, list);
|
|
89
|
+
}
|
|
90
|
+
if (format === "json") {
|
|
91
|
+
const trend = [...monthly.entries()].map(([month, days]) => ({
|
|
92
|
+
month,
|
|
93
|
+
avgDays: days.reduce((s, d) => s + d, 0) / days.length,
|
|
94
|
+
fixes: days.length,
|
|
95
|
+
}));
|
|
96
|
+
console.log(JSON.stringify(trend, null, 2));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
console.log("\nFix Rate Trend:");
|
|
100
|
+
console.log("─".repeat(50));
|
|
101
|
+
for (const [month, days] of monthly) {
|
|
102
|
+
const avg = days.reduce((s, d) => s + d, 0) / days.length;
|
|
103
|
+
const bar = "█".repeat(Math.min(Math.round(avg), 30));
|
|
104
|
+
console.log(` ${month} avg ${avg.toFixed(1)}d (${days.length} fixes) ${bar}`);
|
|
105
|
+
}
|
|
106
|
+
console.log("─".repeat(50));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// show
|
|
110
|
+
if (store.events.length === 0) {
|
|
111
|
+
console.log("No fix events recorded. Use 'judges finding-fix-rate record' to track fixes.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const avgDays = store.events.reduce((s, e) => s + e.daysToFix, 0) / store.events.length;
|
|
115
|
+
const criticals = store.events.filter((e) => e.severity === "critical");
|
|
116
|
+
const avgCriticalDays = criticals.length > 0 ? criticals.reduce((s, e) => s + e.daysToFix, 0) / criticals.length : 0;
|
|
117
|
+
if (format === "json") {
|
|
118
|
+
console.log(JSON.stringify({
|
|
119
|
+
totalFixes: store.events.length,
|
|
120
|
+
avgDaysToFix: avgDays,
|
|
121
|
+
avgCriticalDaysToFix: avgCriticalDays,
|
|
122
|
+
bySeverity: {
|
|
123
|
+
critical: criticals.length,
|
|
124
|
+
high: store.events.filter((e) => e.severity === "high").length,
|
|
125
|
+
medium: store.events.filter((e) => e.severity === "medium").length,
|
|
126
|
+
low: store.events.filter((e) => e.severity === "low").length,
|
|
127
|
+
},
|
|
128
|
+
}, null, 2));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.log("\nFix Rate Summary:");
|
|
132
|
+
console.log("─".repeat(40));
|
|
133
|
+
console.log(` Total fixes: ${store.events.length}`);
|
|
134
|
+
console.log(` Avg days to fix: ${avgDays.toFixed(1)}`);
|
|
135
|
+
console.log(` Avg critical fix time: ${avgCriticalDays > 0 ? avgCriticalDays.toFixed(1) + "d" : "N/A"}`);
|
|
136
|
+
console.log(` Critical fixes: ${criticals.length}`);
|
|
137
|
+
console.log(` High fixes: ${store.events.filter((e) => e.severity === "high").length}`);
|
|
138
|
+
console.log(` Medium fixes: ${store.events.filter((e) => e.severity === "medium").length}`);
|
|
139
|
+
console.log(` Low fixes: ${store.events.filter((e) => e.severity === "low").length}`);
|
|
140
|
+
console.log("─".repeat(40));
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=finding-fix-rate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-fix-rate.js","sourceRoot":"","sources":["../../src/commands/finding-fix-rate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-impact.d.ts","sourceRoot":"","sources":["../../src/commands/finding-impact.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8EH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+FrD"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-impact — Estimate business impact of each finding.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
// ─── Scoring ────────────────────────────────────────────────────────────────
|
|
7
|
+
const SEVERITY_IMPACT = {
|
|
8
|
+
critical: "critical",
|
|
9
|
+
high: "high",
|
|
10
|
+
medium: "medium",
|
|
11
|
+
low: "low",
|
|
12
|
+
info: "info",
|
|
13
|
+
};
|
|
14
|
+
const COST_ESTIMATES = {
|
|
15
|
+
critical: "$50,000–$500,000+ (breach, compliance penalty)",
|
|
16
|
+
high: "$10,000–$50,000 (data loss, downtime)",
|
|
17
|
+
medium: "$1,000–$10,000 (technical debt, moderate risk)",
|
|
18
|
+
low: "$100–$1,000 (minor refactor)",
|
|
19
|
+
info: "Minimal (best practice improvement)",
|
|
20
|
+
};
|
|
21
|
+
const EXPLOITABILITY = {
|
|
22
|
+
critical: "Easily exploitable, active attack vectors known",
|
|
23
|
+
high: "Exploitable with moderate skill",
|
|
24
|
+
medium: "Requires specific conditions to exploit",
|
|
25
|
+
low: "Difficult to exploit in practice",
|
|
26
|
+
info: "Not directly exploitable",
|
|
27
|
+
};
|
|
28
|
+
function assessFinding(finding) {
|
|
29
|
+
const severity = finding.severity || "medium";
|
|
30
|
+
const impact = SEVERITY_IMPACT[severity] || "medium";
|
|
31
|
+
const ruleId = finding.ruleId || "unknown";
|
|
32
|
+
const isInjection = /inject|sqli|xss|command/i.test(ruleId);
|
|
33
|
+
const isAuth = /auth|session|token|cred/i.test(ruleId);
|
|
34
|
+
const isCrypto = /crypt|hash|random|secret/i.test(ruleId);
|
|
35
|
+
let blastRadius = "Localized to component";
|
|
36
|
+
if (isInjection || isAuth)
|
|
37
|
+
blastRadius = "System-wide, potential data exfiltration";
|
|
38
|
+
else if (isCrypto)
|
|
39
|
+
blastRadius = "All encrypted data at risk";
|
|
40
|
+
return {
|
|
41
|
+
ruleId,
|
|
42
|
+
severity,
|
|
43
|
+
title: finding.title || "",
|
|
44
|
+
businessImpact: isInjection && impact !== "critical" ? "high" : impact,
|
|
45
|
+
estimatedCost: COST_ESTIMATES[impact] || COST_ESTIMATES["medium"],
|
|
46
|
+
exploitability: EXPLOITABILITY[impact] || EXPLOITABILITY["medium"],
|
|
47
|
+
blastRadius,
|
|
48
|
+
recommendation: finding.recommendation || "Review and remediate.",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
52
|
+
export function runFindingImpact(argv) {
|
|
53
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
54
|
+
console.log(`
|
|
55
|
+
judges finding-impact — Estimate business impact of findings
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
judges finding-impact --file report.json
|
|
59
|
+
judges finding-impact --file report.json --format json
|
|
60
|
+
|
|
61
|
+
Options:
|
|
62
|
+
--file <path> Path to a tribunal verdict JSON file
|
|
63
|
+
--format json Output as JSON
|
|
64
|
+
--min-impact <level> Filter by minimum impact (critical|high|medium|low|info)
|
|
65
|
+
--help, -h Show this help
|
|
66
|
+
|
|
67
|
+
Analyzes each finding and estimates:
|
|
68
|
+
• Business impact level (critical → info)
|
|
69
|
+
• Estimated remediation cost
|
|
70
|
+
• Exploitability assessment
|
|
71
|
+
• Blast radius (scope of potential damage)
|
|
72
|
+
|
|
73
|
+
Impact assessments are stored in .judges/finding-impact.json.
|
|
74
|
+
`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const filePath = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
78
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
79
|
+
const minImpact = argv.find((_a, i) => argv[i - 1] === "--min-impact") || "info";
|
|
80
|
+
if (!filePath || !existsSync(filePath)) {
|
|
81
|
+
console.error("Error: --file is required and must exist.");
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let verdict;
|
|
86
|
+
try {
|
|
87
|
+
verdict = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
console.error("Error: Could not parse verdict file.");
|
|
91
|
+
process.exitCode = 1;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const findings = verdict.findings || [];
|
|
95
|
+
if (findings.length === 0) {
|
|
96
|
+
console.log("No findings to assess.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const impactLevels = ["info", "low", "medium", "high", "critical"];
|
|
100
|
+
const minIdx = impactLevels.indexOf(minImpact);
|
|
101
|
+
const assessments = findings.map(assessFinding).filter((a) => impactLevels.indexOf(a.businessImpact) >= minIdx);
|
|
102
|
+
const summary = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
103
|
+
for (const a of assessments) {
|
|
104
|
+
summary[a.businessImpact]++;
|
|
105
|
+
}
|
|
106
|
+
const report = {
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
totalFindings: assessments.length,
|
|
109
|
+
assessments,
|
|
110
|
+
summary,
|
|
111
|
+
};
|
|
112
|
+
// Save
|
|
113
|
+
const outPath = join(".judges", "finding-impact.json");
|
|
114
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
115
|
+
writeFileSync(outPath, JSON.stringify(report, null, 2), "utf-8");
|
|
116
|
+
if (format === "json") {
|
|
117
|
+
console.log(JSON.stringify(report, null, 2));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log("\nFinding Impact Assessment:");
|
|
121
|
+
console.log("═".repeat(70));
|
|
122
|
+
console.log(` Total findings assessed: ${assessments.length}`);
|
|
123
|
+
console.log(` Critical: ${summary.critical} High: ${summary.high} Medium: ${summary.medium} Low: ${summary.low} Info: ${summary.info}`);
|
|
124
|
+
console.log("═".repeat(70));
|
|
125
|
+
for (const a of assessments) {
|
|
126
|
+
console.log(`\n [${a.businessImpact.toUpperCase()}] ${a.ruleId}`);
|
|
127
|
+
console.log(` Title: ${a.title}`);
|
|
128
|
+
console.log(` Est. Cost: ${a.estimatedCost}`);
|
|
129
|
+
console.log(` Exploitability: ${a.exploitability}`);
|
|
130
|
+
console.log(` Blast Radius: ${a.blastRadius}`);
|
|
131
|
+
console.log(` Recommendation: ${a.recommendation}`);
|
|
132
|
+
}
|
|
133
|
+
console.log("\n" + "═".repeat(70));
|
|
134
|
+
console.log(` Report saved to ${outPath}`);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=finding-impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-impact.js","sourceRoot":"","sources":["../../src/commands/finding-impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-recurrence.d.ts","sourceRoot":"","sources":["../../src/commands/finding-recurrence.ts"],"names":[],"mappings":"AAAA;;GAEG;AA+CH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsHzD"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-recurrence — Track findings that keep coming back after being fixed.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
// ─── Storage ────────────────────────────────────────────────────────────────
|
|
7
|
+
const RECURRENCE_FILE = join(".judges", "finding-recurrence.json");
|
|
8
|
+
function loadStore() {
|
|
9
|
+
if (!existsSync(RECURRENCE_FILE))
|
|
10
|
+
return { version: "1.0.0", records: [] };
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(RECURRENCE_FILE, "utf-8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { version: "1.0.0", records: [] };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function saveStore(store) {
|
|
19
|
+
mkdirSync(dirname(RECURRENCE_FILE), { recursive: true });
|
|
20
|
+
writeFileSync(RECURRENCE_FILE, JSON.stringify(store, null, 2), "utf-8");
|
|
21
|
+
}
|
|
22
|
+
function fingerprint(f) {
|
|
23
|
+
return [f.ruleId || "", f.title || "", String(f.severity || "")].join("|").toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
26
|
+
export function runFindingRecurrence(argv) {
|
|
27
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
28
|
+
console.log(`
|
|
29
|
+
judges finding-recurrence — Track recurring findings
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
judges finding-recurrence update --file verdict.json Update records
|
|
33
|
+
judges finding-recurrence show Show recurring findings
|
|
34
|
+
judges finding-recurrence show --min 3 Show findings with 3+ occurrences
|
|
35
|
+
judges finding-recurrence clear Clear all data
|
|
36
|
+
|
|
37
|
+
Subcommands:
|
|
38
|
+
update Update recurrence records from verdict
|
|
39
|
+
show Show recurring findings
|
|
40
|
+
clear Clear all data
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--file <path> Verdict JSON file (for update)
|
|
44
|
+
--min <n> Minimum occurrences to show (default: 2)
|
|
45
|
+
--format json JSON output
|
|
46
|
+
--help, -h Show this help
|
|
47
|
+
|
|
48
|
+
Tracks findings that keep reappearing. Data in .judges/finding-recurrence.json.
|
|
49
|
+
`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
53
|
+
const subcommand = argv.find((a) => ["update", "show", "clear"].includes(a)) || "show";
|
|
54
|
+
const store = loadStore();
|
|
55
|
+
if (subcommand === "update") {
|
|
56
|
+
const file = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
57
|
+
if (!file || !existsSync(file)) {
|
|
58
|
+
console.error("Error: --file with valid verdict JSON is required.");
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let verdict;
|
|
63
|
+
try {
|
|
64
|
+
verdict = JSON.parse(readFileSync(file, "utf-8"));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
console.error("Error: Failed to parse verdict file.");
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const now = new Date().toISOString();
|
|
72
|
+
const seenFingerprints = new Set();
|
|
73
|
+
let newCount = 0;
|
|
74
|
+
let recurCount = 0;
|
|
75
|
+
for (const f of verdict.findings || []) {
|
|
76
|
+
const fp = fingerprint(f);
|
|
77
|
+
seenFingerprints.add(fp);
|
|
78
|
+
const existing = store.records.find((r) => r.fingerprint === fp);
|
|
79
|
+
if (existing) {
|
|
80
|
+
existing.occurrences++;
|
|
81
|
+
existing.lastSeen = now;
|
|
82
|
+
recurCount++;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
store.records.push({
|
|
86
|
+
fingerprint: fp,
|
|
87
|
+
ruleId: f.ruleId || "",
|
|
88
|
+
title: f.title || "",
|
|
89
|
+
occurrences: 1,
|
|
90
|
+
firstSeen: now,
|
|
91
|
+
lastSeen: now,
|
|
92
|
+
resolvedCount: 0,
|
|
93
|
+
});
|
|
94
|
+
newCount++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Mark resolved findings
|
|
98
|
+
for (const r of store.records) {
|
|
99
|
+
if (!seenFingerprints.has(r.fingerprint) && r.occurrences > 0) {
|
|
100
|
+
r.resolvedCount++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
saveStore(store);
|
|
104
|
+
console.log(`Updated: ${newCount} new, ${recurCount} recurring findings tracked.`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (subcommand === "clear") {
|
|
108
|
+
saveStore({ version: "1.0.0", records: [] });
|
|
109
|
+
console.log("Recurrence data cleared.");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// show
|
|
113
|
+
const minOccurrences = parseInt(argv.find((_a, i) => argv[i - 1] === "--min") || "2", 10);
|
|
114
|
+
const recurring = store.records.filter((r) => r.occurrences >= minOccurrences);
|
|
115
|
+
recurring.sort((a, b) => b.occurrences - a.occurrences);
|
|
116
|
+
if (format === "json") {
|
|
117
|
+
console.log(JSON.stringify(recurring, null, 2));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (recurring.length === 0) {
|
|
121
|
+
console.log(`No findings with ${minOccurrences}+ occurrences.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(`\nRecurring Findings (${minOccurrences}+ occurrences):`);
|
|
125
|
+
console.log("─".repeat(70));
|
|
126
|
+
console.log(" Occurrences Rule Title");
|
|
127
|
+
console.log("─".repeat(70));
|
|
128
|
+
for (const r of recurring.slice(0, 30)) {
|
|
129
|
+
console.log(` ${String(r.occurrences).padEnd(12)} ${(r.ruleId || "-").padEnd(13)} ${r.title.slice(0, 40)}`);
|
|
130
|
+
}
|
|
131
|
+
console.log("─".repeat(70));
|
|
132
|
+
console.log(` ${recurring.length} recurring finding(s) found`);
|
|
133
|
+
if (recurring.length > 30)
|
|
134
|
+
console.log(` (showing top 30 of ${recurring.length})`);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=finding-recurrence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-recurrence.js","sourceRoot":"","sources":["../../src/commands/finding-recurrence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-archive.d.ts","sourceRoot":"","sources":["../../src/commands/review-archive.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6CH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAmHrD"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-archive — Archive and retrieve old review results.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, copyFileSync } from "fs";
|
|
5
|
+
import { join, basename, dirname } from "path";
|
|
6
|
+
// ─── Storage ────────────────────────────────────────────────────────────────
|
|
7
|
+
const ARCHIVE_DIR = join(".judges", "archive");
|
|
8
|
+
const INDEX_FILE = join(ARCHIVE_DIR, "index.json");
|
|
9
|
+
function loadIndex() {
|
|
10
|
+
if (!existsSync(INDEX_FILE))
|
|
11
|
+
return { version: "1.0.0", entries: [] };
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(INDEX_FILE, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return { version: "1.0.0", entries: [] };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function saveIndex(index) {
|
|
20
|
+
mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
21
|
+
writeFileSync(INDEX_FILE, JSON.stringify(index, null, 2), "utf-8");
|
|
22
|
+
}
|
|
23
|
+
function generateId() {
|
|
24
|
+
return `arc-${Date.now().toString(36)}`;
|
|
25
|
+
}
|
|
26
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
27
|
+
export function runReviewArchive(argv) {
|
|
28
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
29
|
+
console.log(`
|
|
30
|
+
judges review-archive — Archive and retrieve old review results
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
judges review-archive add --file report.json --label sprint-42
|
|
34
|
+
judges review-archive list
|
|
35
|
+
judges review-archive restore --id arc-abc123 --out restored.json
|
|
36
|
+
judges review-archive clear
|
|
37
|
+
|
|
38
|
+
Subcommands:
|
|
39
|
+
add Archive a review result file
|
|
40
|
+
list List archived reviews
|
|
41
|
+
restore Restore an archived review
|
|
42
|
+
clear Clear all archived data
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--file <path> File to archive
|
|
46
|
+
--label <text> Label for the archive entry
|
|
47
|
+
--id <id> Archive entry ID
|
|
48
|
+
--out <path> Output path for restored file
|
|
49
|
+
--format json JSON output
|
|
50
|
+
--help, -h Show this help
|
|
51
|
+
|
|
52
|
+
Archives are stored in .judges/archive/.
|
|
53
|
+
`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const subcommand = argv.find((a) => ["add", "list", "restore", "clear"].includes(a)) || "list";
|
|
57
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
58
|
+
const index = loadIndex();
|
|
59
|
+
if (subcommand === "add") {
|
|
60
|
+
const filePath = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
61
|
+
const label = argv.find((_a, i) => argv[i - 1] === "--label") || "";
|
|
62
|
+
if (!filePath || !existsSync(filePath)) {
|
|
63
|
+
console.error("Error: --file is required and must exist.");
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const id = generateId();
|
|
68
|
+
const archiveName = `${id}-${basename(filePath)}`;
|
|
69
|
+
const archivePath = join(ARCHIVE_DIR, archiveName);
|
|
70
|
+
mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
71
|
+
copyFileSync(filePath, archivePath);
|
|
72
|
+
const stat = readFileSync(filePath);
|
|
73
|
+
index.entries.push({
|
|
74
|
+
id,
|
|
75
|
+
originalPath: filePath,
|
|
76
|
+
archivedAt: new Date().toISOString(),
|
|
77
|
+
label,
|
|
78
|
+
sizeBytes: stat.length,
|
|
79
|
+
});
|
|
80
|
+
saveIndex(index);
|
|
81
|
+
console.log(`Archived "${filePath}" as ${id}${label ? ` (${label})` : ""}.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (subcommand === "restore") {
|
|
85
|
+
const id = argv.find((_a, i) => argv[i - 1] === "--id");
|
|
86
|
+
const out = argv.find((_a, i) => argv[i - 1] === "--out");
|
|
87
|
+
if (!id) {
|
|
88
|
+
console.error("Error: --id is required.");
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const entry = index.entries.find((e) => e.id === id);
|
|
93
|
+
if (!entry) {
|
|
94
|
+
console.error(`Error: Archive "${id}" not found.`);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Find the archived file
|
|
99
|
+
const files = readdirSync(ARCHIVE_DIR);
|
|
100
|
+
const archiveFile = files.find((f) => f.startsWith(id));
|
|
101
|
+
if (!archiveFile) {
|
|
102
|
+
console.error(`Error: Archive file for "${id}" missing from disk.`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const dest = out || entry.originalPath;
|
|
107
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
108
|
+
copyFileSync(join(ARCHIVE_DIR, archiveFile), dest);
|
|
109
|
+
console.log(`Restored ${id} to "${dest}".`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (subcommand === "clear") {
|
|
113
|
+
saveIndex({ version: "1.0.0", entries: [] });
|
|
114
|
+
console.log("Archive index cleared.");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// list
|
|
118
|
+
if (index.entries.length === 0) {
|
|
119
|
+
console.log("No archived reviews. Use 'judges review-archive add' to start.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (format === "json") {
|
|
123
|
+
console.log(JSON.stringify(index.entries, null, 2));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
console.log("\nArchived Reviews:");
|
|
127
|
+
console.log("─".repeat(70));
|
|
128
|
+
for (const e of index.entries) {
|
|
129
|
+
const size = e.sizeBytes < 1024 ? `${e.sizeBytes}B` : `${(e.sizeBytes / 1024).toFixed(1)}KB`;
|
|
130
|
+
console.log(` ${e.id} ${e.archivedAt.slice(0, 10)} ${size.padEnd(8)} ${e.label || "(no label)"}`);
|
|
131
|
+
console.log(` Original: ${e.originalPath}`);
|
|
132
|
+
}
|
|
133
|
+
console.log("─".repeat(70));
|
|
134
|
+
console.log(` Total: ${index.entries.length} archived review(s)`);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=review-archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-archive.js","sourceRoot":"","sources":["../../src/commands/review-archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-benchmark-self.d.ts","sourceRoot":"","sources":["../../src/commands/review-benchmark-self.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyCH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+I3D"}
|