@reneturcios/greenlight 0.14.0 → 0.15.1
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 +140 -212
- package/dist/brief.d.ts +94 -0
- package/dist/brief.js +283 -0
- package/dist/brief.js.map +1 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -1
- package/dist/help.js +22 -0
- package/dist/help.js.map +1 -1
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/radar.d.ts +110 -0
- package/dist/radar.js +468 -0
- package/dist/radar.js.map +1 -0
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/brief.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type CiTriageDeps, type CiTriageResult } from "./ci-triage.js";
|
|
2
|
+
import { type ReviewTriageDeps, type ReviewTriageResult } from "./review-triage.js";
|
|
3
|
+
export type BriefFileArea = "source" | "tests" | "config" | "docs" | "generated" | "other";
|
|
4
|
+
export interface BriefFile {
|
|
5
|
+
filename: string;
|
|
6
|
+
status: "added" | "modified" | "removed" | "renamed" | "other";
|
|
7
|
+
additions: number;
|
|
8
|
+
deletions: number;
|
|
9
|
+
area: BriefFileArea;
|
|
10
|
+
}
|
|
11
|
+
export interface BriefResult {
|
|
12
|
+
pr: string;
|
|
13
|
+
number: number;
|
|
14
|
+
title: string;
|
|
15
|
+
author: string;
|
|
16
|
+
state: string;
|
|
17
|
+
merged: boolean;
|
|
18
|
+
draft: boolean;
|
|
19
|
+
url: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
ageDays: number;
|
|
22
|
+
baseRef: string;
|
|
23
|
+
size: {
|
|
24
|
+
additions: number;
|
|
25
|
+
deletions: number;
|
|
26
|
+
filesChanged: number;
|
|
27
|
+
};
|
|
28
|
+
areas: Array<{
|
|
29
|
+
area: BriefFileArea;
|
|
30
|
+
files: string[];
|
|
31
|
+
}>;
|
|
32
|
+
ci: CiTriageResult;
|
|
33
|
+
review: ReviewTriageResult;
|
|
34
|
+
testEvidence: {
|
|
35
|
+
newOrChangedTestFiles: string[];
|
|
36
|
+
/** Plain statement: what test evidence exists, or that none does. */
|
|
37
|
+
note: string;
|
|
38
|
+
};
|
|
39
|
+
/** Concrete merge blockers found, each with evidence. */
|
|
40
|
+
blockers: string[];
|
|
41
|
+
/** Things not verified, stated plainly. */
|
|
42
|
+
unverified: string[];
|
|
43
|
+
/** One line: what the reviewer should look at first. */
|
|
44
|
+
lookFirst: string;
|
|
45
|
+
/** API calls consumed (measured, for budget honesty). */
|
|
46
|
+
apiCalls: number;
|
|
47
|
+
}
|
|
48
|
+
export interface BriefDeps {
|
|
49
|
+
/** Test seam / alternate PR detail fetch. */
|
|
50
|
+
fetchPullDetail?: (owner: string, repo: string, number: number) => Promise<PullDetailFull>;
|
|
51
|
+
/** Test seam / alternate file list fetch. */
|
|
52
|
+
fetchPullFiles?: (owner: string, repo: string, number: number) => Promise<BriefFile[]>;
|
|
53
|
+
/** Test seam / alternate reviews fetch (for change-request blockers). */
|
|
54
|
+
fetchReviews?: (owner: string, repo: string, number: number) => Promise<ReviewStateItem[]>;
|
|
55
|
+
/** Test seam / alternate contributing-text fetch (changeset signal). */
|
|
56
|
+
fetchContributingText?: (owner: string, repo: string) => Promise<string | undefined>;
|
|
57
|
+
/** Forwarded to the CI triage engine. */
|
|
58
|
+
ciDeps?: CiTriageDeps;
|
|
59
|
+
/** Forwarded to the review triage engine. */
|
|
60
|
+
reviewDeps?: ReviewTriageDeps;
|
|
61
|
+
}
|
|
62
|
+
export interface PullDetailFull {
|
|
63
|
+
title: string;
|
|
64
|
+
author: string;
|
|
65
|
+
state: string;
|
|
66
|
+
merged: boolean;
|
|
67
|
+
draft: boolean;
|
|
68
|
+
url: string;
|
|
69
|
+
createdAt: string;
|
|
70
|
+
baseRef: string;
|
|
71
|
+
mergeable: boolean | null;
|
|
72
|
+
mergeableState: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ReviewStateItem {
|
|
75
|
+
state: string;
|
|
76
|
+
author: string;
|
|
77
|
+
dismissed: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse a PR target: full /pull/ URL, or owner/repo#N shorthand.
|
|
81
|
+
* Throws a usage error otherwise.
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseBriefTarget(target: string): {
|
|
84
|
+
owner: string;
|
|
85
|
+
repo: string;
|
|
86
|
+
number: number;
|
|
87
|
+
};
|
|
88
|
+
/** Area classification for a file path. Pure. */
|
|
89
|
+
export declare function fileArea(path: string): BriefFileArea;
|
|
90
|
+
/**
|
|
91
|
+
* The brief pipeline. Best-effort per source: a failed fetch degrades to
|
|
92
|
+
* an "unverified" note, never to silence or a fabricated verdict.
|
|
93
|
+
*/
|
|
94
|
+
export declare function briefPR(target: string, deps?: BriefDeps): Promise<BriefResult>;
|
package/dist/brief.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR Brief: a verified review brief for ANY pull request.
|
|
3
|
+
*
|
|
4
|
+
* Generalizes the existing review/CI triage (built for our own PRs) into a
|
|
5
|
+
* one-screen brief any reviewer — or maintainer — can read before opening
|
|
6
|
+
* the diff. This is the overlap into review tooling (CodeRabbit/Greptile
|
|
7
|
+
* territory) done Greenlight's way: every claim carries quoted evidence
|
|
8
|
+
* or a link, classifications come from the existing deterministic triage
|
|
9
|
+
* engines, and anything unverified is marked as such. No generic praise,
|
|
10
|
+
* no invented findings.
|
|
11
|
+
*
|
|
12
|
+
* Sections:
|
|
13
|
+
* 1. What changed: files grouped by area/risk (source, tests, config,
|
|
14
|
+
* docs, generated, other), plus size and shape.
|
|
15
|
+
* 2. CI: the existing ci_triage base-vs-head classification, reused.
|
|
16
|
+
* 3. Review findings: the existing review-triage classification, reused
|
|
17
|
+
* (real vs false-positive vs already-addressed, each with evidence).
|
|
18
|
+
* 4. Test evidence present in the PR (new/changed test files), or
|
|
19
|
+
* explicitly marked absent.
|
|
20
|
+
* 5. Merge-readiness blockers: conflicts, unresolved change requests,
|
|
21
|
+
* missing changeset where the repo requires one.
|
|
22
|
+
* 6. A one-line "look at this first" for the reviewer.
|
|
23
|
+
*
|
|
24
|
+
* Budget: PR detail (1) + files (1-2) + reviews (1-2) + the triage
|
|
25
|
+
* engines' own fetches + house-rules (cached, 24h TTL) for the changeset
|
|
26
|
+
* signal. All reads ride the existing file cache.
|
|
27
|
+
*/
|
|
28
|
+
import { apiCallsUsed, apiGet, cacheGet, cacheSet, fetchContributing, parsePullUrl, } from "./github.js";
|
|
29
|
+
import { triagePr } from "./ci-triage.js";
|
|
30
|
+
import { triageReview } from "./review-triage.js";
|
|
31
|
+
/**
|
|
32
|
+
* Parse a PR target: full /pull/ URL, or owner/repo#N shorthand.
|
|
33
|
+
* Throws a usage error otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export function parseBriefTarget(target) {
|
|
36
|
+
const t = target.trim();
|
|
37
|
+
try {
|
|
38
|
+
return parsePullUrl(t);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
const m = /^([^/]+)\/([^/#]+)#(\d+)$/.exec(t);
|
|
42
|
+
if (!m) {
|
|
43
|
+
throw new Error(`Not a PR target: ${target} (expected a pull-request URL or owner/repo#N)`);
|
|
44
|
+
}
|
|
45
|
+
return { owner: m[1], repo: m[2], number: Number(m[3]) };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Area classification for a file path. Pure. */
|
|
49
|
+
export function fileArea(path) {
|
|
50
|
+
const p = path.toLowerCase();
|
|
51
|
+
if (/\/tests?\/|\/__tests__\/|\.test\.|\.spec\.|_test\.|-test\.|test_\b/.test(p) ||
|
|
52
|
+
p.startsWith("test/") ||
|
|
53
|
+
p.startsWith("tests/")) {
|
|
54
|
+
return "tests";
|
|
55
|
+
}
|
|
56
|
+
if (p.startsWith(".github/") ||
|
|
57
|
+
p.endsWith("package-lock.json") ||
|
|
58
|
+
p.endsWith("yarn.lock") ||
|
|
59
|
+
p.endsWith("pnpm-lock.yaml") ||
|
|
60
|
+
p.endsWith(".lock") ||
|
|
61
|
+
/(^|\/)dist\//.test(p) ||
|
|
62
|
+
p.endsWith(".min.js") ||
|
|
63
|
+
p.endsWith(".map") ||
|
|
64
|
+
/\/generated\//.test(p)) {
|
|
65
|
+
return "generated";
|
|
66
|
+
}
|
|
67
|
+
if (p.startsWith(".") ||
|
|
68
|
+
/(^|\/)(config|conf|settings?|scripts?|tools?|tooling|build|ci|infra|docker|k8s|kubernetes)\//.test(p) ||
|
|
69
|
+
/\.(ya?ml|toml|ini|cfg|env|example)$/.test(p) ||
|
|
70
|
+
p === "package.json" ||
|
|
71
|
+
p === "tsconfig.json" ||
|
|
72
|
+
p === "dockerfile") {
|
|
73
|
+
return "config";
|
|
74
|
+
}
|
|
75
|
+
if (p.endsWith(".md") || p.endsWith(".mdx") || p.startsWith("docs/") || p.startsWith("doc/")) {
|
|
76
|
+
return "docs";
|
|
77
|
+
}
|
|
78
|
+
if (/\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|rb|php|java|kt|swift|c|cpp|h|hpp|cs|vue|svelte)$/.test(p)) {
|
|
79
|
+
return "source";
|
|
80
|
+
}
|
|
81
|
+
return "other";
|
|
82
|
+
}
|
|
83
|
+
function toBriefFile(item) {
|
|
84
|
+
const filename = String(item.filename ?? "");
|
|
85
|
+
const rawStatus = String(item.status ?? "other").toLowerCase();
|
|
86
|
+
const status = rawStatus === "added" || rawStatus === "modified" || rawStatus === "removed" || rawStatus === "renamed"
|
|
87
|
+
? rawStatus
|
|
88
|
+
: "other";
|
|
89
|
+
return {
|
|
90
|
+
filename,
|
|
91
|
+
status,
|
|
92
|
+
additions: typeof item.additions === "number" ? item.additions : 0,
|
|
93
|
+
deletions: typeof item.deletions === "number" ? item.deletions : 0,
|
|
94
|
+
area: fileArea(filename),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function defaultFetchPullDetail(owner, repo, number) {
|
|
98
|
+
const key = [owner, repo, String(number), "brief-detail"];
|
|
99
|
+
const cached = cacheGet("issue", key);
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
const p = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}`));
|
|
103
|
+
const detail = {
|
|
104
|
+
title: String(p.title ?? ""),
|
|
105
|
+
author: String(p.user?.login ?? "unknown"),
|
|
106
|
+
state: String(p.state ?? "open"),
|
|
107
|
+
merged: p.merged_at != null,
|
|
108
|
+
draft: Boolean(p.draft),
|
|
109
|
+
url: String(p.html_url ?? `https://github.com/${owner}/${repo}/pull/${number}`),
|
|
110
|
+
createdAt: String(p.created_at ?? ""),
|
|
111
|
+
baseRef: String(p.base?.ref ?? ""),
|
|
112
|
+
mergeable: p.mergeable ?? null,
|
|
113
|
+
mergeableState: String(p.mergeable_state ?? "unknown"),
|
|
114
|
+
};
|
|
115
|
+
cacheSet("issue", key, detail);
|
|
116
|
+
return detail;
|
|
117
|
+
}
|
|
118
|
+
async function defaultFetchPullFiles(owner, repo, number) {
|
|
119
|
+
const key = [owner, repo, String(number), "brief-files"];
|
|
120
|
+
const cached = cacheGet("issue", key);
|
|
121
|
+
if (cached)
|
|
122
|
+
return cached;
|
|
123
|
+
const files = [];
|
|
124
|
+
for (let page = 1; page <= 5; page++) {
|
|
125
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}/files?per_page=100&page=${page}`));
|
|
126
|
+
for (const item of data ?? [])
|
|
127
|
+
files.push(toBriefFile(item));
|
|
128
|
+
if ((data ?? []).length < 100)
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
cacheSet("issue", key, files);
|
|
132
|
+
return files;
|
|
133
|
+
}
|
|
134
|
+
async function defaultFetchReviews(owner, repo, number) {
|
|
135
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`));
|
|
136
|
+
return (data ?? []).map((r) => ({
|
|
137
|
+
state: String(r.state ?? ""),
|
|
138
|
+
author: String(r.user?.login ?? "unknown"),
|
|
139
|
+
dismissed: String(r.state ?? "") === "DISMISSED",
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
const ageDays = (iso) => {
|
|
143
|
+
const t = Date.parse(iso);
|
|
144
|
+
if (!Number.isFinite(t))
|
|
145
|
+
return 0;
|
|
146
|
+
return Math.floor((Date.now() - t) / (24 * 60 * 60 * 1000));
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* The brief pipeline. Best-effort per source: a failed fetch degrades to
|
|
150
|
+
* an "unverified" note, never to silence or a fabricated verdict.
|
|
151
|
+
*/
|
|
152
|
+
export async function briefPR(target, deps = {}) {
|
|
153
|
+
const callsBefore = apiCallsUsed();
|
|
154
|
+
const { owner, repo, number } = parseBriefTarget(target);
|
|
155
|
+
const prUrl = `https://github.com/${owner}/${repo}/pull/${number}`;
|
|
156
|
+
const unverified = [];
|
|
157
|
+
const blockers = [];
|
|
158
|
+
const fetchPullDetail = deps.fetchPullDetail ?? defaultFetchPullDetail;
|
|
159
|
+
const fetchPullFiles = deps.fetchPullFiles ?? defaultFetchPullFiles;
|
|
160
|
+
const fetchReviews = deps.fetchReviews ?? defaultFetchReviews;
|
|
161
|
+
let detail;
|
|
162
|
+
try {
|
|
163
|
+
detail = await fetchPullDetail(owner, repo, number);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
throw new Error(`could not fetch PR ${prUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
}
|
|
168
|
+
let files;
|
|
169
|
+
try {
|
|
170
|
+
files = await fetchPullFiles(owner, repo, number);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
files = [];
|
|
174
|
+
unverified.push("changed-file list unavailable; area breakdown is empty");
|
|
175
|
+
}
|
|
176
|
+
// Reviews for change-request blockers (independent of the triage engine).
|
|
177
|
+
let reviews = [];
|
|
178
|
+
try {
|
|
179
|
+
reviews = await fetchReviews(owner, repo, number);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
unverified.push("review list unavailable; change-request blocker check skipped");
|
|
183
|
+
}
|
|
184
|
+
const openChangeRequests = reviews.filter((r) => r.state === "CHANGES_REQUESTED" && !r.dismissed && r.author !== detail.author);
|
|
185
|
+
for (const r of openChangeRequests) {
|
|
186
|
+
blockers.push(`unresolved change request from @${r.author}`);
|
|
187
|
+
}
|
|
188
|
+
// Merge conflicts.
|
|
189
|
+
if (detail.mergeable === false || detail.mergeableState === "dirty") {
|
|
190
|
+
blockers.push("merge conflicts (mergeable_state=dirty)");
|
|
191
|
+
}
|
|
192
|
+
else if (detail.mergeable === null && detail.mergeableState === "unknown") {
|
|
193
|
+
unverified.push("mergeable state not reported by GitHub yet");
|
|
194
|
+
}
|
|
195
|
+
// CI classification: reuse the existing triage engine (base vs head).
|
|
196
|
+
let ci;
|
|
197
|
+
try {
|
|
198
|
+
ci = await triagePr(prUrl, deps.ciDeps ?? {});
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
ci = {
|
|
202
|
+
pr: prUrl, title: detail.title, baseRef: detail.baseRef, baseSha: "",
|
|
203
|
+
headSha: "", verdict: "NO FAILURES", checks: [],
|
|
204
|
+
};
|
|
205
|
+
unverified.push("CI triage unavailable");
|
|
206
|
+
}
|
|
207
|
+
// Review findings: reuse the existing deterministic review triage.
|
|
208
|
+
let review;
|
|
209
|
+
try {
|
|
210
|
+
review = await triageReview(prUrl, deps.reviewDeps ?? {});
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
review = {
|
|
214
|
+
pr: prUrl, title: detail.title, verdict: "NO FINDINGS",
|
|
215
|
+
findings: [], counts: { likelyReal: 0, likelyNoise: 0, needsDecision: 0, noise: 0, resolved: 0 },
|
|
216
|
+
};
|
|
217
|
+
unverified.push("review triage unavailable");
|
|
218
|
+
}
|
|
219
|
+
// Changeset convention (remote variant of ci-prep's signal): repo
|
|
220
|
+
// mentions changesets in its policy files, but the PR ships no
|
|
221
|
+
// .changeset file -> blocker.
|
|
222
|
+
try {
|
|
223
|
+
const contributing = deps.fetchContributingText
|
|
224
|
+
? await deps.fetchContributingText(owner, repo)
|
|
225
|
+
: await fetchContributing(owner, repo);
|
|
226
|
+
if (contributing && /changeset/i.test(contributing)) {
|
|
227
|
+
const hasChangeset = files.some((f) => /(^|\/)\.changeset\//.test(f.filename));
|
|
228
|
+
if (!hasChangeset)
|
|
229
|
+
blockers.push("repo requires changesets (CONTRIBUTING), none in this PR");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
unverified.push("changeset convention check skipped (policy fetch failed)");
|
|
234
|
+
}
|
|
235
|
+
// Area breakdown.
|
|
236
|
+
const areaOrder = ["source", "tests", "config", "docs", "generated", "other"];
|
|
237
|
+
const areas = areaOrder
|
|
238
|
+
.map((area) => ({ area, files: files.filter((f) => f.area === area).map((f) => f.filename) }))
|
|
239
|
+
.filter((g) => g.files.length > 0);
|
|
240
|
+
const additions = files.reduce((s, f) => s + f.additions, 0);
|
|
241
|
+
const deletions = files.reduce((s, f) => s + f.deletions, 0);
|
|
242
|
+
// Test evidence: new or changed test-area files, stated plainly.
|
|
243
|
+
const testFiles = files.filter((f) => f.area === "tests").map((f) => f.filename);
|
|
244
|
+
const testEvidenceNote = testFiles.length > 0
|
|
245
|
+
? `${testFiles.length} test file${testFiles.length === 1 ? "" : "s"} touched — verification is visible in the PR`
|
|
246
|
+
: "no test files touched — verification is not visible in this PR";
|
|
247
|
+
// Look-first: blockers first, then real findings, then CI, else shape.
|
|
248
|
+
const firstReal = review.findings?.find((f) => f.class === "likely-real");
|
|
249
|
+
const firstFailing = ci.checks?.find((c) => c.class === "likely-yours");
|
|
250
|
+
const biggestArea = areas.reduce((best, g) => (best === undefined || g.files.length > best.count ? { area: g.area, count: g.files.length } : best), undefined);
|
|
251
|
+
const lookFirst = blockers.length > 0
|
|
252
|
+
? `Start here: ${blockers[0]}.`
|
|
253
|
+
: firstReal !== undefined
|
|
254
|
+
? `Start here: ${firstReal.author}'s ${firstReal.class} finding${firstReal.path ? ` on ${firstReal.path}` : ""}: ${firstReal.summary}.`
|
|
255
|
+
: firstFailing !== undefined
|
|
256
|
+
? `Start here: CI check "${firstFailing.name}" classified ${firstFailing.class}: ${firstFailing.evidence}.`
|
|
257
|
+
: biggestArea !== undefined
|
|
258
|
+
? `No blockers or real findings. Start here: ${biggestArea.count} ${biggestArea.area} file${biggestArea.count === 1 ? "" : "s"} carry the change.`
|
|
259
|
+
: "No blockers, findings, or files visible. Nothing to review.";
|
|
260
|
+
return {
|
|
261
|
+
pr: prUrl,
|
|
262
|
+
number,
|
|
263
|
+
title: detail.title,
|
|
264
|
+
author: detail.author,
|
|
265
|
+
state: detail.merged ? "merged" : detail.state,
|
|
266
|
+
merged: detail.merged,
|
|
267
|
+
draft: detail.draft,
|
|
268
|
+
url: detail.url,
|
|
269
|
+
createdAt: detail.createdAt,
|
|
270
|
+
ageDays: ageDays(detail.createdAt),
|
|
271
|
+
baseRef: detail.baseRef,
|
|
272
|
+
size: { additions, deletions, filesChanged: files.length },
|
|
273
|
+
areas,
|
|
274
|
+
ci,
|
|
275
|
+
review,
|
|
276
|
+
testEvidence: { newOrChangedTestFiles: testFiles, note: testEvidenceNote },
|
|
277
|
+
blockers,
|
|
278
|
+
unverified,
|
|
279
|
+
lookFirst,
|
|
280
|
+
apiCalls: apiCallsUsed() - callsBefore,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=brief.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brief.js","sourceRoot":"","sources":["../src/brief.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -50,6 +50,8 @@ import { scoutCampaign, SCOUT_DEFAULT_BUDGET } from "./scout.js";
|
|
|
50
50
|
import { buildVerifiedComment, suggestsVerifiedComment, verifiedCommentSuggestion, parseNoteFile, } from "./verify-comment.js";
|
|
51
51
|
import { verifyRepo } from "./verify.js";
|
|
52
52
|
import { runCiPrep, formatCiPrepHuman } from "./ci-prep.js";
|
|
53
|
+
import { radarPRs } from "./radar.js";
|
|
54
|
+
import { briefPR } from "./brief.js";
|
|
53
55
|
import { babysit, defaultStatePath, defaultAccountAuthor } from "./babysit.js";
|
|
54
56
|
import { runAutopilot, defaultAutopilotStatePath } from "./autopilot.js";
|
|
55
57
|
import { runSync, formatSyncHuman, syncJson } from "./sync.js";
|
|
@@ -776,6 +778,66 @@ async function cmdCiPrep(target, args) {
|
|
|
776
778
|
if (result.verdict === "NO-GO")
|
|
777
779
|
process.exitCode = 1;
|
|
778
780
|
}
|
|
781
|
+
async function cmdRadar(repoRef, args) {
|
|
782
|
+
const { owner, repo } = parseRepoRef(repoRef);
|
|
783
|
+
const asJson = args.includes("--json");
|
|
784
|
+
const limitRaw = flagValue(args, "--limit");
|
|
785
|
+
const limit = limitRaw !== undefined ? Math.max(1, Math.min(Math.trunc(Number(limitRaw)) || 25, 100)) : 25;
|
|
786
|
+
const result = await radarPRs(owner, repo, { limit });
|
|
787
|
+
if (asJson) {
|
|
788
|
+
console.log(JSON.stringify(result, null, 2));
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
console.log(`PR Radar for ${result.repo}: ${result.prsScanned} open PRs scanned, ${result.clusters.length} competing cluster(s), ${result.unclustered.length} unclustered (${result.apiCalls} API calls)`);
|
|
792
|
+
if (result.clusters.length === 0) {
|
|
793
|
+
console.log("No competing-PR clusters found. The open queue has no obvious duplicates.");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
for (const c of result.clusters) {
|
|
797
|
+
const kindLabel = c.kind === "same-issue" ? "same issue" : "file overlap";
|
|
798
|
+
const what = c.issue ? `competing on issue #${c.issue.number} (${c.issue.url})` : "no shared linked issue";
|
|
799
|
+
console.log(`\n[${kindLabel}] ${what}`);
|
|
800
|
+
for (const ev of c.evidence)
|
|
801
|
+
console.log(` - ${ev}`);
|
|
802
|
+
console.log(` suggested keep (heuristic): #${c.suggestedKeep} — ${c.suggestedKeepReason}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
async function cmdBrief(target, args) {
|
|
806
|
+
const asJson = args.includes("--json");
|
|
807
|
+
const result = await briefPR(target);
|
|
808
|
+
if (asJson) {
|
|
809
|
+
console.log(JSON.stringify(result, null, 2));
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
console.log(`PR Brief: ${result.pr}`);
|
|
813
|
+
console.log(` "${result.title}" by @${result.author} — ${result.state}${result.draft ? " (draft)" : ""}, ${result.ageDays}d old`);
|
|
814
|
+
console.log(` size: ${result.size.filesChanged} files, +${result.size.additions}/-${result.size.deletions}`);
|
|
815
|
+
for (const g of result.areas) {
|
|
816
|
+
console.log(` ${g.area}: ${g.files.length} file${g.files.length === 1 ? "" : "s"}`);
|
|
817
|
+
}
|
|
818
|
+
console.log(` CI: ${result.ci.verdict}`);
|
|
819
|
+
for (const check of (result.ci.checks ?? []).filter((c) => c.class !== "pre-existing")) {
|
|
820
|
+
console.log(` [${check.class}] ${check.name}: ${check.evidence}`);
|
|
821
|
+
}
|
|
822
|
+
const rc = result.review.counts;
|
|
823
|
+
console.log(` review: ${result.review.verdict} (real: ${rc.likelyReal}, needs decision: ${rc.needsDecision}, noise: ${rc.likelyNoise})`);
|
|
824
|
+
for (const f of (result.review.findings ?? []).filter((x) => x.class === "likely-real").slice(0, 5)) {
|
|
825
|
+
console.log(` [${f.author}] ${f.summary}${f.path ? ` (${f.path})` : ""}`);
|
|
826
|
+
console.log(` why: ${f.reason}`);
|
|
827
|
+
}
|
|
828
|
+
console.log(` tests: ${result.testEvidence.note}`);
|
|
829
|
+
if (result.blockers.length > 0) {
|
|
830
|
+
console.log(" BLOCKERS:");
|
|
831
|
+
for (const b of result.blockers)
|
|
832
|
+
console.log(` - ${b}`);
|
|
833
|
+
}
|
|
834
|
+
if (result.unverified.length > 0) {
|
|
835
|
+
console.log(" not verified:");
|
|
836
|
+
for (const u of result.unverified)
|
|
837
|
+
console.log(` - ${u}`);
|
|
838
|
+
}
|
|
839
|
+
console.log(`\n ${result.lookFirst}`);
|
|
840
|
+
}
|
|
779
841
|
async function cmdReview(prUrl, asJson) {
|
|
780
842
|
const result = await triageReview(prUrl);
|
|
781
843
|
if (asJson) {
|
|
@@ -1396,6 +1458,10 @@ async function main() {
|
|
|
1396
1458
|
return await cmdCiPrep(target, args);
|
|
1397
1459
|
if (command === "review" && target)
|
|
1398
1460
|
return await cmdReview(target, args.includes("--json"));
|
|
1461
|
+
if (command === "radar" && target)
|
|
1462
|
+
return await cmdRadar(target, args);
|
|
1463
|
+
if (command === "brief" && target)
|
|
1464
|
+
return await cmdBrief(target, args);
|
|
1399
1465
|
if (command === "guard" && target)
|
|
1400
1466
|
return await cmdGuard(target, args);
|
|
1401
1467
|
if (command === "digest" && target)
|