@reneturcios/greenlight 0.3.4 → 0.3.6

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/README.md CHANGED
@@ -85,8 +85,24 @@ npx @reneturcios/greenlight scan PrefectHQ/fastmcp
85
85
  # Friction score for a repo
86
86
  npx @reneturcios/greenlight friction triggerdotdev/trigger.dev
87
87
 
88
+ # CI triage for a red PR: is each failing check yours or pre-existing?
89
+ npx @reneturcios/greenlight ci https://github.com/reticlehq/reticle/pull/1620
90
+
91
+ # [pre-existing] install-gate (windows-latest)
92
+ # same check failing on base: 2 of 3 recent base runs, last at 2026-10-14T10:00:00Z
93
+
94
+ # [external-blocker] Vercel - Authorization required
95
+ # check name matches the Vercel deploy/auth pattern; this failure is infrastructure, not the PR
96
+
88
97
  # JSON output for scripting
89
- npx @reneturcios/greenlight check <issue-url> --json
98
+ npx @reneturcios/greenlight ci <pr-url> --json
99
+
100
+ # Review triage for a PR with review feedback: which comments need you?
101
+ npx @reneturcios/greenlight review https://github.com/n8n-io/n8n/pull/41235
102
+
103
+ # [likely-real] interpolation drops `__proto__` (packages/bruno-cli/src/runner/interpolate-vars.js:42)
104
+ # why: anchor matches: the comment's quoted token Object.assign({}, params) is present in the anchored hunk
105
+ # draft reply: Good catch. Fixed in <next commit>: ...
90
106
  ```
91
107
 
92
108
  Real examples from our vetting:
@@ -146,9 +162,19 @@ Tools:
146
162
  * `claim_issue` - verdict-gated claim comment (dry run unless `execute: true`)
147
163
  * `prep_issue` - fork and branch check plus a local PREP.md checklist with the repo's learned conventions (dry run unless `execute: true`)
148
164
  * `open_pr` - DCO-aware PR opening from a fork branch (dry run unless `execute: true`)
165
+ * `ci_triage` - classify each failing check on a PR as pre-existing, likely-yours, indeterminate (with a stated lean), or external-blocker, by comparing against the base branch's recent check history
166
+ * `review_triage` - triage the review feedback on your PR: each finding classified likely-real / likely-noise / needs-decision with its reason and a reply draft (humans are never called noise; drafts only, nothing posted)
149
167
 
150
168
  Run it locally: `npm run dev` (MCP entry is `src/index.ts`; CLI is `src/cli.ts`).
151
169
 
170
+ ### v0.3.5: CI triage (solve-first)
171
+
172
+ The first solve-side command. On a red PR, `ci` fetches the PR's base and head, collects the failing check runs (with the first failure lines from annotations when it can), and builds the same checks' history from the five most recent base-branch commits. Each failing check gets one line with its class and the evidence: `pre-existing` (same check failing on base, with counts and the last failure time), `likely-yours` (failing on head, passing on base, and the PR touches files that relate to the check), `indeterminate` (no base signal to compare), or `external-blocker` (Vercel/Netlify/deploy-preview auth failures, infrastructure not the PR). Indeterminate checks always state their lean: not touching the check's area leans pre-existing, touching it leans yours, with the reason spelled out. A verdict line closes it out: ALL PRE-EXISTING, HAS LIKELY-YOURS, INDETERMINATE, or NO FAILURES.
173
+
174
+ ### v0.3.6: review triage (solve-first)
175
+
176
+ The second solve-side command. On a PR with review feedback, `review` gathers every review thread (bot and human), classifies each finding, and drafts the reply for you. Findings group into three buckets: **likely-real** (the comment's quoted anchor token is in the diff hunk; fix these), **needs-decision** (bot questions and every substantive human comment, which is never called noise; your call), and **likely-noise** (findings on generated files, style nits contradicted by the surrounding code, anchor mismatches, security claims the hunk disproves; probably push back). Each finding prints with its author, file and line, the one-line summary, the deterministic reason for its class, and the draft reply. Summaries, walkthroughs, praise, and findings on dismissed reviews count as noise or resolved, never as findings. Like everything in Greenlight, the drafts are output only: nothing is posted.
177
+
152
178
  ## Automation
153
179
 
154
180
  Greenlight 0.2.0 automates the contribution pipeline its author ran by hand
@@ -208,6 +234,8 @@ npm run build
208
234
 
209
235
  ## Roadmap (Later)
210
236
 
237
+ * Review-response triage: gather every bot and human review thread on a PR, classify each finding (real, noise, needs a decision) with the code context, draft the reply or fix list (solve-first, next after CI triage)
238
+ * Claim-to-PR prep scaffolding: emit the per-repo work order (house rules, CLA method and link, changeset conventions, fork/branch names) from one `prep` pass
211
239
  * Browser extension that scores issues inline on github.com
212
240
  * Hosted dashboard of the friction index, refreshed on a schedule
213
241
  * Maintainer GitHub Action that auto-labels claim comments
@@ -0,0 +1,85 @@
1
+ export type CiCheckClass = "pre-existing" | "likely-yours" | "indeterminate" | "external-blocker";
2
+ export type CiTriageVerdict = "ALL PRE-EXISTING" | "HAS LIKELY-YOURS" | "INDETERMINATE" | "NO FAILURES";
3
+ /** A failing check run on the PR head. */
4
+ export interface HeadCheck {
5
+ name: string;
6
+ conclusion: string;
7
+ detailsUrl?: string;
8
+ /** First meaningful failure lines from check-run annotations, best-effort. */
9
+ failureLines?: string[];
10
+ }
11
+ /** One check-run result from the base branch's recent window. */
12
+ export interface BaseCheckRecord {
13
+ name: string;
14
+ conclusion: string;
15
+ completedAt: string;
16
+ }
17
+ export interface ClassifiedCheck {
18
+ name: string;
19
+ conclusion: string;
20
+ class: CiCheckClass;
21
+ /** Human-readable evidence: base status, times, or the linked file. */
22
+ evidence: string;
23
+ /** On indeterminate only: which way it leans and why is in evidence. */
24
+ lean?: "pre-existing" | "yours";
25
+ failureLines?: string[];
26
+ detailsUrl?: string;
27
+ }
28
+ export interface CiTriageInput {
29
+ prUrl: string;
30
+ prTitle: string;
31
+ baseRef: string;
32
+ baseSha: string;
33
+ headSha: string;
34
+ headChecks: HeadCheck[];
35
+ baseRecords: BaseCheckRecord[];
36
+ changedFiles: string[];
37
+ }
38
+ export interface CiTriageResult {
39
+ pr: string;
40
+ title: string;
41
+ baseRef: string;
42
+ baseSha: string;
43
+ headSha: string;
44
+ checks: ClassifiedCheck[];
45
+ verdict: CiTriageVerdict;
46
+ }
47
+ /** Matrix suffixes ("name (windows-latest)") are family, not identity. */
48
+ export declare function stripMatrix(name: string): string;
49
+ /** Case and whitespace are noise; the matrix suffix is kept so exact
50
+ * variants match first and families match second. */
51
+ export declare function normalizeCheckName(name: string): string;
52
+ /**
53
+ * Files the PR touched that plausibly relate to a check, by path-token or
54
+ * semantic-token match. Explainable by design: the caller quotes the
55
+ * returned paths as the link evidence.
56
+ */
57
+ export declare function checkTouchesFiles(checkName: string, files: string[]): string[];
58
+ /**
59
+ * Classify one failing head check against base records and changed files.
60
+ * now is injected so tests pin ages.
61
+ */
62
+ export declare function classifyCheck(head: HeadCheck, baseRecords: BaseCheckRecord[], changedFiles: string[]): ClassifiedCheck;
63
+ /** Roll up per-check classes into the one-line verdict. */
64
+ export declare function triageVerdict(checks: ClassifiedCheck[]): CiTriageVerdict;
65
+ /** The pure engine: classify every failing head check, then roll up. */
66
+ export declare function classifyCiTriage(input: CiTriageInput): CiTriageResult;
67
+ export interface PullDetail {
68
+ title: string;
69
+ baseRef: string;
70
+ baseSha: string;
71
+ headSha: string;
72
+ changedFiles: string[];
73
+ }
74
+ /** Test seam: every network call behind a named function. */
75
+ export interface CiTriageDeps {
76
+ fetchPull?: (owner: string, repo: string, number: number) => Promise<PullDetail>;
77
+ fetchHeadChecks?: (owner: string, repo: string, headSha: string) => Promise<HeadCheck[]>;
78
+ fetchBaseRecords?: (owner: string, repo: string, baseRef: string) => Promise<BaseCheckRecord[]>;
79
+ }
80
+ /**
81
+ * Full triage for a PR URL. Best-effort: if the base window cannot be
82
+ * read, every check is classified without base data (indeterminate with a
83
+ * lean) rather than erroring.
84
+ */
85
+ export declare function triagePr(prUrl: string, deps?: CiTriageDeps): Promise<CiTriageResult>;
@@ -0,0 +1,335 @@
1
+ /**
2
+ * CI failure triage. Answers the question every contributor asks on a red
3
+ * PR: "is this failure mine or pre-existing?"
4
+ *
5
+ * The classifier is a pure function over injected data (no network), so
6
+ * the rules are unit-testable and pinned to the campaign's real cases:
7
+ *
8
+ * - reticle #1620: windows install-gate failing identically on base
9
+ * (leftover-process flake) -> pre-existing
10
+ * - reticle #1633: fixture-based test failing in an untouched package,
11
+ * same check intermittent on base -> pre-existing
12
+ * - a unit-test check failing only on head in a package the PR touched
13
+ * -> likely-yours
14
+ * - Vercel "Authorization required" -> external-blocker (not the PR)
15
+ *
16
+ * Rule order per failing head check:
17
+ * 1. external-blocker: the check name matches a known deploy/auth
18
+ * pattern (Vercel, Netlify, deploy previews). Evidence quotes the match.
19
+ * 2. pre-existing: the same check name failed in the recent base
20
+ * window (exact variant first, then the matrix family, e.g. a windows
21
+ * install-gate flake with no exact-variant history). Evidence quotes
22
+ * base failure counts and the last failure time.
23
+ * 3. likely-yours: failing on head, passing (or absent but with base
24
+ * history present) on base, AND the PR touches files plausibly
25
+ * related to the check (path token or semantic token match).
26
+ * 4. indeterminate: failing on head with no base signal to compare
27
+ * against. Carries a lean ("pre-existing" when the PR does not touch
28
+ * the check's area, "yours" when it does) and says why.
29
+ *
30
+ * The live fetch layer is best-effort: base history and annotations each
31
+ * degrade to "no evidence" on failure rather than guessing.
32
+ */
33
+ import { apiGet, parsePullUrl } from "./github.js";
34
+ /** Conclusions that count as "this check failed". "stale" (superseded by a newer push) is not a failure. */
35
+ const FAILING = new Set(["failure", "timed_out", "action_required"]);
36
+ /** Check-name patterns that mean the failure is infrastructure, not the PR. */
37
+ const EXTERNAL_PATTERNS = [
38
+ { pattern: /vercel/i, label: "Vercel" },
39
+ { pattern: /netlify/i, label: "Netlify" },
40
+ { pattern: /authorization required/i, label: "authorization required" },
41
+ { pattern: /deploy(ment)?\s*(preview|check)?/i, label: "deploy" },
42
+ { pattern: /preview/i, label: "preview" },
43
+ { pattern: /cloudflare pages/i, label: "Cloudflare Pages" },
44
+ ];
45
+ /** Matrix suffixes ("name (windows-latest)") are family, not identity. */
46
+ export function stripMatrix(name) {
47
+ return name.replace(/\s*\([^)]*\)\s*/g, " ").replace(/\s+/g, " ").trim();
48
+ }
49
+ /** Case and whitespace are noise; the matrix suffix is kept so exact
50
+ * variants match first and families match second. */
51
+ export function normalizeCheckName(name) {
52
+ return name.toLowerCase().replace(/\s+/g, " ").trim();
53
+ }
54
+ /** Path segments that describe CI environments, not code areas. Ignored for file linking. */
55
+ const ENV_TOKENS = new Set([
56
+ "windows", "macos", "ubuntu", "linux", "node", "nodejs", "python", "npm", "pnpm", "yarn",
57
+ "run", "ci", "on", "the", "with", "and", "or", "e2e",
58
+ ]);
59
+ /** Check-name tokens that semantically relate to file shapes. */
60
+ const SEMANTIC_TOKENS = [
61
+ { tokens: new Set(["lint", "eslint", "prettier", "format"]), fileTest: /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|rb)$/i },
62
+ { tokens: new Set(["test", "tests", "unit", "spec", "jest", "vitest", "mocha", "pytest", "coverage"]), fileTest: /(^|\/)(test|tests|__tests__|spec)\b|\.test\.|\.spec\./i },
63
+ { tokens: new Set(["typecheck", "tsc", "mypy", "pyright"]), fileTest: /\.(ts|tsx|py)$/i },
64
+ { tokens: new Set(["build", "compile"]), fileTest: /\.(ts|tsx|js|jsx|py|go|rs)$/i },
65
+ ];
66
+ const MAX_LINKED_EVIDENCE = 3;
67
+ /**
68
+ * Files the PR touched that plausibly relate to a check, by path-token or
69
+ * semantic-token match. Explainable by design: the caller quotes the
70
+ * returned paths as the link evidence.
71
+ */
72
+ export function checkTouchesFiles(checkName, files) {
73
+ const tokens = checkName
74
+ .toLowerCase()
75
+ .split(/[^a-z0-9]+/)
76
+ .filter((t) => t.length > 2 && !ENV_TOKENS.has(t));
77
+ const linked = new Set();
78
+ for (const file of files) {
79
+ const fl = file.toLowerCase();
80
+ const segs = fl.split("/");
81
+ const stem = (segs.pop() ?? "").replace(/\.[a-z0-9]+$/, "");
82
+ for (const t of tokens) {
83
+ if (stem.includes(t)) {
84
+ linked.add(file);
85
+ break;
86
+ }
87
+ if (segs.some((s) => s === t || (t.length > 4 && s.includes(t)))) {
88
+ linked.add(file);
89
+ break;
90
+ }
91
+ if (SEMANTIC_TOKENS.some((g) => g.tokens.has(t) && g.fileTest.test(fl))) {
92
+ linked.add(file);
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ return [...linked].slice(0, MAX_LINKED_EVIDENCE);
98
+ }
99
+ /**
100
+ * Classify one failing head check against base records and changed files.
101
+ * now is injected so tests pin ages.
102
+ */
103
+ export function classifyCheck(head, baseRecords, changedFiles) {
104
+ const name = head.name;
105
+ for (const { pattern, label } of EXTERNAL_PATTERNS) {
106
+ if (pattern.test(name)) {
107
+ return {
108
+ name,
109
+ conclusion: head.conclusion,
110
+ class: "external-blocker",
111
+ evidence: `check name matches the ${label} deploy/auth pattern; this failure is infrastructure, not the PR`,
112
+ detailsUrl: head.detailsUrl,
113
+ failureLines: head.failureLines,
114
+ };
115
+ }
116
+ }
117
+ const wanted = normalizeCheckName(name);
118
+ const family = stripMatrix(wanted);
119
+ const baseForCheck = baseRecords.filter((r) => normalizeCheckName(r.name) === wanted);
120
+ const baseFamilyFailures = baseRecords.filter((r) => FAILING.has(r.conclusion) && stripMatrix(normalizeCheckName(r.name)) === family);
121
+ const baseFailures = baseForCheck.filter((r) => FAILING.has(r.conclusion));
122
+ if (baseFailures.length > 0) {
123
+ baseFailures.sort((a, b) => (a.completedAt < b.completedAt ? 1 : -1));
124
+ const total = baseForCheck.length;
125
+ const lastAt = baseFailures[0].completedAt ? `, last at ${baseFailures[0].completedAt}` : "";
126
+ return {
127
+ name,
128
+ conclusion: head.conclusion,
129
+ class: "pre-existing",
130
+ evidence: `same check failing on base: ${baseFailures.length} of ${total} recent base run${total === 1 ? "" : "s"}${lastAt}`,
131
+ detailsUrl: head.detailsUrl,
132
+ failureLines: head.failureLines,
133
+ };
134
+ }
135
+ if (baseFamilyFailures.length > 0) {
136
+ baseFamilyFailures.sort((a, b) => (a.completedAt < b.completedAt ? 1 : -1));
137
+ const familyRuns = baseRecords.filter((r) => stripMatrix(normalizeCheckName(r.name)) === family).length;
138
+ const lastAt = baseFamilyFailures[0].completedAt ? `, last at ${baseFamilyFailures[0].completedAt}` : "";
139
+ return {
140
+ name,
141
+ conclusion: head.conclusion,
142
+ class: "pre-existing",
143
+ evidence: `check family "${family}" failing on base: ${baseFamilyFailures.length} of ${familyRuns} family runs${lastAt}`,
144
+ detailsUrl: head.detailsUrl,
145
+ failureLines: head.failureLines,
146
+ };
147
+ }
148
+ const linked = checkTouchesFiles(name, changedFiles);
149
+ if (baseForCheck.length > 0 && linked.length > 0) {
150
+ return {
151
+ name,
152
+ conclusion: head.conclusion,
153
+ class: "likely-yours",
154
+ evidence: `passing on base but failing on head, and the PR touches ${linked.join(", ")} which relates to this check`,
155
+ detailsUrl: head.detailsUrl,
156
+ failureLines: head.failureLines,
157
+ };
158
+ }
159
+ if (linked.length > 0) {
160
+ return {
161
+ name,
162
+ conclusion: head.conclusion,
163
+ class: "indeterminate",
164
+ lean: "yours",
165
+ evidence: `no base history for this check to compare against; the PR touches ${linked.join(", ")} which relates to it, so it leans yours`,
166
+ detailsUrl: head.detailsUrl,
167
+ failureLines: head.failureLines,
168
+ };
169
+ }
170
+ return {
171
+ name,
172
+ conclusion: head.conclusion,
173
+ class: "indeterminate",
174
+ lean: "pre-existing",
175
+ evidence: `no base history for this check to compare against; none of the PR's files relate to it, so it leans pre-existing (an unrelated flake is more likely than a new failure caused by this PR)`,
176
+ detailsUrl: head.detailsUrl,
177
+ failureLines: head.failureLines,
178
+ };
179
+ }
180
+ /** Roll up per-check classes into the one-line verdict. */
181
+ export function triageVerdict(checks) {
182
+ if (checks.length === 0)
183
+ return "NO FAILURES";
184
+ if (checks.some((c) => c.class === "likely-yours"))
185
+ return "HAS LIKELY-YOURS";
186
+ if (checks.some((c) => c.class === "indeterminate"))
187
+ return "INDETERMINATE";
188
+ return "ALL PRE-EXISTING";
189
+ }
190
+ /** The pure engine: classify every failing head check, then roll up. */
191
+ export function classifyCiTriage(input) {
192
+ const checks = input.headChecks.map((h) => classifyCheck(h, input.baseRecords, input.changedFiles));
193
+ return {
194
+ pr: input.prUrl,
195
+ title: input.prTitle,
196
+ baseRef: input.baseRef,
197
+ baseSha: input.baseSha,
198
+ headSha: input.headSha,
199
+ checks,
200
+ verdict: triageVerdict(checks),
201
+ };
202
+ }
203
+ async function fetchPullFiles(owner, repo, number) {
204
+ const files = [];
205
+ for (let page = 1; page <= 5; page++) {
206
+ const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}/files?per_page=100&page=${page}`));
207
+ for (const f of data ?? []) {
208
+ if (typeof f.filename === "string")
209
+ files.push(f.filename);
210
+ }
211
+ if ((data ?? []).length < 100)
212
+ break;
213
+ }
214
+ return files;
215
+ }
216
+ async function defaultFetchPull(owner, repo, number) {
217
+ const p = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}`));
218
+ return {
219
+ title: String(p.title ?? ""),
220
+ baseRef: String(p.base?.ref ?? ""),
221
+ baseSha: String(p.base?.sha ?? ""),
222
+ headSha: String(p.head?.sha ?? ""),
223
+ changedFiles: await fetchPullFiles(owner, repo, number),
224
+ };
225
+ }
226
+ const ANNOTATION_FETCH_LIMIT = 6;
227
+ async function fetchFailureLines(owner, repo, checkRunId) {
228
+ try {
229
+ const data = (await apiGet(`/repos/${owner}/${repo}/check-runs/${checkRunId}/annotations?per_page=50`));
230
+ const lines = [];
231
+ for (const a of data ?? []) {
232
+ const level = String(a.annotation_level ?? "");
233
+ if (level !== "failure" && level !== "error")
234
+ continue;
235
+ const msg = String(a.message ?? "").trim();
236
+ if (msg)
237
+ lines.push(msg.split("\n")[0].slice(0, 240));
238
+ if (lines.length >= 3)
239
+ break;
240
+ }
241
+ return lines;
242
+ }
243
+ catch {
244
+ return [];
245
+ }
246
+ }
247
+ async function defaultFetchHeadChecks(owner, repo, headSha) {
248
+ const data = (await apiGet(`/repos/${owner}/${repo}/commits/${headSha}/check-runs?per_page=100`));
249
+ const failing = (data.check_runs ?? []).filter((c) => c.status === "completed" && FAILING.has(String(c.conclusion ?? "")));
250
+ const out = [];
251
+ let annotationBudget = ANNOTATION_FETCH_LIMIT;
252
+ for (const c of failing) {
253
+ const id = typeof c.id === "number" ? c.id : undefined;
254
+ let failureLines;
255
+ if (id !== undefined && annotationBudget > 0) {
256
+ annotationBudget -= 1;
257
+ const lines = await fetchFailureLines(owner, repo, id);
258
+ if (lines.length > 0)
259
+ failureLines = lines;
260
+ }
261
+ out.push({
262
+ name: String(c.name ?? "unnamed check"),
263
+ conclusion: String(c.conclusion ?? "unknown"),
264
+ detailsUrl: typeof c.details_url === "string" ? c.details_url : undefined,
265
+ failureLines,
266
+ });
267
+ }
268
+ return out;
269
+ }
270
+ /** How many recent base-branch commits to inspect for each check's history. */
271
+ const BASE_COMMIT_WINDOW = 5;
272
+ async function defaultFetchBaseRecords(owner, repo, baseRef) {
273
+ const records = [];
274
+ let commits;
275
+ try {
276
+ commits = (await apiGet(`/repos/${owner}/${repo}/commits?sha=${encodeURIComponent(baseRef)}&per_page=${BASE_COMMIT_WINDOW}`));
277
+ }
278
+ catch {
279
+ return records;
280
+ }
281
+ for (const commit of commits ?? []) {
282
+ const sha = typeof commit.sha === "string" ? commit.sha : undefined;
283
+ if (!sha)
284
+ continue;
285
+ try {
286
+ const data = (await apiGet(`/repos/${owner}/${repo}/commits/${sha}/check-runs?per_page=100`));
287
+ for (const c of data.check_runs ?? []) {
288
+ if (c.status !== "completed" || !c.conclusion)
289
+ continue;
290
+ records.push({
291
+ name: String(c.name ?? "unnamed check"),
292
+ conclusion: String(c.conclusion),
293
+ completedAt: typeof c.completed_at === "string" ? c.completed_at : "",
294
+ });
295
+ }
296
+ }
297
+ catch {
298
+ // One bad commit in the window is not the whole story; keep going.
299
+ }
300
+ }
301
+ // completedAt: check-runs list on commits lacks completed_at in some shapes;
302
+ // fall back to "recent run" ordering when absent.
303
+ return records;
304
+ }
305
+ /**
306
+ * Full triage for a PR URL. Best-effort: if the base window cannot be
307
+ * read, every check is classified without base data (indeterminate with a
308
+ * lean) rather than erroring.
309
+ */
310
+ export async function triagePr(prUrl, deps = {}) {
311
+ const { owner, repo, number } = parsePullUrl(prUrl);
312
+ const fetchPull = deps.fetchPull ?? defaultFetchPull;
313
+ const fetchHeadChecks = deps.fetchHeadChecks ?? defaultFetchHeadChecks;
314
+ const fetchBaseRecords = deps.fetchBaseRecords ?? defaultFetchBaseRecords;
315
+ const pull = await fetchPull(owner, repo, number);
316
+ const headChecks = await fetchHeadChecks(owner, repo, pull.headSha);
317
+ let baseRecords;
318
+ try {
319
+ baseRecords = await fetchBaseRecords(owner, repo, pull.baseRef);
320
+ }
321
+ catch {
322
+ baseRecords = [];
323
+ }
324
+ return classifyCiTriage({
325
+ prUrl,
326
+ prTitle: pull.title,
327
+ baseRef: pull.baseRef,
328
+ baseSha: pull.baseSha,
329
+ headSha: pull.headSha,
330
+ headChecks,
331
+ baseRecords,
332
+ changedFiles: pull.changedFiles,
333
+ });
334
+ }
335
+ //# sourceMappingURL=ci-triage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-triage.js","sourceRoot":"","sources":["../src/ci-triage.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -6,6 +6,8 @@
6
6
  * greenlight scan <owner/repo> [--json] [--no-fallback] [--gates-file path]
