@kevinrabun/judges 3.62.0 → 3.64.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 +14 -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/auto-approve.d.ts +5 -0
- package/dist/commands/auto-approve.d.ts.map +1 -0
- package/dist/commands/auto-approve.js +189 -0
- package/dist/commands/auto-approve.js.map +1 -0
- package/dist/commands/diff-explain.d.ts +5 -0
- package/dist/commands/diff-explain.d.ts.map +1 -0
- package/dist/commands/diff-explain.js +143 -0
- package/dist/commands/diff-explain.js.map +1 -0
- package/dist/commands/finding-group.d.ts +16 -0
- package/dist/commands/finding-group.d.ts.map +1 -0
- package/dist/commands/finding-group.js +165 -0
- package/dist/commands/finding-group.js.map +1 -0
- package/dist/commands/fix-suggest.d.ts +5 -0
- package/dist/commands/fix-suggest.d.ts.map +1 -0
- package/dist/commands/fix-suggest.js +172 -0
- package/dist/commands/fix-suggest.js.map +1 -0
- package/dist/commands/ignore-list.d.ts +19 -0
- package/dist/commands/ignore-list.d.ts.map +1 -0
- package/dist/commands/ignore-list.js +166 -0
- package/dist/commands/ignore-list.js.map +1 -0
- package/dist/commands/incremental-review.d.ts +5 -0
- package/dist/commands/incremental-review.d.ts.map +1 -0
- package/dist/commands/incremental-review.js +240 -0
- package/dist/commands/incremental-review.js.map +1 -0
- package/dist/commands/multi-lang-review.d.ts +5 -0
- package/dist/commands/multi-lang-review.d.ts.map +1 -0
- package/dist/commands/multi-lang-review.js +231 -0
- package/dist/commands/multi-lang-review.js.map +1 -0
- package/dist/commands/review-cache.d.ts +23 -0
- package/dist/commands/review-cache.d.ts.map +1 -0
- package/dist/commands/review-cache.js +135 -0
- package/dist/commands/review-cache.js.map +1 -0
- package/dist/commands/review-log.d.ts +23 -0
- package/dist/commands/review-log.d.ts.map +1 -0
- package/dist/commands/review-log.js +165 -0
- package/dist/commands/review-log.js.map +1 -0
- package/dist/commands/review-priority.d.ts +5 -0
- package/dist/commands/review-priority.d.ts.map +1 -0
- package/dist/commands/review-priority.js +158 -0
- package/dist/commands/review-priority.js.map +1 -0
- package/dist/commands/review-profile.d.ts +5 -0
- package/dist/commands/review-profile.d.ts.map +1 -0
- package/dist/commands/review-profile.js +169 -0
- package/dist/commands/review-profile.js.map +1 -0
- package/dist/commands/review-stats.d.ts +5 -0
- package/dist/commands/review-stats.d.ts.map +1 -0
- package/dist/commands/review-stats.js +176 -0
- package/dist/commands/review-stats.js.map +1 -0
- package/dist/commands/review-summary.d.ts +5 -0
- package/dist/commands/review-summary.d.ts.map +1 -0
- package/dist/commands/review-summary.js +175 -0
- package/dist/commands/review-summary.js.map +1 -0
- package/dist/commands/review-template.d.ts +5 -0
- package/dist/commands/review-template.d.ts.map +1 -0
- package/dist/commands/review-template.js +213 -0
- package/dist/commands/review-template.js.map +1 -0
- package/dist/commands/rule-test.d.ts +5 -0
- package/dist/commands/rule-test.d.ts.map +1 -0
- package/dist/commands/rule-test.js +216 -0
- package/dist/commands/rule-test.js.map +1 -0
- package/dist/commands/team-config.d.ts +5 -0
- package/dist/commands/team-config.d.ts.map +1 -0
- package/dist/commands/team-config.js +235 -0
- package/dist/commands/team-config.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-summary — Generate PR-ready review summaries with key metrics.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
// ─── Metrics extraction ─────────────────────────────────────────────────────
|
|
6
|
+
function extractMetrics(verdict) {
|
|
7
|
+
const findings = verdict.findings || [];
|
|
8
|
+
const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
9
|
+
const ruleCounts = new Map();
|
|
10
|
+
for (const f of findings) {
|
|
11
|
+
const sev = f.severity || "low";
|
|
12
|
+
severityCounts[sev] = (severityCounts[sev] || 0) + 1;
|
|
13
|
+
const rid = f.ruleId || "unknown";
|
|
14
|
+
ruleCounts.set(rid, (ruleCounts.get(rid) || 0) + 1);
|
|
15
|
+
}
|
|
16
|
+
const topRules = [...ruleCounts.entries()]
|
|
17
|
+
.sort((a, b) => b[1] - a[1])
|
|
18
|
+
.slice(0, 5)
|
|
19
|
+
.map(([ruleId, count]) => ({ ruleId, count }));
|
|
20
|
+
return {
|
|
21
|
+
totalFindings: findings.length,
|
|
22
|
+
criticalCount: severityCounts["critical"],
|
|
23
|
+
highCount: severityCounts["high"],
|
|
24
|
+
mediumCount: severityCounts["medium"],
|
|
25
|
+
lowCount: severityCounts["low"],
|
|
26
|
+
judgeCount: verdict.evaluations?.length || 0,
|
|
27
|
+
overallScore: verdict.overallScore ?? 0,
|
|
28
|
+
overallVerdict: verdict.overallVerdict || "unknown",
|
|
29
|
+
topRules,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// ─── Formatters ─────────────────────────────────────────────────────────────
|
|
33
|
+
function severityEmoji(count, level) {
|
|
34
|
+
if (count === 0)
|
|
35
|
+
return `✅ 0 ${level}`;
|
|
36
|
+
if (level === "critical")
|
|
37
|
+
return `🔴 ${count} ${level}`;
|
|
38
|
+
if (level === "high")
|
|
39
|
+
return `🟠 ${count} ${level}`;
|
|
40
|
+
if (level === "medium")
|
|
41
|
+
return `🟡 ${count} ${level}`;
|
|
42
|
+
return `🔵 ${count} ${level}`;
|
|
43
|
+
}
|
|
44
|
+
function verdictEmoji(verdict) {
|
|
45
|
+
const map = {
|
|
46
|
+
pass: "✅ PASS",
|
|
47
|
+
fail: "❌ FAIL",
|
|
48
|
+
warning: "⚠️ WARNING",
|
|
49
|
+
};
|
|
50
|
+
return map[verdict] || verdict.toUpperCase();
|
|
51
|
+
}
|
|
52
|
+
function formatMarkdown(metrics) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
lines.push(`## Judges Review Summary`);
|
|
55
|
+
lines.push(``);
|
|
56
|
+
lines.push(`**Verdict:** ${verdictEmoji(metrics.overallVerdict)} | **Score:** ${metrics.overallScore}/100 | **Judges:** ${metrics.judgeCount}`);
|
|
57
|
+
lines.push(``);
|
|
58
|
+
lines.push(`| Severity | Count |`);
|
|
59
|
+
lines.push(`|----------|-------|`);
|
|
60
|
+
lines.push(`| ${severityEmoji(metrics.criticalCount, "critical")} | ${metrics.criticalCount} |`);
|
|
61
|
+
lines.push(`| ${severityEmoji(metrics.highCount, "high")} | ${metrics.highCount} |`);
|
|
62
|
+
lines.push(`| ${severityEmoji(metrics.mediumCount, "medium")} | ${metrics.mediumCount} |`);
|
|
63
|
+
lines.push(`| ${severityEmoji(metrics.lowCount, "low")} | ${metrics.lowCount} |`);
|
|
64
|
+
lines.push(`| **Total** | **${metrics.totalFindings}** |`);
|
|
65
|
+
if (metrics.topRules.length > 0) {
|
|
66
|
+
lines.push(``);
|
|
67
|
+
lines.push(`**Top Rules:**`);
|
|
68
|
+
for (const r of metrics.topRules) {
|
|
69
|
+
lines.push(`- \`${r.ruleId}\` (${r.count})`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
lines.push(``);
|
|
73
|
+
lines.push(`---`);
|
|
74
|
+
lines.push(`*Generated by [Judges Panel](https://github.com/KevinRabun/judges)*`);
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
function formatSlack(metrics) {
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(`*Judges Review Summary*`);
|
|
80
|
+
lines.push(`Verdict: ${verdictEmoji(metrics.overallVerdict)} | Score: ${metrics.overallScore}/100 | Judges: ${metrics.judgeCount}`);
|
|
81
|
+
lines.push(``);
|
|
82
|
+
lines.push(`${severityEmoji(metrics.criticalCount, "critical")} | ${severityEmoji(metrics.highCount, "high")} | ${severityEmoji(metrics.mediumCount, "medium")} | ${severityEmoji(metrics.lowCount, "low")}`);
|
|
83
|
+
lines.push(`Total: ${metrics.totalFindings} findings`);
|
|
84
|
+
if (metrics.topRules.length > 0) {
|
|
85
|
+
lines.push(``);
|
|
86
|
+
lines.push(`Top rules: ${metrics.topRules.map((r) => `${r.ruleId} (${r.count})`).join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
function formatOneLiner(metrics) {
|
|
91
|
+
return `${verdictEmoji(metrics.overallVerdict)} Score: ${metrics.overallScore}/100 | ${metrics.totalFindings} findings (${metrics.criticalCount}C/${metrics.highCount}H/${metrics.mediumCount}M/${metrics.lowCount}L) | ${metrics.judgeCount} judges`;
|
|
92
|
+
}
|
|
93
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
94
|
+
export function runReviewSummary(argv) {
|
|
95
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
96
|
+
console.log(`
|
|
97
|
+
judges review-summary — Generate PR-ready review summary
|
|
98
|
+
|
|
99
|
+
Usage:
|
|
100
|
+
judges review-summary --input verdict.json
|
|
101
|
+
judges review-summary --input verdict.json --style markdown
|
|
102
|
+
judges review-summary --input verdict.json --style slack
|
|
103
|
+
judges review-summary --format json
|
|
104
|
+
|
|
105
|
+
Options:
|
|
106
|
+
--input <file> JSON file with TribunalVerdict (required)
|
|
107
|
+
--style <type> Output style: text, markdown, slack, oneliner (default: text)
|
|
108
|
+
--format json Raw JSON metrics output
|
|
109
|
+
--help, -h Show this help
|
|
110
|
+
|
|
111
|
+
Styles:
|
|
112
|
+
text Plain text summary (default)
|
|
113
|
+
markdown GitHub PR description format
|
|
114
|
+
slack Slack message format
|
|
115
|
+
oneliner Single-line summary
|
|
116
|
+
|
|
117
|
+
Generates a concise, copy-pasteable review summary suitable for
|
|
118
|
+
PR descriptions, Slack messages, or CI logs.
|
|
119
|
+
`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const inputPath = argv.find((_a, i) => argv[i - 1] === "--input");
|
|
123
|
+
const style = argv.find((_a, i) => argv[i - 1] === "--style") || "text";
|
|
124
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
125
|
+
if (!inputPath) {
|
|
126
|
+
console.error("Error: --input is required. Provide a JSON verdict file.");
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let verdict;
|
|
131
|
+
try {
|
|
132
|
+
const raw = readFileSync(inputPath, "utf-8");
|
|
133
|
+
verdict = JSON.parse(raw);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
console.error(`Error: Cannot read or parse ${inputPath}`);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const metrics = extractMetrics(verdict);
|
|
141
|
+
if (format === "json") {
|
|
142
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
switch (style) {
|
|
146
|
+
case "markdown":
|
|
147
|
+
console.log(formatMarkdown(metrics));
|
|
148
|
+
break;
|
|
149
|
+
case "slack":
|
|
150
|
+
console.log(formatSlack(metrics));
|
|
151
|
+
break;
|
|
152
|
+
case "oneliner":
|
|
153
|
+
console.log(formatOneLiner(metrics));
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
console.log(`\n Review Summary\n ─────────────────────────────`);
|
|
157
|
+
console.log(` Verdict: ${verdictEmoji(metrics.overallVerdict)}`);
|
|
158
|
+
console.log(` Score: ${metrics.overallScore}/100`);
|
|
159
|
+
console.log(` Judges: ${metrics.judgeCount}`);
|
|
160
|
+
console.log(` Total findings: ${metrics.totalFindings}`);
|
|
161
|
+
console.log(` ${severityEmoji(metrics.criticalCount, "critical")}`);
|
|
162
|
+
console.log(` ${severityEmoji(metrics.highCount, "high")}`);
|
|
163
|
+
console.log(` ${severityEmoji(metrics.mediumCount, "medium")}`);
|
|
164
|
+
console.log(` ${severityEmoji(metrics.lowCount, "low")}`);
|
|
165
|
+
if (metrics.topRules.length > 0) {
|
|
166
|
+
console.log(`\n Top Rules:`);
|
|
167
|
+
for (const r of metrics.topRules) {
|
|
168
|
+
console.log(` ${r.ruleId} (${r.count})`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
console.log();
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=review-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-summary.js","sourceRoot":"","sources":["../../src/commands/review-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-template.d.ts","sourceRoot":"","sources":["../../src/commands/review-template.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqHH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqItD"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-template — Reusable review templates for common workflows.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
// ─── Built-in templates ────────────────────────────────────────────────────
|
|
7
|
+
const BUILTIN_TEMPLATES = [
|
|
8
|
+
{
|
|
9
|
+
id: "security-audit",
|
|
10
|
+
name: "Security Audit",
|
|
11
|
+
description: "Deep security review focusing on OWASP Top 10 and CWE patterns",
|
|
12
|
+
judges: ["data-security", "cybersecurity", "authentication", "input-guard"],
|
|
13
|
+
preset: "security-only",
|
|
14
|
+
minSeverity: "medium",
|
|
15
|
+
focusRules: [],
|
|
16
|
+
excludeRules: [],
|
|
17
|
+
outputFormat: "sarif",
|
|
18
|
+
failOnFindings: true,
|
|
19
|
+
tags: ["security", "compliance"],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "pr-review",
|
|
23
|
+
name: "PR Review",
|
|
24
|
+
description: "Standard pull request review for code quality and correctness",
|
|
25
|
+
judges: [],
|
|
26
|
+
preset: "recommended",
|
|
27
|
+
minSeverity: "low",
|
|
28
|
+
focusRules: [],
|
|
29
|
+
excludeRules: [],
|
|
30
|
+
outputFormat: "text",
|
|
31
|
+
failOnFindings: false,
|
|
32
|
+
tags: ["pr", "quality"],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "pre-deploy",
|
|
36
|
+
name: "Pre-Deploy Check",
|
|
37
|
+
description: "Critical checks before deployment — security and reliability only",
|
|
38
|
+
judges: ["data-security", "cybersecurity", "reliability", "error-handling"],
|
|
39
|
+
preset: "strict",
|
|
40
|
+
minSeverity: "high",
|
|
41
|
+
focusRules: [],
|
|
42
|
+
excludeRules: [],
|
|
43
|
+
outputFormat: "json",
|
|
44
|
+
failOnFindings: true,
|
|
45
|
+
tags: ["deploy", "ci"],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "ai-code-review",
|
|
49
|
+
name: "AI Code Review",
|
|
50
|
+
description: "Specialized review for AI-generated code patterns",
|
|
51
|
+
judges: [],
|
|
52
|
+
preset: "strict",
|
|
53
|
+
minSeverity: "low",
|
|
54
|
+
focusRules: [],
|
|
55
|
+
excludeRules: [],
|
|
56
|
+
outputFormat: "text",
|
|
57
|
+
failOnFindings: false,
|
|
58
|
+
tags: ["ai", "generated"],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "quick-scan",
|
|
62
|
+
name: "Quick Scan",
|
|
63
|
+
description: "Fast scan for critical issues only",
|
|
64
|
+
judges: ["data-security", "cybersecurity"],
|
|
65
|
+
preset: "lenient",
|
|
66
|
+
minSeverity: "critical",
|
|
67
|
+
focusRules: [],
|
|
68
|
+
excludeRules: [],
|
|
69
|
+
outputFormat: "text",
|
|
70
|
+
failOnFindings: false,
|
|
71
|
+
tags: ["quick", "critical"],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
// ─── Template storage ──────────────────────────────────────────────────────
|
|
75
|
+
const TEMPLATE_DIR = join(".judges", "templates");
|
|
76
|
+
function loadCustomTemplates() {
|
|
77
|
+
if (!existsSync(TEMPLATE_DIR))
|
|
78
|
+
return [];
|
|
79
|
+
const files = readdirSync(TEMPLATE_DIR);
|
|
80
|
+
const templates = [];
|
|
81
|
+
for (const f of files) {
|
|
82
|
+
if (!f.endsWith(".json"))
|
|
83
|
+
continue;
|
|
84
|
+
try {
|
|
85
|
+
const t = JSON.parse(readFileSync(join(TEMPLATE_DIR, f), "utf-8"));
|
|
86
|
+
templates.push(t);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Skip invalid files
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return templates;
|
|
93
|
+
}
|
|
94
|
+
function getAllTemplates() {
|
|
95
|
+
return [...BUILTIN_TEMPLATES, ...loadCustomTemplates()];
|
|
96
|
+
}
|
|
97
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
98
|
+
export function runReviewTemplate(argv) {
|
|
99
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
100
|
+
console.log(`
|
|
101
|
+
judges review-template — Reusable review templates
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
judges review-template list List all templates
|
|
105
|
+
judges review-template show --id security-audit Show template details
|
|
106
|
+
judges review-template create --id my-template Create custom template
|
|
107
|
+
judges review-template export --id my-template Export template to JSON
|
|
108
|
+
judges review-template --format json JSON output
|
|
109
|
+
|
|
110
|
+
Subcommands:
|
|
111
|
+
list List built-in and custom templates
|
|
112
|
+
show Show template details
|
|
113
|
+
create Create a new custom template
|
|
114
|
+
export Export template to stdout
|
|
115
|
+
|
|
116
|
+
Options:
|
|
117
|
+
--id <id> Template ID (required for show/create/export)
|
|
118
|
+
--name <name> Template display name (for create)
|
|
119
|
+
--desc <text> Template description (for create)
|
|
120
|
+
--preset <name> Preset to use (for create)
|
|
121
|
+
--format json JSON output
|
|
122
|
+
--help, -h Show this help
|
|
123
|
+
|
|
124
|
+
Built-in templates: security-audit, pr-review, pre-deploy, ai-code-review, quick-scan.
|
|
125
|
+
Custom templates are stored in .judges/templates/.
|
|
126
|
+
`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
130
|
+
const templateId = argv.find((_a, i) => argv[i - 1] === "--id");
|
|
131
|
+
const subcommand = argv.find((a) => ["list", "show", "create", "export"].includes(a)) || "list";
|
|
132
|
+
const allTemplates = getAllTemplates();
|
|
133
|
+
if (subcommand === "list") {
|
|
134
|
+
if (format === "json") {
|
|
135
|
+
console.log(JSON.stringify({
|
|
136
|
+
templates: allTemplates.map((t) => ({ id: t.id, name: t.name, description: t.description, tags: t.tags })),
|
|
137
|
+
}, null, 2));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
console.log(`\n Review Templates (${allTemplates.length})\n ─────────────────────────────`);
|
|
141
|
+
for (const t of allTemplates) {
|
|
142
|
+
const tags = t.tags.length > 0 ? ` [${t.tags.join(", ")}]` : "";
|
|
143
|
+
console.log(` 📋 ${t.id} — ${t.name}${tags}`);
|
|
144
|
+
console.log(` ${t.description}`);
|
|
145
|
+
}
|
|
146
|
+
console.log();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (!templateId) {
|
|
150
|
+
console.error("Error: --id is required.");
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (subcommand === "create") {
|
|
155
|
+
const existing = allTemplates.find((t) => t.id === templateId);
|
|
156
|
+
if (existing) {
|
|
157
|
+
console.error(`Error: Template '${templateId}' already exists.`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const name = argv.find((_a, i) => argv[i - 1] === "--name") || templateId;
|
|
162
|
+
const desc = argv.find((_a, i) => argv[i - 1] === "--desc") || "Custom review template";
|
|
163
|
+
const preset = argv.find((_a, i) => argv[i - 1] === "--preset") || "recommended";
|
|
164
|
+
const template = {
|
|
165
|
+
id: templateId,
|
|
166
|
+
name,
|
|
167
|
+
description: desc,
|
|
168
|
+
judges: [],
|
|
169
|
+
preset,
|
|
170
|
+
minSeverity: "low",
|
|
171
|
+
focusRules: [],
|
|
172
|
+
excludeRules: [],
|
|
173
|
+
outputFormat: "text",
|
|
174
|
+
failOnFindings: false,
|
|
175
|
+
tags: ["custom"],
|
|
176
|
+
};
|
|
177
|
+
const filePath = join(TEMPLATE_DIR, `${templateId}.json`);
|
|
178
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
179
|
+
writeFileSync(filePath, JSON.stringify(template, null, 2), "utf-8");
|
|
180
|
+
console.log(`Created template '${templateId}' in ${filePath}.`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const template = allTemplates.find((t) => t.id === templateId);
|
|
184
|
+
if (!template) {
|
|
185
|
+
console.error(`Error: Template '${templateId}' not found.`);
|
|
186
|
+
process.exitCode = 1;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (subcommand === "export") {
|
|
190
|
+
console.log(JSON.stringify(template, null, 2));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Show
|
|
194
|
+
if (format === "json") {
|
|
195
|
+
console.log(JSON.stringify(template, null, 2));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
console.log(`\n Template: ${template.name}\n ─────────────────────────────`);
|
|
199
|
+
console.log(` ID: ${template.id}`);
|
|
200
|
+
console.log(` Description: ${template.description}`);
|
|
201
|
+
console.log(` Preset: ${template.preset}`);
|
|
202
|
+
console.log(` Min severity: ${template.minSeverity}`);
|
|
203
|
+
console.log(` Output format: ${template.outputFormat}`);
|
|
204
|
+
console.log(` Fail on findings: ${template.failOnFindings}`);
|
|
205
|
+
if (template.judges.length > 0) {
|
|
206
|
+
console.log(` Judges: ${template.judges.join(", ")}`);
|
|
207
|
+
}
|
|
208
|
+
if (template.tags.length > 0) {
|
|
209
|
+
console.log(` Tags: ${template.tags.join(", ")}`);
|
|
210
|
+
}
|
|
211
|
+
console.log();
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=review-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-template.js","sourceRoot":"","sources":["../../src/commands/review-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-test.d.ts","sourceRoot":"","sources":["../../src/commands/rule-test.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6HH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAsIhD"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule-test — Test custom rules against sample code before deployment.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── Rule loading ───────────────────────────────────────────────────────────
|
|
7
|
+
function loadRules(rulesPath) {
|
|
8
|
+
const raw = readFileSync(rulesPath, "utf-8");
|
|
9
|
+
const parsed = JSON.parse(raw);
|
|
10
|
+
const rules = Array.isArray(parsed) ? parsed : parsed.rules || [];
|
|
11
|
+
return rules;
|
|
12
|
+
}
|
|
13
|
+
// ─── File collection ────────────────────────────────────────────────────────
|
|
14
|
+
function collectFiles(dirPath) {
|
|
15
|
+
const results = [];
|
|
16
|
+
if (!existsSync(dirPath))
|
|
17
|
+
return results;
|
|
18
|
+
const entries = readdirSync(dirPath);
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const fullPath = join(dirPath, entry);
|
|
21
|
+
try {
|
|
22
|
+
const st = statSync(fullPath);
|
|
23
|
+
if (st.isDirectory()) {
|
|
24
|
+
results.push(...collectFiles(fullPath));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
results.push(fullPath);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// skip inaccessible entries
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
// ─── Language matching ──────────────────────────────────────────────────────
|
|
37
|
+
const EXT_TO_LANG = {
|
|
38
|
+
".ts": "typescript",
|
|
39
|
+
".tsx": "typescript",
|
|
40
|
+
".js": "javascript",
|
|
41
|
+
".jsx": "javascript",
|
|
42
|
+
".py": "python",
|
|
43
|
+
".go": "go",
|
|
44
|
+
".rs": "rust",
|
|
45
|
+
".java": "java",
|
|
46
|
+
".cs": "csharp",
|
|
47
|
+
".cpp": "cpp",
|
|
48
|
+
".c": "c",
|
|
49
|
+
".rb": "ruby",
|
|
50
|
+
".php": "php",
|
|
51
|
+
};
|
|
52
|
+
function matchesLanguage(filePath, languages) {
|
|
53
|
+
if (!languages || languages.length === 0)
|
|
54
|
+
return true;
|
|
55
|
+
const ext = extname(filePath).toLowerCase();
|
|
56
|
+
const lang = EXT_TO_LANG[ext] || ext.slice(1);
|
|
57
|
+
return languages.includes(lang);
|
|
58
|
+
}
|
|
59
|
+
// ─── Rule testing ───────────────────────────────────────────────────────────
|
|
60
|
+
function testRule(rule, files) {
|
|
61
|
+
const results = [];
|
|
62
|
+
let regex;
|
|
63
|
+
try {
|
|
64
|
+
regex = new RegExp(rule.pattern, "gi");
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
console.error(` ❌ Rule '${rule.id}' has invalid regex: ${rule.pattern}`);
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
if (!matchesLanguage(file, rule.languages))
|
|
72
|
+
continue;
|
|
73
|
+
let content;
|
|
74
|
+
try {
|
|
75
|
+
content = readFileSync(file, "utf-8");
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const lines = content.split("\n");
|
|
81
|
+
const matches = [];
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
regex.lastIndex = 0;
|
|
84
|
+
if (regex.test(lines[i])) {
|
|
85
|
+
matches.push({ line: i + 1, content: lines[i].trim().slice(0, 120) });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
results.push({
|
|
89
|
+
ruleId: rule.id,
|
|
90
|
+
file,
|
|
91
|
+
matches,
|
|
92
|
+
matched: matches.length > 0,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
98
|
+
export function runRuleTest(argv) {
|
|
99
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
100
|
+
console.log(`
|
|
101
|
+
judges rule-test — Test custom rules against sample code
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
judges rule-test --rules custom-rules.json --fixtures tests/fixtures
|
|
105
|
+
judges rule-test --rules custom-rules.json --file sample.ts
|
|
106
|
+
judges rule-test --rules custom-rules.json --fixtures src --format json
|
|
107
|
+
|
|
108
|
+
Options:
|
|
109
|
+
--rules <file> JSON file with custom rules (required)
|
|
110
|
+
--fixtures <dir> Directory with test fixture files
|
|
111
|
+
--file <path> Test against a single file
|
|
112
|
+
--rule <id> Test only a specific rule by ID
|
|
113
|
+
--format json JSON output
|
|
114
|
+
--help, -h Show this help
|
|
115
|
+
|
|
116
|
+
Validates custom rules against real code before deployment. Ensures
|
|
117
|
+
patterns match expected findings and catch the intended issues.
|
|
118
|
+
`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const rulesPath = argv.find((_a, i) => argv[i - 1] === "--rules");
|
|
122
|
+
const fixturesDir = argv.find((_a, i) => argv[i - 1] === "--fixtures");
|
|
123
|
+
const singleFile = argv.find((_a, i) => argv[i - 1] === "--file");
|
|
124
|
+
const ruleFilter = argv.find((_a, i) => argv[i - 1] === "--rule");
|
|
125
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
126
|
+
if (!rulesPath) {
|
|
127
|
+
console.error("Error: --rules is required. Provide a custom rules JSON file.");
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!existsSync(rulesPath)) {
|
|
132
|
+
console.error(`Error: Rules file not found: ${rulesPath}`);
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let rules;
|
|
137
|
+
try {
|
|
138
|
+
rules = loadRules(rulesPath);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
console.error(`Error: Cannot parse rules file: ${rulesPath}`);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (ruleFilter) {
|
|
146
|
+
rules = rules.filter((r) => r.id === ruleFilter);
|
|
147
|
+
if (rules.length === 0) {
|
|
148
|
+
console.error(`Error: Rule '${ruleFilter}' not found in ${rulesPath}`);
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Gather target files
|
|
154
|
+
let files;
|
|
155
|
+
if (singleFile) {
|
|
156
|
+
if (!existsSync(singleFile)) {
|
|
157
|
+
console.error(`Error: File not found: ${singleFile}`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
files = [singleFile];
|
|
162
|
+
}
|
|
163
|
+
else if (fixturesDir) {
|
|
164
|
+
files = collectFiles(fixturesDir);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.error("Error: Either --fixtures or --file is required.");
|
|
168
|
+
process.exitCode = 1;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (files.length === 0) {
|
|
172
|
+
console.log("No files found to test against.");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Run tests
|
|
176
|
+
const allResults = [];
|
|
177
|
+
for (const rule of rules) {
|
|
178
|
+
allResults.push(...testRule(rule, files));
|
|
179
|
+
}
|
|
180
|
+
const matchedResults = allResults.filter((r) => r.matched);
|
|
181
|
+
const rulesWithMatches = new Set(matchedResults.map((r) => r.ruleId));
|
|
182
|
+
if (format === "json") {
|
|
183
|
+
console.log(JSON.stringify({
|
|
184
|
+
rulesCount: rules.length,
|
|
185
|
+
filesCount: files.length,
|
|
186
|
+
matchedRules: rulesWithMatches.size,
|
|
187
|
+
totalMatches: matchedResults.reduce((s, r) => s + r.matches.length, 0),
|
|
188
|
+
results: matchedResults.map((r) => ({
|
|
189
|
+
ruleId: r.ruleId,
|
|
190
|
+
file: r.file,
|
|
191
|
+
matchCount: r.matches.length,
|
|
192
|
+
matches: r.matches,
|
|
193
|
+
})),
|
|
194
|
+
}, null, 2));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
console.log(`\n Rule Test Results\n ─────────────────────────────`);
|
|
198
|
+
console.log(` Rules tested: ${rules.length}`);
|
|
199
|
+
console.log(` Files scanned: ${files.length}`);
|
|
200
|
+
console.log(` Rules with matches: ${rulesWithMatches.size}\n`);
|
|
201
|
+
for (const rule of rules) {
|
|
202
|
+
const ruleResults = matchedResults.filter((r) => r.ruleId === rule.id);
|
|
203
|
+
const totalMatches = ruleResults.reduce((s, r) => s + r.matches.length, 0);
|
|
204
|
+
const icon = totalMatches > 0 ? "✅" : "⬜";
|
|
205
|
+
console.log(` ${icon} ${rule.id} — ${totalMatches} match(es) across ${ruleResults.length} file(s)`);
|
|
206
|
+
console.log(` Pattern: ${rule.pattern}`);
|
|
207
|
+
console.log(` Severity: ${rule.severity}`);
|
|
208
|
+
for (const rr of ruleResults.slice(0, 3)) {
|
|
209
|
+
for (const m of rr.matches.slice(0, 3)) {
|
|
210
|
+
console.log(` L${m.line}: ${m.content}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.log();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=rule-test.js.map
|