@kevinrabun/judges 3.109.0 → 3.111.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 +26 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/finding-auto-priority.d.ts +2 -0
- package/dist/commands/finding-auto-priority.d.ts.map +1 -0
- package/dist/commands/finding-auto-priority.js +101 -0
- package/dist/commands/finding-auto-priority.js.map +1 -0
- package/dist/commands/finding-cluster-summary.d.ts +2 -0
- package/dist/commands/finding-cluster-summary.d.ts.map +1 -0
- package/dist/commands/finding-cluster-summary.js +86 -0
- package/dist/commands/finding-cluster-summary.js.map +1 -0
- package/dist/commands/finding-context-link.d.ts +2 -0
- package/dist/commands/finding-context-link.d.ts.map +1 -0
- package/dist/commands/finding-context-link.js +95 -0
- package/dist/commands/finding-context-link.js.map +1 -0
- package/dist/commands/finding-dependency-impact.d.ts +2 -0
- package/dist/commands/finding-dependency-impact.d.ts.map +1 -0
- package/dist/commands/finding-dependency-impact.js +98 -0
- package/dist/commands/finding-dependency-impact.js.map +1 -0
- package/dist/commands/finding-effort-rank.d.ts +2 -0
- package/dist/commands/finding-effort-rank.d.ts.map +1 -0
- package/dist/commands/finding-effort-rank.js +94 -0
- package/dist/commands/finding-effort-rank.js.map +1 -0
- package/dist/commands/finding-fix-chain.d.ts +2 -0
- package/dist/commands/finding-fix-chain.d.ts.map +1 -0
- package/dist/commands/finding-fix-chain.js +79 -0
- package/dist/commands/finding-fix-chain.js.map +1 -0
- package/dist/commands/finding-owner-notify.d.ts +2 -0
- package/dist/commands/finding-owner-notify.d.ts.map +1 -0
- package/dist/commands/finding-owner-notify.js +122 -0
- package/dist/commands/finding-owner-notify.js.map +1 -0
- package/dist/commands/finding-patch-chain.d.ts +2 -0
- package/dist/commands/finding-patch-chain.d.ts.map +1 -0
- package/dist/commands/finding-patch-chain.js +91 -0
- package/dist/commands/finding-patch-chain.js.map +1 -0
- package/dist/commands/finding-resolution-workflow.d.ts +2 -0
- package/dist/commands/finding-resolution-workflow.d.ts.map +1 -0
- package/dist/commands/finding-resolution-workflow.js +92 -0
- package/dist/commands/finding-resolution-workflow.js.map +1 -0
- package/dist/commands/finding-risk-label.d.ts +2 -0
- package/dist/commands/finding-risk-label.d.ts.map +1 -0
- package/dist/commands/finding-risk-label.js +73 -0
- package/dist/commands/finding-risk-label.js.map +1 -0
- package/dist/commands/review-config-health.d.ts +2 -0
- package/dist/commands/review-config-health.d.ts.map +1 -0
- package/dist/commands/review-config-health.js +173 -0
- package/dist/commands/review-config-health.js.map +1 -0
- package/dist/commands/review-engagement-score.d.ts +2 -0
- package/dist/commands/review-engagement-score.d.ts.map +1 -0
- package/dist/commands/review-engagement-score.js +112 -0
- package/dist/commands/review-engagement-score.js.map +1 -0
- package/dist/commands/review-feedback-summary.d.ts +2 -0
- package/dist/commands/review-feedback-summary.d.ts.map +1 -0
- package/dist/commands/review-feedback-summary.js +131 -0
- package/dist/commands/review-feedback-summary.js.map +1 -0
- package/dist/commands/review-mentor-suggest.d.ts +2 -0
- package/dist/commands/review-mentor-suggest.d.ts.map +1 -0
- package/dist/commands/review-mentor-suggest.js +113 -0
- package/dist/commands/review-mentor-suggest.js.map +1 -0
- package/dist/commands/review-progress-report.d.ts +2 -0
- package/dist/commands/review-progress-report.d.ts.map +1 -0
- package/dist/commands/review-progress-report.js +96 -0
- package/dist/commands/review-progress-report.js.map +1 -0
- package/dist/commands/review-quality-baseline.d.ts +2 -0
- package/dist/commands/review-quality-baseline.d.ts.map +1 -0
- package/dist/commands/review-quality-baseline.js +135 -0
- package/dist/commands/review-quality-baseline.js.map +1 -0
- package/dist/commands/review-retrospective.d.ts +2 -0
- package/dist/commands/review-retrospective.d.ts.map +1 -0
- package/dist/commands/review-retrospective.js +119 -0
- package/dist/commands/review-retrospective.js.map +1 -0
- package/dist/commands/review-team-velocity.d.ts +2 -0
- package/dist/commands/review-team-velocity.d.ts.map +1 -0
- package/dist/commands/review-team-velocity.js +104 -0
- package/dist/commands/review-team-velocity.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-fix-chain.js","sourceRoot":"","sources":["../../src/commands/finding-fix-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-owner-notify.d.ts","sourceRoot":"","sources":["../../src/commands/finding-owner-notify.ts"],"names":[],"mappings":"AAgGA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAyD1D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
function loadOwnerMappings(mappingPath) {
|
|
4
|
+
if (!existsSync(mappingPath))
|
|
5
|
+
return [];
|
|
6
|
+
try {
|
|
7
|
+
const content = readFileSync(mappingPath, "utf-8");
|
|
8
|
+
// Support simple JSON format: [{ "pattern": "auth", "owners": ["@alice"] }]
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// Try CODEOWNERS-like format: pattern @owner1 @owner2
|
|
13
|
+
const lines = readFileSync(mappingPath, "utf-8").split("\n");
|
|
14
|
+
const mappings = [];
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
18
|
+
continue;
|
|
19
|
+
const parts = trimmed.split(/\s+/);
|
|
20
|
+
if (parts.length >= 2) {
|
|
21
|
+
mappings.push({ pattern: parts[0], owners: parts.slice(1) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return mappings;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const SEVERITY_RANK = { critical: 5, high: 4, medium: 3, low: 2, info: 1 };
|
|
28
|
+
function rankToSev(rank) {
|
|
29
|
+
if (rank >= 5)
|
|
30
|
+
return "critical";
|
|
31
|
+
if (rank >= 4)
|
|
32
|
+
return "high";
|
|
33
|
+
if (rank >= 3)
|
|
34
|
+
return "medium";
|
|
35
|
+
if (rank >= 2)
|
|
36
|
+
return "low";
|
|
37
|
+
return "info";
|
|
38
|
+
}
|
|
39
|
+
function buildNotifications(verdict, mappings) {
|
|
40
|
+
const ownerMap = new Map();
|
|
41
|
+
for (const f of verdict.findings ?? []) {
|
|
42
|
+
const ruleIdLower = f.ruleId.toLowerCase();
|
|
43
|
+
const titleLower = f.title.toLowerCase();
|
|
44
|
+
let matched = false;
|
|
45
|
+
for (const mapping of mappings) {
|
|
46
|
+
if (ruleIdLower.includes(mapping.pattern.toLowerCase()) || titleLower.includes(mapping.pattern.toLowerCase())) {
|
|
47
|
+
for (const owner of mapping.owners) {
|
|
48
|
+
const entry = ownerMap.get(owner) ?? { findings: [], maxRank: 0 };
|
|
49
|
+
entry.findings.push({ ruleId: f.ruleId, title: f.title, severity: f.severity });
|
|
50
|
+
entry.maxRank = Math.max(entry.maxRank, SEVERITY_RANK[f.severity] ?? 0);
|
|
51
|
+
ownerMap.set(owner, entry);
|
|
52
|
+
}
|
|
53
|
+
matched = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!matched) {
|
|
58
|
+
const entry = ownerMap.get("unassigned") ?? { findings: [], maxRank: 0 };
|
|
59
|
+
entry.findings.push({ ruleId: f.ruleId, title: f.title, severity: f.severity });
|
|
60
|
+
entry.maxRank = Math.max(entry.maxRank, SEVERITY_RANK[f.severity] ?? 0);
|
|
61
|
+
ownerMap.set("unassigned", entry);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const [owner, data] of ownerMap) {
|
|
66
|
+
results.push({
|
|
67
|
+
owner,
|
|
68
|
+
findings: data.findings,
|
|
69
|
+
highestSeverity: rankToSev(data.maxRank),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
results.sort((a, b) => (SEVERITY_RANK[b.highestSeverity] ?? 0) - (SEVERITY_RANK[a.highestSeverity] ?? 0));
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
export function runFindingOwnerNotify(argv) {
|
|
76
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
77
|
+
console.log(`Usage: judges finding-owner-notify [options]
|
|
78
|
+
|
|
79
|
+
Generate ownership-based notification lists for findings.
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
--report <path> Path to verdict JSON
|
|
83
|
+
--owners <path> Path to owner mapping file (JSON or CODEOWNERS format)
|
|
84
|
+
--format <fmt> Output format: table (default) or json
|
|
85
|
+
-h, --help Show this help message`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const formatIdx = argv.indexOf("--format");
|
|
89
|
+
const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
|
|
90
|
+
const reportIdx = argv.indexOf("--report");
|
|
91
|
+
const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
|
|
92
|
+
? join(process.cwd(), argv[reportIdx + 1])
|
|
93
|
+
: join(process.cwd(), ".judges", "last-verdict.json");
|
|
94
|
+
const ownersIdx = argv.indexOf("--owners");
|
|
95
|
+
const ownersPath = ownersIdx !== -1 && argv[ownersIdx + 1]
|
|
96
|
+
? join(process.cwd(), argv[ownersIdx + 1])
|
|
97
|
+
: join(process.cwd(), ".judges", "owners.json");
|
|
98
|
+
if (!existsSync(reportPath)) {
|
|
99
|
+
console.log(`No report found at: ${reportPath}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const data = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
103
|
+
const mappings = loadOwnerMappings(ownersPath);
|
|
104
|
+
const notifications = buildNotifications(data, mappings);
|
|
105
|
+
if (format === "json") {
|
|
106
|
+
console.log(JSON.stringify(notifications, null, 2));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
console.log(`\n=== Owner Notifications (${notifications.length} owners) ===\n`);
|
|
110
|
+
if (notifications.length === 0) {
|
|
111
|
+
console.log("No findings to notify about.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
for (const n of notifications) {
|
|
115
|
+
console.log(` ${n.owner} (${n.findings.length} findings, highest: ${n.highestSeverity})`);
|
|
116
|
+
for (const f of n.findings) {
|
|
117
|
+
console.log(` [${f.severity.toUpperCase()}] ${f.ruleId}: ${f.title}`);
|
|
118
|
+
}
|
|
119
|
+
console.log();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=finding-owner-notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-owner-notify.js","sourceRoot":"","sources":["../../src/commands/finding-owner-notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-patch-chain.d.ts","sourceRoot":"","sources":["../../src/commands/finding-patch-chain.ts"],"names":[],"mappings":"AA2EA,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsDzD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
function buildPatchChains(verdict) {
|
|
4
|
+
const findings = verdict.findings ?? [];
|
|
5
|
+
const withPatches = [];
|
|
6
|
+
const withoutPatches = [];
|
|
7
|
+
for (const f of findings) {
|
|
8
|
+
const entry = {
|
|
9
|
+
ruleId: f.ruleId,
|
|
10
|
+
title: f.title,
|
|
11
|
+
severity: f.severity,
|
|
12
|
+
hasPatch: f.patch !== undefined && f.patch !== null,
|
|
13
|
+
patchPreview: f.patch !== undefined && f.patch !== null ? String(f.patch).slice(0, 80) : "",
|
|
14
|
+
lineNumbers: f.lineNumbers ?? [],
|
|
15
|
+
};
|
|
16
|
+
if (entry.hasPatch)
|
|
17
|
+
withPatches.push(entry);
|
|
18
|
+
else
|
|
19
|
+
withoutPatches.push(entry);
|
|
20
|
+
}
|
|
21
|
+
// Sort patches by line number (earliest first) for safe application order
|
|
22
|
+
withPatches.sort((a, b) => {
|
|
23
|
+
const aFirst = a.lineNumbers.length > 0 ? a.lineNumbers[0] : Infinity;
|
|
24
|
+
const bFirst = b.lineNumbers.length > 0 ? b.lineNumbers[0] : Infinity;
|
|
25
|
+
return bFirst - aFirst; // Apply from bottom up to avoid line shifts
|
|
26
|
+
});
|
|
27
|
+
const groups = [];
|
|
28
|
+
if (withPatches.length > 0) {
|
|
29
|
+
groups.push({
|
|
30
|
+
groupId: 1,
|
|
31
|
+
label: "Patchable findings (apply bottom-up)",
|
|
32
|
+
entries: withPatches,
|
|
33
|
+
applyOrder: "Apply from highest line number to lowest to avoid line shifts",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (withoutPatches.length > 0) {
|
|
37
|
+
groups.push({
|
|
38
|
+
groupId: groups.length + 1,
|
|
39
|
+
label: "Manual fixes required",
|
|
40
|
+
entries: withoutPatches,
|
|
41
|
+
applyOrder: "Fix in any order — no auto-patches available",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return groups;
|
|
45
|
+
}
|
|
46
|
+
export function runFindingPatchChain(argv) {
|
|
47
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
48
|
+
console.log(`Usage: judges finding-patch-chain [options]
|
|
49
|
+
|
|
50
|
+
Link and order patches across findings for safe application.
|
|
51
|
+
|
|
52
|
+
Options:
|
|
53
|
+
--report <path> Path to verdict JSON
|
|
54
|
+
--format <fmt> Output format: table (default) or json
|
|
55
|
+
-h, --help Show this help message`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const formatIdx = argv.indexOf("--format");
|
|
59
|
+
const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
|
|
60
|
+
const reportIdx = argv.indexOf("--report");
|
|
61
|
+
const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
|
|
62
|
+
? join(process.cwd(), argv[reportIdx + 1])
|
|
63
|
+
: join(process.cwd(), ".judges", "last-verdict.json");
|
|
64
|
+
if (!existsSync(reportPath)) {
|
|
65
|
+
console.log(`No report found at: ${reportPath}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const data = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
69
|
+
const groups = buildPatchChains(data);
|
|
70
|
+
if (format === "json") {
|
|
71
|
+
console.log(JSON.stringify(groups, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const totalPatched = groups.filter((g) => g.label.includes("Patchable")).reduce((s, g) => s + g.entries.length, 0);
|
|
75
|
+
const totalManual = groups.filter((g) => g.label.includes("Manual")).reduce((s, g) => s + g.entries.length, 0);
|
|
76
|
+
console.log(`\n=== Patch Chain (${totalPatched} patchable, ${totalManual} manual) ===\n`);
|
|
77
|
+
for (const group of groups) {
|
|
78
|
+
console.log(` Group #${group.groupId}: ${group.label}`);
|
|
79
|
+
console.log(` Order: ${group.applyOrder}\n`);
|
|
80
|
+
for (const e of group.entries) {
|
|
81
|
+
const lines = e.lineNumbers.length > 0 ? ` (L${e.lineNumbers.join(",")})` : "";
|
|
82
|
+
console.log(` [${e.severity.toUpperCase()}] ${e.ruleId}${lines}`);
|
|
83
|
+
console.log(` ${e.title}`);
|
|
84
|
+
if (e.patchPreview) {
|
|
85
|
+
console.log(` Patch: ${e.patchPreview}...`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=finding-patch-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-patch-chain.js","sourceRoot":"","sources":["../../src/commands/finding-patch-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"finding-risk-label.d.ts","sourceRoot":"","sources":["../../src/commands/finding-risk-label.ts"],"names":[],"mappings":"AAuDA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiDxD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
const RISK_PATTERNS = {
|
|
4
|
+
exploitable: ["injection", "xss", "rce", "deserialization", "ssrf", "command"],
|
|
5
|
+
"data-loss": ["leak", "exposure", "pii", "secret", "credential", "token"],
|
|
6
|
+
compliance: ["audit", "log", "gdpr", "hipaa", "pci", "compliance", "license"],
|
|
7
|
+
operational: ["perf", "memory", "timeout", "resource", "deadlock", "crash"],
|
|
8
|
+
reputational: ["quality", "smell", "complexity", "dead-code", "duplicate"],
|
|
9
|
+
};
|
|
10
|
+
function labelFindings(verdict) {
|
|
11
|
+
const results = [];
|
|
12
|
+
for (const f of verdict.findings ?? []) {
|
|
13
|
+
const combined = (f.ruleId + " " + f.title).toLowerCase();
|
|
14
|
+
const labels = [];
|
|
15
|
+
for (const [category, patterns] of Object.entries(RISK_PATTERNS)) {
|
|
16
|
+
for (const pattern of patterns) {
|
|
17
|
+
if (combined.includes(pattern)) {
|
|
18
|
+
labels.push(category);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// High/critical severity without specific match gets "operational"
|
|
24
|
+
if (labels.length === 0 && (f.severity === "critical" || f.severity === "high")) {
|
|
25
|
+
labels.push("operational");
|
|
26
|
+
}
|
|
27
|
+
results.push({ ruleId: f.ruleId, title: f.title, severity: f.severity, riskLabels: labels });
|
|
28
|
+
}
|
|
29
|
+
results.sort((a, b) => b.riskLabels.length - a.riskLabels.length);
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
export function runFindingRiskLabel(argv) {
|
|
33
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
34
|
+
console.log(`Usage: judges finding-risk-label [options]
|
|
35
|
+
|
|
36
|
+
Label findings with risk categories.
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
--report <path> Path to verdict JSON
|
|
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 reportIdx = argv.indexOf("--report");
|
|
47
|
+
const reportPath = reportIdx !== -1 && argv[reportIdx + 1]
|
|
48
|
+
? join(process.cwd(), argv[reportIdx + 1])
|
|
49
|
+
: join(process.cwd(), ".judges", "last-verdict.json");
|
|
50
|
+
if (!existsSync(reportPath)) {
|
|
51
|
+
console.log(`No report found at: ${reportPath}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const data = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
55
|
+
const labeled = labelFindings(data);
|
|
56
|
+
if (format === "json") {
|
|
57
|
+
console.log(JSON.stringify(labeled, null, 2));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const withLabels = labeled.filter((l) => l.riskLabels.length > 0);
|
|
61
|
+
console.log(`\n=== Risk Labels (${withLabels.length} labeled of ${labeled.length} findings) ===\n`);
|
|
62
|
+
if (withLabels.length === 0) {
|
|
63
|
+
console.log("No risk labels matched.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const entry of withLabels) {
|
|
67
|
+
console.log(` ${entry.severity.toUpperCase().padEnd(9)} ${entry.ruleId}`);
|
|
68
|
+
console.log(` ${entry.title}`);
|
|
69
|
+
console.log(` Labels: ${entry.riskLabels.join(", ")}`);
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=finding-risk-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-risk-label.js","sourceRoot":"","sources":["../../src/commands/finding-risk-label.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-config-health.d.ts","sourceRoot":"","sources":["../../src/commands/review-config-health.ts"],"names":[],"mappings":"AA6JA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAuC1D"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
function assessHealth(projectDir) {
|
|
4
|
+
const checks = [];
|
|
5
|
+
// Check for config file
|
|
6
|
+
const configPaths = [".judgesrc", ".judgesrc.json", "judges.config.json"];
|
|
7
|
+
const foundConfig = configPaths.find((p) => existsSync(join(projectDir, p)));
|
|
8
|
+
if (!foundConfig) {
|
|
9
|
+
checks.push({
|
|
10
|
+
check: "Config file",
|
|
11
|
+
status: "error",
|
|
12
|
+
detail: "No configuration file found",
|
|
13
|
+
suggestion: "Run `judges init` to create a configuration file",
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
try {
|
|
18
|
+
const raw = readFileSync(join(projectDir, foundConfig), "utf-8");
|
|
19
|
+
const cfg = JSON.parse(raw);
|
|
20
|
+
checks.push({
|
|
21
|
+
check: "Config file",
|
|
22
|
+
status: "healthy",
|
|
23
|
+
detail: `Found ${foundConfig}`,
|
|
24
|
+
suggestion: "",
|
|
25
|
+
});
|
|
26
|
+
// Check preset
|
|
27
|
+
if (!cfg["preset"]) {
|
|
28
|
+
checks.push({
|
|
29
|
+
check: "Preset configured",
|
|
30
|
+
status: "warning",
|
|
31
|
+
detail: "No preset defined",
|
|
32
|
+
suggestion: "Add a preset like 'security', 'quality', or 'full' for optimized defaults",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
checks.push({
|
|
37
|
+
check: "Preset configured",
|
|
38
|
+
status: "healthy",
|
|
39
|
+
detail: `Preset: ${String(cfg["preset"])}`,
|
|
40
|
+
suggestion: "",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Check minSeverity
|
|
44
|
+
if (!cfg["minSeverity"]) {
|
|
45
|
+
checks.push({
|
|
46
|
+
check: "Minimum severity",
|
|
47
|
+
status: "warning",
|
|
48
|
+
detail: "No minSeverity set — all severities reported",
|
|
49
|
+
suggestion: "Set minSeverity to 'low' or 'medium' to reduce noise",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
checks.push({
|
|
54
|
+
check: "Minimum severity",
|
|
55
|
+
status: "healthy",
|
|
56
|
+
detail: `minSeverity: ${String(cfg["minSeverity"])}`,
|
|
57
|
+
suggestion: "",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// Check disabled judges
|
|
61
|
+
const disabled = cfg["disabledJudges"];
|
|
62
|
+
if (Array.isArray(disabled) && disabled.length > 5) {
|
|
63
|
+
checks.push({
|
|
64
|
+
check: "Disabled judges",
|
|
65
|
+
status: "warning",
|
|
66
|
+
detail: `${disabled.length} judges disabled`,
|
|
67
|
+
suggestion: "Consider using a different preset instead of disabling many judges",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
checks.push({
|
|
72
|
+
check: "Disabled judges",
|
|
73
|
+
status: "healthy",
|
|
74
|
+
detail: Array.isArray(disabled) ? `${disabled.length} disabled` : "None disabled",
|
|
75
|
+
suggestion: "",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Check rule overrides
|
|
79
|
+
const overrides = cfg["ruleOverrides"];
|
|
80
|
+
if (overrides && typeof overrides === "object") {
|
|
81
|
+
const count = Object.keys(overrides).length;
|
|
82
|
+
checks.push({
|
|
83
|
+
check: "Rule overrides",
|
|
84
|
+
status: count > 20 ? "warning" : "healthy",
|
|
85
|
+
detail: `${count} rule overrides`,
|
|
86
|
+
suggestion: count > 20 ? "Many overrides — consider adjusting preset instead" : "",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
checks.push({
|
|
92
|
+
check: "Config parseable",
|
|
93
|
+
status: "error",
|
|
94
|
+
detail: `Config file ${foundConfig} has syntax errors`,
|
|
95
|
+
suggestion: "Fix JSON syntax in the config file",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check .judges directory
|
|
100
|
+
const judgesDir = join(projectDir, ".judges");
|
|
101
|
+
if (!existsSync(judgesDir)) {
|
|
102
|
+
checks.push({
|
|
103
|
+
check: "Judges directory",
|
|
104
|
+
status: "warning",
|
|
105
|
+
detail: "No .judges directory found",
|
|
106
|
+
suggestion: "Run a review to create the .judges directory with results",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
checks.push({
|
|
111
|
+
check: "Judges directory",
|
|
112
|
+
status: "healthy",
|
|
113
|
+
detail: ".judges directory exists",
|
|
114
|
+
suggestion: "",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Check .gitignore includes .judges
|
|
118
|
+
const gitignorePath = join(projectDir, ".gitignore");
|
|
119
|
+
if (existsSync(gitignorePath)) {
|
|
120
|
+
const gitignore = readFileSync(gitignorePath, "utf-8");
|
|
121
|
+
if (!gitignore.includes(".judges")) {
|
|
122
|
+
checks.push({
|
|
123
|
+
check: "Gitignore",
|
|
124
|
+
status: "warning",
|
|
125
|
+
detail: ".judges not in .gitignore",
|
|
126
|
+
suggestion: "Add .judges/ to .gitignore to avoid committing review artifacts",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
checks.push({
|
|
131
|
+
check: "Gitignore",
|
|
132
|
+
status: "healthy",
|
|
133
|
+
detail: ".judges directory excluded from git",
|
|
134
|
+
suggestion: "",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return checks;
|
|
139
|
+
}
|
|
140
|
+
export function runReviewConfigHealth(argv) {
|
|
141
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
142
|
+
console.log(`Usage: judges review-config-health [options]
|
|
143
|
+
|
|
144
|
+
Assess configuration health and suggest improvements.
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
--dir <path> Project directory (default: current)
|
|
148
|
+
--format <fmt> Output format: table (default) or json
|
|
149
|
+
-h, --help Show this help message`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const formatIdx = argv.indexOf("--format");
|
|
153
|
+
const format = formatIdx !== -1 && argv[formatIdx + 1] ? argv[formatIdx + 1] : "table";
|
|
154
|
+
const dirIdx = argv.indexOf("--dir");
|
|
155
|
+
const projectDir = dirIdx !== -1 && argv[dirIdx + 1] ? join(process.cwd(), argv[dirIdx + 1]) : process.cwd();
|
|
156
|
+
const checks = assessHealth(projectDir);
|
|
157
|
+
if (format === "json") {
|
|
158
|
+
console.log(JSON.stringify(checks, null, 2));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const errors = checks.filter((c) => c.status === "error").length;
|
|
162
|
+
const warnings = checks.filter((c) => c.status === "warning").length;
|
|
163
|
+
const healthy = checks.filter((c) => c.status === "healthy").length;
|
|
164
|
+
console.log(`\n=== Config Health (${healthy} healthy, ${warnings} warnings, ${errors} errors) ===\n`);
|
|
165
|
+
for (const c of checks) {
|
|
166
|
+
const icon = c.status === "healthy" ? "✓" : c.status === "warning" ? "!" : "✗";
|
|
167
|
+
console.log(` ${icon} ${c.check}: ${c.detail}`);
|
|
168
|
+
if (c.suggestion) {
|
|
169
|
+
console.log(` → ${c.suggestion}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=review-config-health.js.map
|