7
7
  * greenlight mcp
8
8
  * greenlight friction <owner/repo> [--json]
9
+ * greenlight ci <pr-url> [--json]
10
+ * greenlight review <pr-url> [--json]
9
11
  * greenlight claim <issue-url> [--yes] [--force] [--json]
10
12
  * greenlight prep <issue-url> [--dir path] [--yes] [--json]
11
13
  * greenlight pr <issue-url> --branch <name> [--title t] [--yes] [--json]
@@ -21,6 +23,8 @@ import { existsSync, readFileSync } from "node:fs";
21
23
  import { scoreFriction } from "./friction.js";
22
24
  import { createRestClient, claimIssue, prepIssue, openPr, AutomationRefusal, } from "./automate.js";
23
25
  import { fetchMergeVelocity, describeMergeVelocity } from "./merge-velocity.js";
26
+ import { triagePr } from "./ci-triage.js";
27
+ import { triageReview } from "./review-triage.js";
24
28
  const SYMBOL = {
25
29
  GO: "[GO]",
26
30
  RISKY: "[RISKY]",
@@ -282,6 +286,68 @@ async function cmdPr(issueUrl, args) {
282
286
  console.log(`Re-run with --yes (and GITHUB_TOKEN set) to open it.`);
283
287
  }
284
288
  }
