@kevinrabun/judges 3.40.0 → 3.42.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 (94) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +133 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/auto-calibrate.d.ts +15 -0
  6. package/dist/commands/auto-calibrate.d.ts.map +1 -0
  7. package/dist/commands/auto-calibrate.js +107 -0
  8. package/dist/commands/auto-calibrate.js.map +1 -0
  9. package/dist/commands/auto-triage.d.ts +32 -0
  10. package/dist/commands/auto-triage.d.ts.map +1 -0
  11. package/dist/commands/auto-triage.js +126 -0
  12. package/dist/commands/auto-triage.js.map +1 -0
  13. package/dist/commands/config-migrate.d.ts +44 -0
  14. package/dist/commands/config-migrate.d.ts.map +1 -0
  15. package/dist/commands/config-migrate.js +241 -0
  16. package/dist/commands/config-migrate.js.map +1 -0
  17. package/dist/commands/coverage-map.d.ts +23 -0
  18. package/dist/commands/coverage-map.d.ts.map +1 -0
  19. package/dist/commands/coverage-map.js +223 -0
  20. package/dist/commands/coverage-map.js.map +1 -0
  21. package/dist/commands/dedup-report.d.ts +13 -0
  22. package/dist/commands/dedup-report.d.ts.map +1 -0
  23. package/dist/commands/dedup-report.js +138 -0
  24. package/dist/commands/dedup-report.js.map +1 -0
  25. package/dist/commands/dep-audit.d.ts +53 -0
  26. package/dist/commands/dep-audit.d.ts.map +1 -0
  27. package/dist/commands/dep-audit.js +278 -0
  28. package/dist/commands/dep-audit.js.map +1 -0
  29. package/dist/commands/deprecated.d.ts +48 -0
  30. package/dist/commands/deprecated.d.ts.map +1 -0
  31. package/dist/commands/deprecated.js +202 -0
  32. package/dist/commands/deprecated.js.map +1 -0
  33. package/dist/commands/diff-only.d.ts +34 -0
  34. package/dist/commands/diff-only.d.ts.map +1 -0
  35. package/dist/commands/diff-only.js +152 -0
  36. package/dist/commands/diff-only.js.map +1 -0
  37. package/dist/commands/fix-pr.d.ts +23 -0
  38. package/dist/commands/fix-pr.d.ts.map +1 -0
  39. package/dist/commands/fix-pr.js +323 -0
  40. package/dist/commands/fix-pr.js.map +1 -0
  41. package/dist/commands/group-findings.d.ts +23 -0
  42. package/dist/commands/group-findings.d.ts.map +1 -0
  43. package/dist/commands/group-findings.js +155 -0
  44. package/dist/commands/group-findings.js.map +1 -0
  45. package/dist/commands/interactive-fix.d.ts +23 -0
  46. package/dist/commands/interactive-fix.d.ts.map +1 -0
  47. package/dist/commands/interactive-fix.js +140 -0
  48. package/dist/commands/interactive-fix.js.map +1 -0
  49. package/dist/commands/monorepo.d.ts +38 -0
  50. package/dist/commands/monorepo.d.ts.map +1 -0
  51. package/dist/commands/monorepo.js +233 -0
  52. package/dist/commands/monorepo.js.map +1 -0
  53. package/dist/commands/notify.d.ts +79 -0
  54. package/dist/commands/notify.d.ts.map +1 -0
  55. package/dist/commands/notify.js +325 -0
  56. package/dist/commands/notify.js.map +1 -0
  57. package/dist/commands/pr-summary.d.ts +26 -0
  58. package/dist/commands/pr-summary.d.ts.map +1 -0
  59. package/dist/commands/pr-summary.js +188 -0
  60. package/dist/commands/pr-summary.js.map +1 -0
  61. package/dist/commands/profile.d.ts +38 -0
  62. package/dist/commands/profile.d.ts.map +1 -0
  63. package/dist/commands/profile.js +102 -0
  64. package/dist/commands/profile.js.map +1 -0
  65. package/dist/commands/quality-gate.d.ts +70 -0
  66. package/dist/commands/quality-gate.d.ts.map +1 -0
  67. package/dist/commands/quality-gate.js +264 -0
  68. package/dist/commands/quality-gate.js.map +1 -0
  69. package/dist/commands/smart-select.d.ts +27 -0
  70. package/dist/commands/smart-select.d.ts.map +1 -0
  71. package/dist/commands/smart-select.js +346 -0
  72. package/dist/commands/smart-select.js.map +1 -0
  73. package/dist/commands/upload.d.ts +14 -0
  74. package/dist/commands/upload.d.ts.map +1 -0
  75. package/dist/commands/upload.js +173 -0
  76. package/dist/commands/upload.js.map +1 -0
  77. package/dist/commands/validate-config.d.ts +17 -0
  78. package/dist/commands/validate-config.d.ts.map +1 -0
  79. package/dist/commands/validate-config.js +268 -0
  80. package/dist/commands/validate-config.js.map +1 -0
  81. package/dist/commands/warm-cache.d.ts +31 -0
  82. package/dist/commands/warm-cache.d.ts.map +1 -0
  83. package/dist/commands/warm-cache.js +166 -0
  84. package/dist/commands/warm-cache.js.map +1 -0
  85. package/dist/evaluators/framework-rules.d.ts +59 -0
  86. package/dist/evaluators/framework-rules.d.ts.map +1 -0
  87. package/dist/evaluators/framework-rules.js +292 -0
  88. package/dist/evaluators/framework-rules.js.map +1 -0
  89. package/dist/parallel.d.ts +53 -0
  90. package/dist/parallel.d.ts.map +1 -0
  91. package/dist/parallel.js +170 -0
  92. package/dist/parallel.js.map +1 -0
  93. package/package.json +1 -1
  94. package/server.json +2 -2
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `judges notify` — Webhook notification system for findings alerts.
3
+ *
4
+ * Sends evaluation results to configured webhook endpoints (Slack, Teams,
5
+ * generic HTTP). Users configure their own endpoints — Judges never stores
6
+ * or processes data on behalf of users.
7
+ *
8
+ * Usage:
9
+ * judges notify --file results.json --channel slack # Send to Slack
10
+ * judges notify --file results.json --channel teams # Send to Teams
11
+ * judges notify --file results.json --channel webhook # Generic webhook
12
+ * judges eval src/app.ts --notify # Evaluate + notify
13
+ *
14
+ * Configuration in .judgesrc:
15
+ * ```json
16
+ * {
17
+ * "notifications": {
18
+ * "channels": [
19
+ * { "type": "slack", "url": "https://hooks.slack.com/..." },
20
+ * { "type": "teams", "url": "https://outlook.office.com/webhook/..." },
21
+ * { "type": "webhook", "url": "https://my-server.com/judges-hook",
22
+ * "headers": { "Authorization": "Bearer ..." } }
23
+ * ],
24
+ * "minSeverity": "medium",
25
+ * "onlyOnFailure": false
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ import type { Finding, Severity, TribunalVerdict } from "../types.js";
31
+ export type NotificationChannelType = "slack" | "teams" | "webhook";
32
+ export interface NotificationChannel {
33
+ /** Channel type */
34
+ type: NotificationChannelType;
35
+ /** Webhook URL — provided and hosted by the user */
36
+ url: string;
37
+ /** Optional custom headers (e.g. auth tokens) */
38
+ headers?: Record<string, string>;
39
+ /** Optional display name for this channel */
40
+ name?: string;
41
+ }
42
+ export interface NotificationConfig {
43
+ /** Channels to send notifications to */
44
+ channels: NotificationChannel[];
45
+ /** Only notify for findings at or above this severity */
46
+ minSeverity?: Severity;
47
+ /** Only send when the verdict is "fail" */
48
+ onlyOnFailure?: boolean;
49
+ }
50
+ export interface NotificationPayload {
51
+ /** Project or file being evaluated */
52
+ target: string;
53
+ /** Overall verdict */
54
+ verdict: "pass" | "fail" | "warning";
55
+ /** Aggregate score (0-10) */
56
+ score: number;
57
+ /** Summary counts by severity */
58
+ summary: Record<Severity, number>;
59
+ /** Total finding count */
60
+ totalFindings: number;
61
+ /** Top findings (limited to 10 for brevity) */
62
+ topFindings: Array<{
63
+ ruleId: string;
64
+ severity: Severity;
65
+ title: string;
66
+ line?: number;
67
+ }>;
68
+ /** Timestamp of evaluation */
69
+ timestamp: string;
70
+ }
71
+ export declare function buildNotificationPayload(target: string, verdict: TribunalVerdict, filteredFindings: Finding[]): NotificationPayload;
72
+ export declare function sendNotification(channel: NotificationChannel, payload: NotificationPayload): Promise<{
73
+ success: boolean;
74
+ error?: string;
75
+ }>;
76
+ export declare function notifyAllChannels(config: NotificationConfig, target: string, verdict: TribunalVerdict): Promise<void>;
77
+ export declare function parseNotificationConfig(obj: Record<string, unknown>): NotificationConfig | undefined;
78
+ export declare function runNotify(argv: string[]): Promise<void>;
79
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAItE,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,mBAAmB;IACnB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,yDAAyD;IACzD,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClC,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,WAAW,EAAE,KAAK,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,QAAQ,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAaD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,EACxB,gBAAgB,EAAE,OAAO,EAAE,GAC1B,mBAAmB,CAyBrB;AA6FD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC/C;AAID,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkCf;AAMD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,GAAG,SAAS,CAmCpG;AAID,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyF7D"}
@@ -0,0 +1,325 @@
1
+ /**
2
+ * `judges notify` — Webhook notification system for findings alerts.
3
+ *
4
+ * Sends evaluation results to configured webhook endpoints (Slack, Teams,
5
+ * generic HTTP). Users configure their own endpoints — Judges never stores
6
+ * or processes data on behalf of users.
7
+ *
8
+ * Usage:
9
+ * judges notify --file results.json --channel slack # Send to Slack
10
+ * judges notify --file results.json --channel teams # Send to Teams
11
+ * judges notify --file results.json --channel webhook # Generic webhook
12
+ * judges eval src/app.ts --notify # Evaluate + notify
13
+ *
14
+ * Configuration in .judgesrc:
15
+ * ```json
16
+ * {
17
+ * "notifications": {
18
+ * "channels": [
19
+ * { "type": "slack", "url": "https://hooks.slack.com/..." },
20
+ * { "type": "teams", "url": "https://outlook.office.com/webhook/..." },
21
+ * { "type": "webhook", "url": "https://my-server.com/judges-hook",
22
+ * "headers": { "Authorization": "Bearer ..." } }
23
+ * ],
24
+ * "minSeverity": "medium",
25
+ * "onlyOnFailure": false
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ // ─── Severity Filtering ─────────────────────────────────────────────────────
31
+ const SEVERITY_RANK = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
32
+ function meetsMinSeverity(findings, minSeverity) {
33
+ const threshold = SEVERITY_RANK[minSeverity] ?? 4;
34
+ return findings.filter((f) => (SEVERITY_RANK[f.severity] ?? 4) <= threshold);
35
+ }
36
+ // ─── Payload Construction ───────────────────────────────────────────────────
37
+ export function buildNotificationPayload(target, verdict, filteredFindings) {
38
+ const summary = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
39
+ for (const f of filteredFindings) {
40
+ summary[f.severity] = (summary[f.severity] || 0) + 1;
41
+ }
42
+ const topFindings = filteredFindings
43
+ .sort((a, b) => (SEVERITY_RANK[a.severity] ?? 4) - (SEVERITY_RANK[b.severity] ?? 4))
44
+ .slice(0, 10)
45
+ .map((f) => ({
46
+ ruleId: f.ruleId,
47
+ severity: f.severity,
48
+ title: f.title,
49
+ line: f.lineNumbers?.[0],
50
+ }));
51
+ return {
52
+ target,
53
+ verdict: verdict.overallVerdict,
54
+ score: verdict.overallScore,
55
+ summary,
56
+ totalFindings: filteredFindings.length,
57
+ topFindings,
58
+ timestamp: new Date().toISOString(),
59
+ };
60
+ }
61
+ // ─── Channel Formatters ─────────────────────────────────────────────────────
62
+ function formatSlackPayload(payload) {
63
+ const emoji = payload.verdict === "pass" ? ":white_check_mark:" : payload.verdict === "fail" ? ":x:" : ":warning:";
64
+ const color = payload.verdict === "pass" ? "#36a64f" : payload.verdict === "fail" ? "#e01e5a" : "#ecb22e";
65
+ const findingLines = payload.topFindings
66
+ .map((f) => `• \`${f.ruleId}\` [${f.severity.toUpperCase()}] ${f.title}${f.line ? ` (L${f.line})` : ""}`)
67
+ .join("\n");
68
+ return {
69
+ attachments: [
70
+ {
71
+ color,
72
+ blocks: [
73
+ {
74
+ type: "section",
75
+ text: {
76
+ type: "mrkdwn",
77
+ text: `${emoji} *Judges Panel — ${payload.verdict.toUpperCase()}*\n*Target:* \`${payload.target}\`\n*Score:* ${payload.score}/10 | *Findings:* ${payload.totalFindings}`,
78
+ },
79
+ },
80
+ ...(payload.totalFindings > 0
81
+ ? [
82
+ {
83
+ type: "section",
84
+ text: {
85
+ type: "mrkdwn",
86
+ text: `*Summary:* ${Object.entries(payload.summary)
87
+ .filter(([, v]) => v > 0)
88
+ .map(([k, v]) => `${k}: ${v}`)
89
+ .join(" | ")}`,
90
+ },
91
+ },
92
+ ]
93
+ : []),
94
+ ...(findingLines
95
+ ? [
96
+ {
97
+ type: "section",
98
+ text: {
99
+ type: "mrkdwn",
100
+ text: `*Top Findings:*\n${findingLines}`,
101
+ },
102
+ },
103
+ ]
104
+ : []),
105
+ ],
106
+ },
107
+ ],
108
+ };
109
+ }
110
+ function formatTeamsPayload(payload) {
111
+ const color = payload.verdict === "pass" ? "00FF00" : payload.verdict === "fail" ? "FF0000" : "FFAA00";
112
+ const icon = payload.verdict === "pass" ? "✅" : payload.verdict === "fail" ? "❌" : "⚠️";
113
+ const findingRows = payload.topFindings.map((f) => `| ${f.ruleId} | ${f.severity} | ${f.title} |`).join("\n");
114
+ return {
115
+ "@type": "MessageCard",
116
+ "@context": "http://schema.org/extensions",
117
+ themeColor: color,
118
+ summary: `Judges Panel — ${payload.verdict.toUpperCase()}`,
119
+ sections: [
120
+ {
121
+ activityTitle: `${icon} Judges Panel — ${payload.verdict.toUpperCase()}`,
122
+ facts: [
123
+ { name: "Target", value: payload.target },
124
+ { name: "Score", value: `${payload.score}/10` },
125
+ { name: "Findings", value: String(payload.totalFindings) },
126
+ ...Object.entries(payload.summary)
127
+ .filter(([, v]) => v > 0)
128
+ .map(([k, v]) => ({ name: k.charAt(0).toUpperCase() + k.slice(1), value: String(v) })),
129
+ ],
130
+ markdown: true,
131
+ text: payload.topFindings.length > 0
132
+ ? `**Top Findings:**\n\n| Rule | Severity | Title |\n|------|----------|-------|\n${findingRows}`
133
+ : "",
134
+ },
135
+ ],
136
+ };
137
+ }
138
+ function formatGenericWebhookPayload(payload) {
139
+ return payload;
140
+ }
141
+ // ─── Send Notification ──────────────────────────────────────────────────────
142
+ export async function sendNotification(channel, payload) {
143
+ let body;
144
+ switch (channel.type) {
145
+ case "slack":
146
+ body = formatSlackPayload(payload);
147
+ break;
148
+ case "teams":
149
+ body = formatTeamsPayload(payload);
150
+ break;
151
+ case "webhook":
152
+ default:
153
+ body = formatGenericWebhookPayload(payload);
154
+ break;
155
+ }
156
+ try {
157
+ const response = await fetch(channel.url, {
158
+ method: "POST",
159
+ headers: {
160
+ "Content-Type": "application/json",
161
+ ...(channel.headers ?? {}),
162
+ },
163
+ body: JSON.stringify(body),
164
+ });
165
+ if (!response.ok) {
166
+ return { success: false, error: `HTTP ${response.status}: ${response.statusText}` };
167
+ }
168
+ return { success: true };
169
+ }
170
+ catch (err) {
171
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
172
+ }
173
+ }
174
+ // ─── Notify All Channels ────────────────────────────────────────────────────
175
+ export async function notifyAllChannels(config, target, verdict) {
176
+ // Apply severity filter
177
+ const filtered = config.minSeverity ? meetsMinSeverity(verdict.findings, config.minSeverity) : verdict.findings;
178
+ // Skip if onlyOnFailure and verdict is pass
179
+ if (config.onlyOnFailure && verdict.overallVerdict === "pass") {
180
+ return;
181
+ }
182
+ // Skip if no findings after filtering and onlyOnFailure
183
+ if (filtered.length === 0 && config.onlyOnFailure) {
184
+ return;
185
+ }
186
+ const payload = buildNotificationPayload(target, verdict, filtered);
187
+ const results = await Promise.allSettled(config.channels.map(async (ch) => {
188
+ const result = await sendNotification(ch, payload);
189
+ return { channel: ch.name || ch.type, ...result };
190
+ }));
191
+ for (const r of results) {
192
+ if (r.status === "fulfilled") {
193
+ if (r.value.success) {
194
+ console.log(` ✓ Notification sent to ${r.value.channel}`);
195
+ }
196
+ else {
197
+ console.error(` ✗ Notification failed for ${r.value.channel}: ${r.value.error}`);
198
+ }
199
+ }
200
+ else {
201
+ console.error(` ✗ Notification error: ${r.reason}`);
202
+ }
203
+ }
204
+ }
205
+ // ─── Config Parsing ─────────────────────────────────────────────────────────
206
+ const VALID_SEVERITIES = new Set(["critical", "high", "medium", "low", "info"]);
207
+ export function parseNotificationConfig(obj) {
208
+ if (!obj.notifications)
209
+ return undefined;
210
+ const raw = obj.notifications;
211
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
212
+ return undefined;
213
+ if (!Array.isArray(raw.channels))
214
+ return undefined;
215
+ const channels = [];
216
+ for (const ch of raw.channels) {
217
+ if (typeof ch !== "object" || ch === null)
218
+ continue;
219
+ const type = ch.type;
220
+ const url = ch.url;
221
+ if (!type || !url || !["slack", "teams", "webhook"].includes(type))
222
+ continue;
223
+ if (typeof url !== "string" || !url.startsWith("https://"))
224
+ continue;
225
+ channels.push({
226
+ type: type,
227
+ url,
228
+ headers: typeof ch.headers === "object" && ch.headers !== null ? ch.headers : undefined,
229
+ name: typeof ch.name === "string" ? ch.name : undefined,
230
+ });
231
+ }
232
+ if (channels.length === 0)
233
+ return undefined;
234
+ return {
235
+ channels,
236
+ minSeverity: typeof raw.minSeverity === "string" && VALID_SEVERITIES.has(raw.minSeverity)
237
+ ? raw.minSeverity
238
+ : undefined,
239
+ onlyOnFailure: typeof raw.onlyOnFailure === "boolean" ? raw.onlyOnFailure : false,
240
+ };
241
+ }
242
+ // ─── CLI Runner ─────────────────────────────────────────────────────────────
243
+ export async function runNotify(argv) {
244
+ const file = argv.find((_a, i) => argv[i - 1] === "--file") || argv.find((_a, i) => argv[i - 1] === "-f");
245
+ const channelType = argv.find((_a, i) => argv[i - 1] === "--channel");
246
+ const url = argv.find((_a, i) => argv[i - 1] === "--url");
247
+ if (argv.includes("--help") || argv.includes("-h")) {
248
+ console.log(`
249
+ judges notify — Send evaluation results to webhook channels
250
+
251
+ Usage:
252
+ judges notify --file results.json Send to configured channels
253
+ judges notify --file results.json --channel slack Send to specific channel type
254
+ judges notify --url https://hooks.slack.com/... Send to ad-hoc webhook
255
+
256
+ Options:
257
+ --file, -f Path to a Judges JSON result file
258
+ --channel Channel type filter: slack | teams | webhook
259
+ --url Ad-hoc webhook URL (overrides config)
260
+ --help, -h Show this help
261
+ `);
262
+ return;
263
+ }
264
+ // Load notification config from .judgesrc
265
+ const { existsSync: exists, readFileSync: readFile } = await import("fs");
266
+ let notifConfig;
267
+ for (const name of [".judgesrc", ".judgesrc.json"]) {
268
+ if (exists(name)) {
269
+ try {
270
+ const raw = JSON.parse(readFile(name, "utf-8"));
271
+ notifConfig = parseNotificationConfig(raw);
272
+ }
273
+ catch {
274
+ // Skip invalid config
275
+ }
276
+ break;
277
+ }
278
+ }
279
+ // Ad-hoc URL override
280
+ if (url) {
281
+ notifConfig = {
282
+ channels: [{ type: channelType || "webhook", url }],
283
+ };
284
+ }
285
+ if (!notifConfig || notifConfig.channels.length === 0) {
286
+ console.error('Error: No notification channels configured. Add "notifications" to .judgesrc or use --url.');
287
+ process.exit(1);
288
+ }
289
+ // Filter to specific channel type if requested
290
+ if (channelType && !url) {
291
+ notifConfig.channels = notifConfig.channels.filter((ch) => ch.type === channelType);
292
+ if (notifConfig.channels.length === 0) {
293
+ console.error(`Error: No ${channelType} channels configured.`);
294
+ process.exit(1);
295
+ }
296
+ }
297
+ if (!file) {
298
+ console.error("Error: --file is required. Provide a Judges JSON result file.");
299
+ process.exit(1);
300
+ }
301
+ if (!exists(file)) {
302
+ console.error(`Error: File not found: ${file}`);
303
+ process.exit(1);
304
+ }
305
+ try {
306
+ const data = JSON.parse(readFile(file, "utf-8"));
307
+ const verdict = {
308
+ overallVerdict: data.overallVerdict || data.verdict || "pass",
309
+ overallScore: data.overallScore || data.score || 0,
310
+ findings: data.findings || [],
311
+ evaluations: data.evaluations || [],
312
+ summary: data.summary || "",
313
+ criticalCount: data.criticalCount || 0,
314
+ highCount: data.highCount || 0,
315
+ timestamp: data.timestamp || new Date().toISOString(),
316
+ };
317
+ console.log(`Sending notifications for ${file}...`);
318
+ await notifyAllChannels(notifConfig, file, verdict);
319
+ }
320
+ catch (err) {
321
+ console.error(`Error reading results file: ${err instanceof Error ? err.message : String(err)}`);
322
+ process.exit(1);
323
+ }
324
+ }
325
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * PR summary comment — post a top-level comment on a GitHub PR
3
+ * with the overall Judges verdict, score, and finding counts.
4
+ *
5
+ * Uses the GitHub API to create or update a comment with a distinctive
6
+ * marker so subsequent runs update in-place rather than spamming.
7
+ */
8
+ import type { TribunalVerdict } from "../types.js";
9
+ export interface PrSummaryOptions {
10
+ owner: string;
11
+ repo: string;
12
+ prNumber: number;
13
+ token: string;
14
+ verdict: TribunalVerdict;
15
+ baseUrl?: string;
16
+ }
17
+ interface PrSummaryResult {
18
+ commentId: number;
19
+ updated: boolean;
20
+ url: string;
21
+ }
22
+ export declare function formatPrSummary(verdict: TribunalVerdict): string;
23
+ export declare function postPrSummary(options: PrSummaryOptions): Promise<PrSummaryResult>;
24
+ export declare function runPrSummary(argv: string[]): Promise<void>;
25
+ export {};
26
+ //# sourceMappingURL=pr-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-summary.d.ts","sourceRoot":"","sources":["../../src/commands/pr-summary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAmBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAoDhE;AAsBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAiCvF;AAID,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFhE"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * PR summary comment — post a top-level comment on a GitHub PR
3
+ * with the overall Judges verdict, score, and finding counts.
4
+ *
5
+ * Uses the GitHub API to create or update a comment with a distinctive
6
+ * marker so subsequent runs update in-place rather than spamming.
7
+ */
8
+ // ─── Marker ─────────────────────────────────────────────────────────────────
9
+ const COMMENT_MARKER = "<!-- judges-pr-summary -->";
10
+ // ─── Formatting ─────────────────────────────────────────────────────────────
11
+ function verdictEmoji(verdict) {
12
+ if (verdict === "pass")
13
+ return "✅";
14
+ if (verdict === "fail")
15
+ return "❌";
16
+ return "⚠️";
17
+ }
18
+ function severityBadge(severity) {
19
+ const map = { critical: "🔴", high: "🟠", medium: "🟡", low: "🔵", info: "⚪" };
20
+ return map[severity] || "⚪";
21
+ }
22
+ export function formatPrSummary(verdict) {
23
+ const emoji = verdictEmoji(verdict.overallVerdict);
24
+ const lines = [
25
+ COMMENT_MARKER,
26
+ `## ${emoji} Judges Code Review — ${verdict.overallVerdict.toUpperCase()}`,
27
+ "",
28
+ `| Metric | Value |`,
29
+ `|--------|-------|`,
30
+ `| **Verdict** | ${verdict.overallVerdict} |`,
31
+ `| **Score** | ${verdict.overallScore}/100 |`,
32
+ `| **Critical** | ${verdict.criticalCount} |`,
33
+ `| **High** | ${verdict.highCount} |`,
34
+ `| **Judges Run** | ${verdict.evaluations.length} |`,
35
+ "",
36
+ ];
37
+ // Per-judge breakdown
38
+ if (verdict.evaluations.length > 0) {
39
+ lines.push("### Judge Breakdown", "");
40
+ lines.push("| Judge | Verdict | Score | Findings |");
41
+ lines.push("|-------|---------|-------|----------|");
42
+ for (const evaluation of verdict.evaluations) {
43
+ const jEmoji = verdictEmoji(evaluation.verdict);
44
+ const findingCount = evaluation.findings?.length ?? 0;
45
+ lines.push(`| ${evaluation.judgeId} | ${jEmoji} ${evaluation.verdict} | ${evaluation.score}/100 | ${findingCount} |`);
46
+ }
47
+ lines.push("");
48
+ }
49
+ // Top findings
50
+ const allFindings = verdict.evaluations.flatMap((e) => e.findings || []);
51
+ const topFindings = allFindings.filter((f) => f.severity === "critical" || f.severity === "high").slice(0, 10);
52
+ if (topFindings.length > 0) {
53
+ lines.push("### Top Findings", "");
54
+ for (const f of topFindings) {
55
+ const badge = severityBadge(f.severity);
56
+ const loc = f.lineNumbers?.length ? ` (line ${f.lineNumbers[0]})` : "";
57
+ lines.push(`- ${badge} **${f.severity.toUpperCase()}** — ${f.ruleId}: ${f.title}${loc}`);
58
+ }
59
+ lines.push("");
60
+ }
61
+ lines.push("---", `<sub>Generated by [Judges](https://github.com/KevinRabun/judges) at ${new Date().toISOString()}</sub>`);
62
+ return lines.join("\n");
63
+ }
64
+ // ─── GitHub API ─────────────────────────────────────────────────────────────
65
+ async function findExistingComment(owner, repo, prNumber, token, baseUrl) {
66
+ const url = `${baseUrl}/repos/${owner}/${repo}/issues/${prNumber}/comments?per_page=100`;
67
+ const res = await fetch(url, {
68
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" },
69
+ });
70
+ if (!res.ok)
71
+ return null;
72
+ const comments = (await res.json());
73
+ const existing = comments.find((c) => c.body.includes(COMMENT_MARKER));
74
+ return existing ? { id: existing.id, url: existing.html_url } : null;
75
+ }
76
+ export async function postPrSummary(options) {
77
+ const baseUrl = options.baseUrl || "https://api.github.com";
78
+ const body = formatPrSummary(options.verdict);
79
+ const headers = {
80
+ Authorization: `Bearer ${options.token}`,
81
+ Accept: "application/vnd.github+json",
82
+ "Content-Type": "application/json",
83
+ };
84
+ // Check for existing comment to update
85
+ const existing = await findExistingComment(options.owner, options.repo, options.prNumber, options.token, baseUrl);
86
+ if (existing) {
87
+ const url = `${baseUrl}/repos/${options.owner}/${options.repo}/issues/comments/${existing.id}`;
88
+ const res = await fetch(url, {
89
+ method: "PATCH",
90
+ headers,
91
+ body: JSON.stringify({ body }),
92
+ });
93
+ if (!res.ok)
94
+ throw new Error(`Failed to update comment: ${res.status} ${res.statusText}`);
95
+ return { commentId: existing.id, updated: true, url: existing.url };
96
+ }
97
+ const url = `${baseUrl}/repos/${options.owner}/${options.repo}/issues/${options.prNumber}/comments`;
98
+ const res = await fetch(url, {
99
+ method: "POST",
100
+ headers,
101
+ body: JSON.stringify({ body }),
102
+ });
103
+ if (!res.ok)
104
+ throw new Error(`Failed to create comment: ${res.status} ${res.statusText}`);
105
+ const data = (await res.json());
106
+ return { commentId: data.id, updated: false, url: data.html_url };
107
+ }
108
+ // ─── CLI ────────────────────────────────────────────────────────────────────
109
+ export async function runPrSummary(argv) {
110
+ if (argv.includes("--help") || argv.includes("-h")) {
111
+ console.log(`
112
+ judges pr-summary — Post a PR summary comment with Judges results
113
+
114
+ Usage:
115
+ judges eval --file src/app.ts --format json | judges pr-summary --pr 42 --repo owner/repo
116
+
117
+ Options:
118
+ --pr <number> PR number (required)
119
+ --repo <owner/repo> GitHub repo (auto-detected from git)
120
+ --token <token> GitHub token (default: GITHUB_TOKEN env)
121
+ --sarif <path> Read results from SARIF file instead of stdin
122
+ --help, -h Show this help
123
+ `);
124
+ return;
125
+ }
126
+ const { readFileSync, existsSync } = await import("fs");
127
+ const { execSync } = await import("child_process");
128
+ const prArg = argv.find((_a, i) => argv[i - 1] === "--pr");
129
+ if (!prArg) {
130
+ console.error("Error: --pr <number> is required");
131
+ process.exit(1);
132
+ }
133
+ const prNumber = parseInt(prArg, 10);
134
+ let repoArg = argv.find((_a, i) => argv[i - 1] === "--repo");
135
+ if (!repoArg) {
136
+ try {
137
+ const remote = execSync("git remote get-url origin", { encoding: "utf-8" }).trim();
138
+ const match = remote.match(/github\.com[:/](.+?)(?:\.git)?$/);
139
+ if (match)
140
+ repoArg = match[1];
141
+ }
142
+ catch {
143
+ /* ignore */
144
+ }
145
+ }
146
+ if (!repoArg) {
147
+ console.error("Error: --repo <owner/repo> required (could not auto-detect)");
148
+ process.exit(1);
149
+ }
150
+ const [owner, repo] = repoArg.split("/");
151
+ const token = argv.find((_a, i) => argv[i - 1] === "--token") || process.env.GITHUB_TOKEN || "";
152
+ if (!token) {
153
+ console.error("Error: --token or GITHUB_TOKEN env required");
154
+ process.exit(1);
155
+ }
156
+ // Load verdict
157
+ const sarifPath = argv.find((_a, i) => argv[i - 1] === "--sarif");
158
+ let verdict;
159
+ if (sarifPath && existsSync(sarifPath)) {
160
+ // Parse SARIF and synthesize a verdict
161
+ const sarif = JSON.parse(readFileSync(sarifPath, "utf-8"));
162
+ const findings = sarif.runs?.[0]?.results || [];
163
+ verdict = {
164
+ overallVerdict: findings.some((f) => f.level === "error") ? "fail" : "pass",
165
+ overallScore: Math.max(0, 100 - findings.length * 5),
166
+ summary: `SARIF upload with ${findings.length} findings`,
167
+ evaluations: [],
168
+ findings: [],
169
+ criticalCount: findings.filter((f) => f.level === "error").length,
170
+ highCount: findings.filter((f) => f.level === "warning").length,
171
+ timestamp: new Date().toISOString(),
172
+ };
173
+ }
174
+ else {
175
+ // Try reading JSON verdict from stdin arg or file
176
+ const jsonArg = argv.find((_a, i) => argv[i - 1] === "--json");
177
+ if (jsonArg && existsSync(jsonArg)) {
178
+ verdict = JSON.parse(readFileSync(jsonArg, "utf-8"));
179
+ }
180
+ else {
181
+ console.error("Error: provide --sarif <path> or --json <path>");
182
+ process.exit(1);
183
+ }
184
+ }
185
+ const result = await postPrSummary({ owner, repo, prNumber, token, verdict });
186
+ console.log(`${result.updated ? "Updated" : "Created"} PR summary comment: ${result.url}`);
187
+ }
188
+ //# sourceMappingURL=pr-summary.js.map