@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +112 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/auto-approve.d.ts +5 -0
  6. package/dist/commands/auto-approve.d.ts.map +1 -0
  7. package/dist/commands/auto-approve.js +189 -0
  8. package/dist/commands/auto-approve.js.map +1 -0
  9. package/dist/commands/diff-explain.d.ts +5 -0
  10. package/dist/commands/diff-explain.d.ts.map +1 -0
  11. package/dist/commands/diff-explain.js +143 -0
  12. package/dist/commands/diff-explain.js.map +1 -0
  13. package/dist/commands/finding-group.d.ts +16 -0
  14. package/dist/commands/finding-group.d.ts.map +1 -0
  15. package/dist/commands/finding-group.js +165 -0
  16. package/dist/commands/finding-group.js.map +1 -0
  17. package/dist/commands/fix-suggest.d.ts +5 -0
  18. package/dist/commands/fix-suggest.d.ts.map +1 -0
  19. package/dist/commands/fix-suggest.js +172 -0
  20. package/dist/commands/fix-suggest.js.map +1 -0
  21. package/dist/commands/ignore-list.d.ts +19 -0
  22. package/dist/commands/ignore-list.d.ts.map +1 -0
  23. package/dist/commands/ignore-list.js +166 -0
  24. package/dist/commands/ignore-list.js.map +1 -0
  25. package/dist/commands/incremental-review.d.ts +5 -0
  26. package/dist/commands/incremental-review.d.ts.map +1 -0
  27. package/dist/commands/incremental-review.js +240 -0
  28. package/dist/commands/incremental-review.js.map +1 -0
  29. package/dist/commands/multi-lang-review.d.ts +5 -0
  30. package/dist/commands/multi-lang-review.d.ts.map +1 -0
  31. package/dist/commands/multi-lang-review.js +231 -0
  32. package/dist/commands/multi-lang-review.js.map +1 -0
  33. package/dist/commands/review-cache.d.ts +23 -0
  34. package/dist/commands/review-cache.d.ts.map +1 -0
  35. package/dist/commands/review-cache.js +135 -0
  36. package/dist/commands/review-cache.js.map +1 -0
  37. package/dist/commands/review-log.d.ts +23 -0
  38. package/dist/commands/review-log.d.ts.map +1 -0
  39. package/dist/commands/review-log.js +165 -0
  40. package/dist/commands/review-log.js.map +1 -0
  41. package/dist/commands/review-priority.d.ts +5 -0
  42. package/dist/commands/review-priority.d.ts.map +1 -0
  43. package/dist/commands/review-priority.js +158 -0
  44. package/dist/commands/review-priority.js.map +1 -0
  45. package/dist/commands/review-profile.d.ts +5 -0
  46. package/dist/commands/review-profile.d.ts.map +1 -0
  47. package/dist/commands/review-profile.js +169 -0
  48. package/dist/commands/review-profile.js.map +1 -0
  49. package/dist/commands/review-stats.d.ts +5 -0
  50. package/dist/commands/review-stats.d.ts.map +1 -0
  51. package/dist/commands/review-stats.js +176 -0
  52. package/dist/commands/review-stats.js.map +1 -0
  53. package/dist/commands/review-summary.d.ts +5 -0
  54. package/dist/commands/review-summary.d.ts.map +1 -0
  55. package/dist/commands/review-summary.js +175 -0
  56. package/dist/commands/review-summary.js.map +1 -0
  57. package/dist/commands/review-template.d.ts +5 -0
  58. package/dist/commands/review-template.d.ts.map +1 -0
  59. package/dist/commands/review-template.js +213 -0
  60. package/dist/commands/review-template.js.map +1 -0
  61. package/dist/commands/rule-test.d.ts +5 -0
  62. package/dist/commands/rule-test.d.ts.map +1 -0
  63. package/dist/commands/rule-test.js +216 -0
  64. package/dist/commands/rule-test.js.map +1 -0
  65. package/dist/commands/team-config.d.ts +5 -0
  66. package/dist/commands/team-config.d.ts.map +1 -0
  67. package/dist/commands/team-config.js +235 -0
  68. package/dist/commands/team-config.js.map +1 -0
  69. package/package.json +1 -1
  70. 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,5 @@
1
+ /**
2
+ * Review-template — Reusable review templates for common workflows.
3
+ */
4
+ export declare function runReviewTemplate(argv: string[]): void;
5
+ //# sourceMappingURL=review-template.d.ts.map
@@ -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":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAkBrC,8EAA8E;AAE9E,MAAM,iBAAiB,GAAqB;IAC1C;QACE,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,gEAAgE;QAC7E,MAAM,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,CAAC;QAC3E,MAAM,EAAE,eAAe;QACvB,WAAW,EAAE,QAAQ;QACrB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,OAAO;QACrB,cAAc,EAAE,IAAI;QACpB,IAAI,EAAE,CAAC,UAAU,EAAE,YAAY,CAAC;KACjC;IACD;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,+DAA+D;QAC5E,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,aAAa;QACrB,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,KAAK;QACrB,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC;KACxB;IACD;QACE,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,mEAAmE;QAChF,MAAM,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,CAAC;QAC3E,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,MAAM;QACnB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,IAAI;QACpB,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;KACvB;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,mDAAmD;QAChE,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,KAAK;QACrB,IAAI,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC;KAC1B;IACD;QACE,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,oCAAoC;QACjD,MAAM,EAAE,CAAC,eAAe,EAAE,eAAe,CAAC;QAC1C,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,UAAU;QACvB,UAAU,EAAE,EAAE;QACd,YAAY,EAAE,EAAE;QAChB,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,KAAK;QACrB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC;KAC5B;CACF,CAAC;AAEF,8EAA8E;AAE9E,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAElD,SAAS,mBAAmB;IAC1B,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAwB,CAAC;IAC/D,MAAM,SAAS,GAAqB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAmB,CAAC;YACrF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,CAAC,GAAG,iBAAiB,EAAE,GAAG,mBAAmB,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Bf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAC1F,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;IAChF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAEhG,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IAEvC,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC1B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;gBACE,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;aAC3G,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,yBAAyB,YAAY,CAAC,MAAM,oCAAoC,CAAC,CAAC;QAC9F,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO;IACT,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QAC/D,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,oBAAoB,UAAU,mBAAmB,CAAC,CAAC;YACjE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,UAAU,CAAC;QAC1F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,wBAAwB,CAAC;QACxG,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,aAAa,CAAC;QAEjG,MAAM,QAAQ,GAAmB;YAC/B,EAAE,EAAE,UAAU;YACd,IAAI;YACJ,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,EAAE;YACV,MAAM;YACN,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,EAAE;YACd,YAAY,EAAE,EAAE;YAChB,YAAY,EAAE,MAAM;YACpB,cAAc,EAAE,KAAK;YACrB,IAAI,EAAE,CAAC,QAAQ,CAAC;SACjB,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,UAAU,OAAO,CAAC,CAAC;QAC1D,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,qBAAqB,UAAU,QAAQ,QAAQ,GAAG,CAAC,CAAC;QAChE,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,oBAAoB,UAAU,cAAc,CAAC,CAAC;QAC5D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,OAAO;IACP,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,CAAC,IAAI,mCAAmC,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CAAC,WAAW,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,oBAAoB,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,qBAAqB,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,sBAAsB,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,yBAAyB,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC;IAChE,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,aAAa,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Rule-test — Test custom rules against sample code before deployment.
3
+ */
4
+ export declare function runRuleTest(argv: string[]): void;
5
+ //# sourceMappingURL=rule-test.d.ts.map
@@ -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