289
+ async function cmdCi(prUrl, asJson) {
290
+ const result = await triagePr(prUrl);
291
+ if (asJson) {
292
+ console.log(JSON.stringify(result, null, 2));
293
+ return;
294
+ }
295
+ console.log(`CI triage for ${result.pr}`);
296
+ console.log(` ${result.title}`);
297
+ console.log(` head ${result.headSha.slice(0, 7)} <- base ${result.baseRef}`);
298
+ if (result.checks.length === 0) {
299
+ console.log(` No failing checks on the PR head.`);
300
+ console.log(`Verdict: ${result.verdict}`);
301
+ return;
302
+ }
303
+ for (const c of result.checks) {
304
+ console.log(` [${c.class}] ${c.name}`);
305
+ console.log(` ${c.evidence}`);
306
+ if (c.lean)
307
+ console.log(` lean: ${c.lean}`);
308
+ if (c.detailsUrl)
309
+ console.log(` ${c.detailsUrl}`);
310
+ if (c.failureLines)
311
+ for (const line of c.failureLines)
312
+ console.log(` > ${line}`);
313
+ }
314
+ console.log(`Verdict: ${result.verdict}`);
315
+ }
316
+ async function cmdReview(prUrl, asJson) {
317
+ const result = await triageReview(prUrl);
318
+ if (asJson) {
319
+ console.log(JSON.stringify(result, null, 2));
320
+ return;
321
+ }
322
+ console.log(`Review triage for ${result.pr}`);
323
+ console.log(` ${result.title}`);
324
+ if (result.findings.length === 0) {
325
+ const c = result.counts;
326
+ console.log(` No actionable findings (noise: ${c.noise}, resolved: ${c.resolved}).`);
327
+ console.log(`Verdict: ${result.verdict}`);
328
+ return;
329
+ }
330
+ const groups = [
331
+ ["likely-real", "LIKELY REAL - fix these"],
332
+ ["needs-decision", "NEEDS DECISION - your call"],
333
+ ["likely-noise", "LIKELY NOISE - probably push back"],
334
+ ];
335
+ for (const [cls, heading] of groups) {
336
+ const items = result.findings.filter((f) => f.class === cls);
337
+ if (items.length === 0)
338
+ continue;
339
+ console.log(`\n${heading}`);
340
+ for (const f of items) {
341
+ const anchor = f.path ? ` (${f.path}${f.line ? `:${f.line}` : ""})` : "";
342
+ console.log(` [${f.author}] ${f.summary}${anchor}`);
343
+ console.log(` why: ${f.reason}`);
344
+ console.log(` draft reply: ${f.draft}`);
345
+ }
346
+ }
347
+ const c = result.counts;
348
+ console.log(`\nSummary: ${c.likelyReal} likely-real, ${c.needsDecision} needs-decision, ${c.likelyNoise} likely-noise (noise: ${c.noise}, resolved: ${c.resolved})`);
349
+ console.log(`Verdict: ${result.verdict}`);
350
+ }
285
351
  function usage() {
286
352
  console.log("greenlight: pre-flight checks for open-source contributions");
287
353
  console.log("");
@@ -289,6 +355,8 @@ function usage() {
289
355
  console.log(" greenlight scan <owner/repo> [--json] [--no-fallback] [--gates-file path]");
290
356
  console.log(" greenlight mcp (run the MCP server on stdio)");
291
357
  console.log(" greenlight friction <owner/repo> [--json]");
358
+ console.log(" greenlight ci <pr-url> [--json] (CI failure triage: is the red check yours or pre-existing?)");
359
+ console.log(" greenlight review <pr-url> [--json] (review triage: which review comments need you, with reply drafts)");
292
360
  console.log(" greenlight claim <issue-url> [--yes] [--force] [--json]");
293
361
  console.log(" greenlight prep <issue-url> [--dir path] [--yes] [--json]");
294
362
  console.log(" greenlight pr <issue-url> --branch <name> [--title t] [--yes] [--json]");
@@ -326,6 +394,10 @@ async function main() {
326
394
  return await cmdScan(target, args.includes("--json"), args);
327
395
  if (command === "friction" && target)
328
396
  return await cmdFriction(target, args.includes("--json"));
397
+ if (command === "ci" && target)
398
+ return await cmdCi(target, args.includes("--json"));
399
+ if (command === "review" && target)
400
+ return await cmdReview(target, args.includes("--json"));
329
401
  if (command === "claim" && target)
330
402
  return await cmdClaim(target, args);
331
403
  if (command === "prep" && target)