@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/radar.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** One open PR, as the radar sees it. */
|
|
2
|
+
export interface RadarPR {
|
|
3
|
+
number: number;
|
|
4
|
+
title: string;
|
|
5
|
+
body: string;
|
|
6
|
+
author: string;
|
|
7
|
+
branch: string;
|
|
8
|
+
headSha: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
url: string;
|
|
11
|
+
draft: boolean;
|
|
12
|
+
/** Fetched lazily, only for pre-filtered overlap candidates. */
|
|
13
|
+
changedFiles?: string[];
|
|
14
|
+
}
|
|
15
|
+
export type RadarClusterKind = "same-issue" | "file-overlap";
|
|
16
|
+
export interface RadarCluster {
|
|
17
|
+
kind: RadarClusterKind;
|
|
18
|
+
/** Set for same-issue clusters. */
|
|
19
|
+
issue?: {
|
|
20
|
+
number: number;
|
|
21
|
+
url: string;
|
|
22
|
+
};
|
|
23
|
+
prs: number[];
|
|
24
|
+
authors: string[];
|
|
25
|
+
evidence: string[];
|
|
26
|
+
/** A heuristic suggestion only: which PR to keep reviewing. */
|
|
27
|
+
suggestedKeep: number;
|
|
28
|
+
suggestedKeepReason: string;
|
|
29
|
+
}
|
|
30
|
+
export interface RadarResult {
|
|
31
|
+
repo: string;
|
|
32
|
+
prsScanned: number;
|
|
33
|
+
clusters: RadarCluster[];
|
|
34
|
+
/** PR numbers that matched nothing. */
|
|
35
|
+
unclustered: number[];
|
|
36
|
+
/** API calls consumed (measured, for budget honesty). */
|
|
37
|
+
apiCalls: number;
|
|
38
|
+
}
|
|
39
|
+
export interface RadarDeps {
|
|
40
|
+
/** Test seam / alternate listing. Default: live PR list via apiGet. */
|
|
41
|
+
listOpenPRs?: (owner: string, repo: string, limit: number) => Promise<RadarPR[]>;
|
|
42
|
+
/** Test seam for the file-list fetch. Default: live, cached. */
|
|
43
|
+
fetchFiles?: (owner: string, repo: string, prNumber: number) => Promise<string[]>;
|
|
44
|
+
/** Test seam for combined CI status. Default: live, only for top-2 age candidates. */
|
|
45
|
+
fetchCombinedState?: (owner: string, repo: string, sha: string) => Promise<string>;
|
|
46
|
+
}
|
|
47
|
+
/** Issue numbers referenced in a PR title/body, deduplicated. */
|
|
48
|
+
export declare function extractIssueRefs(text: string): number[];
|
|
49
|
+
/**
|
|
50
|
+
* Issue numbers referenced by a branch name, deduplicated. Pure.
|
|
51
|
+
*
|
|
52
|
+
* A number counts ONLY under a defensible convention:
|
|
53
|
+
* - explicit "#N" marker in the branch;
|
|
54
|
+
* - an issue-prefix token immediately before it ("fix-9483-wsdl", "issue-61");
|
|
55
|
+
* - first token of a branch segment ("feat/61-resume-block", "123-fix-x");
|
|
56
|
+
* - trailing token at the end of the branch when it is a 3+ digit,
|
|
57
|
+
* non-status-code number preceded by a word ("fix/studio-sms-...-6435");
|
|
58
|
+
* - bare mid-branch token when it is a 3+ digit, non-status-code number
|
|
59
|
+
* ("pay-852-partial-...").
|
|
60
|
+
* Never counted: GitHub web-UI "patch-N" names (patch-1, inian-patch-1),
|
|
61
|
+
* version numbers (1.0, 2.73.0-rc.5), sub-counters after another number
|
|
62
|
+
* (claude/task-22584764486-1), bare counters under 100 mid/trailing
|
|
63
|
+
* (bundle-size-3-migrate, ...-try-2), and alphanumerics like "oauth2"
|
|
64
|
+
* (letter boundaries are not delimiters).
|
|
65
|
+
*
|
|
66
|
+
* Found by the maintainer-seeding runs: the seeding reports document 6
|
|
67
|
+
* false-positive same-issue clusters, all from branch numbers that are not
|
|
68
|
+
* issue references (supabase #1/#2 from patch-N branches, posthog #500
|
|
69
|
+
* from "500 errors" branches, n8n #1 from "prior-to-1.0" and a bot task
|
|
70
|
+
* counter, langfuse #1/#3 from patch-1 and bundle-size-3-migrate). Each is
|
|
71
|
+
* pinned by a regression test naming the source.
|
|
72
|
+
*/
|
|
73
|
+
export declare function branchIssueRefs(branch: string): number[];
|
|
74
|
+
/**
|
|
75
|
+
* The issue a PR is really working on: the referenced issue number for
|
|
76
|
+
* which classifyLinkStrength says "closing". Bare mentions (pdfcn #91
|
|
77
|
+
* listing sibling issues) never cluster. Candidate numbers come from
|
|
78
|
+
* title/body #N refs AND branchIssueRefs (the tightened branch rule set,
|
|
79
|
+
* so "oauth2", "patch-N", status codes, versions, and counters never
|
|
80
|
+
* yield candidates); both go through the same classifyLinkStrength
|
|
81
|
+
* gate. Pure.
|
|
82
|
+
*/
|
|
83
|
+
export declare function closingIssueForPR(pr: RadarPR): number | undefined;
|
|
84
|
+
/** Significant lowercase title tokens for the cheap overlap pre-filter. Pure. */
|
|
85
|
+
export declare function titleTokens(title: string): string[];
|
|
86
|
+
/**
|
|
87
|
+
* Two PRs are overlap candidates when their title tokens overlap enough.
|
|
88
|
+
* Pure; the expensive file fetch happens only past this gate.
|
|
89
|
+
*/
|
|
90
|
+
export declare function titlePrefilterHit(a: RadarPR, b: RadarPR): boolean;
|
|
91
|
+
/** Full file-set Jaccard; requires a shared top-level directory too. Pure. */
|
|
92
|
+
export declare function filesOverlap(a: string[], b: string[]): {
|
|
93
|
+
overlap: boolean;
|
|
94
|
+
jaccard: number;
|
|
95
|
+
sharedDir?: string;
|
|
96
|
+
};
|
|
97
|
+
/** Connected components over PR numbers. Pure. */
|
|
98
|
+
export declare function clusterPairs(members: number[], pairs: Array<[number, number]>): number[][];
|
|
99
|
+
/** Heuristic keep pick: earliest opened, then most files, then greenest CI. */
|
|
100
|
+
export declare function suggestKeep(cluster: RadarPR[], ciState: Map<number, string>): {
|
|
101
|
+
keep: number;
|
|
102
|
+
reason: string;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The radar pipeline. Best-effort: a failed file fetch removes that PR
|
|
106
|
+
* from overlap consideration instead of failing the whole run.
|
|
107
|
+
*/
|
|
108
|
+
export declare function radarPRs(owner: string, repo: string, opts?: {
|
|
109
|
+
limit?: number;
|
|
110
|
+
}, deps?: RadarDeps): Promise<RadarResult>;
|
package/dist/radar.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR Radar: maintainer-side duplicate / competing PR detection.
|
|
3
|
+
*
|
|
4
|
+
* The maintainer control-room wedge from the roadmap. In the AI-PR era,
|
|
5
|
+
* maintainers drown in overlapping submissions: two contributors solving
|
|
6
|
+
* the same issue, three bots regenerating the same migration, an old
|
|
7
|
+
* stalled PR superseded by a fresh one. Review bots (CodeRabbit, Greptile)
|
|
8
|
+
* live inside one repo's pipeline and do not answer "which of these open
|
|
9
|
+
* PRs are doing the same work?" — that is the boundary layer Greenlight
|
|
10
|
+
* owns, and every claim here carries quoted evidence, never a generic
|
|
11
|
+
* AI opinion.
|
|
12
|
+
*
|
|
13
|
+
* Clustering (documented, deterministic):
|
|
14
|
+
* (a) same linked issue: a PR whose title/body/branch has CLOSING
|
|
15
|
+
* strength against an issue number it references (reusing
|
|
16
|
+
* classifyLinkStrength from linkstrength.js — the same machinery
|
|
17
|
+
* the check pipeline and Timeline X-Ray use; a bare "#N" mention
|
|
18
|
+
* is explicitly not enough, cf. issue #1).
|
|
19
|
+
* (b) no shared closing issue, but high changed-file overlap: pairs are
|
|
20
|
+
* cheaply pre-filtered by title-token overlap (Jaccard >= 0.4 on
|
|
21
|
+
* significant title tokens) so file lists are fetched only for
|
|
22
|
+
* plausible pairs; then Jaccard similarity on the full filename sets
|
|
23
|
+
* >= 0.6 AND a shared top-level directory -> cluster. File-list
|
|
24
|
+
* fetches are cached.
|
|
25
|
+
* Clusters are connected components over the pair graph: if A overlaps B
|
|
26
|
+
* and B overlaps C, all three land in one cluster even when A and C
|
|
27
|
+
* only touch lightly.
|
|
28
|
+
*
|
|
29
|
+
* Suggested keep (always labeled heuristic, never a verdict): earliest
|
|
30
|
+
* opened wins ties by PR number; within a cluster the pick is the earliest
|
|
31
|
+
* created_at, then the PR touching the most files, then the greenest
|
|
32
|
+
* combined CI state (fetched only for the top two age candidates, so CI
|
|
33
|
+
* never costs more than 2 calls per cluster).
|
|
34
|
+
*
|
|
35
|
+
* Budget (measured and reported in output):
|
|
36
|
+
* PR list: 1 call per 100 PRs (capped at 2 pages); linked-issue
|
|
37
|
+
* classification is free (title/body/branch regex); file lists: 1 call
|
|
38
|
+
* per 100 files per pre-filtered PR; CI status: <= 2 calls per cluster.
|
|
39
|
+
* Everything rides the existing file cache.
|
|
40
|
+
*/
|
|
41
|
+
import { apiCallsUsed, apiGet, cacheGet, cacheSet } from "./github.js";
|
|
42
|
+
import { classifyLinkStrength } from "./linkstrength.js";
|
|
43
|
+
const MAX_PR_PAGES = 2;
|
|
44
|
+
const JACCARD_FILE_THRESHOLD = 0.6;
|
|
45
|
+
const JACCARD_TITLE_PREFILTER = 0.4;
|
|
46
|
+
function toRadarPR(item) {
|
|
47
|
+
return {
|
|
48
|
+
number: Number(item.number ?? 0),
|
|
49
|
+
title: String(item.title ?? ""),
|
|
50
|
+
body: String(item.body ?? ""),
|
|
51
|
+
author: String(item.user?.login ?? "unknown"),
|
|
52
|
+
branch: String(item.head?.ref ?? ""),
|
|
53
|
+
headSha: String(item.head?.sha ?? ""),
|
|
54
|
+
createdAt: String(item.created_at ?? ""),
|
|
55
|
+
url: String(item.html_url ?? ""),
|
|
56
|
+
draft: Boolean(item.draft),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function defaultListOpenPRs(owner, repo, limit) {
|
|
60
|
+
const key = [owner, repo, `open-prs-${limit}`];
|
|
61
|
+
const cached = cacheGet("search", key);
|
|
62
|
+
if (cached)
|
|
63
|
+
return cached;
|
|
64
|
+
const out = [];
|
|
65
|
+
for (let page = 1; page <= MAX_PR_PAGES && out.length < limit; page++) {
|
|
66
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls?state=open&sort=created&direction=asc&per_page=100&page=${page}`));
|
|
67
|
+
out.push(...(data ?? []).map(toRadarPR));
|
|
68
|
+
if ((data ?? []).length < 100)
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
const sliced = out.slice(0, limit);
|
|
72
|
+
cacheSet("search", key, sliced);
|
|
73
|
+
return sliced;
|
|
74
|
+
}
|
|
75
|
+
async function defaultFetchFiles(owner, repo, prNumber) {
|
|
76
|
+
const key = [owner, repo, String(prNumber), "files"];
|
|
77
|
+
const cached = cacheGet("search", key);
|
|
78
|
+
if (cached)
|
|
79
|
+
return cached;
|
|
80
|
+
const files = [];
|
|
81
|
+
for (let page = 1; page <= 5; page++) {
|
|
82
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/pulls/${prNumber}/files?per_page=100&page=${page}`));
|
|
83
|
+
for (const f of data ?? []) {
|
|
84
|
+
if (typeof f.filename === "string")
|
|
85
|
+
files.push(f.filename);
|
|
86
|
+
}
|
|
87
|
+
if ((data ?? []).length < 100)
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
cacheSet("search", key, files);
|
|
91
|
+
return files;
|
|
92
|
+
}
|
|
93
|
+
async function defaultFetchCombinedState(owner, repo, sha) {
|
|
94
|
+
if (!sha)
|
|
95
|
+
return "unknown";
|
|
96
|
+
try {
|
|
97
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/commits/${sha}/status`));
|
|
98
|
+
return String(data.state ?? "unknown");
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return "unknown";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Issue numbers referenced in a PR title/body, deduplicated. */
|
|
105
|
+
export function extractIssueRefs(text) {
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const out = [];
|
|
108
|
+
const re = /#(\d{1,6})\b/g;
|
|
109
|
+
let m;
|
|
110
|
+
while ((m = re.exec(text)) !== null) {
|
|
111
|
+
const n = Number(m[1]);
|
|
112
|
+
if (n > 0 && !seen.has(n)) {
|
|
113
|
+
seen.add(n);
|
|
114
|
+
out.push(n);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Tokens that, immediately before a number in a branch name, mark it as an
|
|
121
|
+
* intentional issue reference ("fix-9483-wsdl", "issue-61"). Pure data.
|
|
122
|
+
*/
|
|
123
|
+
const ISSUE_PREFIX_TOKENS = new Set([
|
|
124
|
+
"fix",
|
|
125
|
+
"fixes",
|
|
126
|
+
"fixed",
|
|
127
|
+
"bug",
|
|
128
|
+
"bugfix",
|
|
129
|
+
"hotfix",
|
|
130
|
+
"issue",
|
|
131
|
+
"issues",
|
|
132
|
+
"feat",
|
|
133
|
+
"feature",
|
|
134
|
+
"pr",
|
|
135
|
+
"gh",
|
|
136
|
+
"ticket",
|
|
137
|
+
]);
|
|
138
|
+
/**
|
|
139
|
+
* Common HTTP status codes. A bare one in a branch name ("fix-mcp-oauth-500",
|
|
140
|
+
* "fix-experiment-exposure-500-missing-variants") is an error code, not an
|
|
141
|
+
* issue number; it only counts with an explicit marker (#N or a prefix token).
|
|
142
|
+
*/
|
|
143
|
+
const HTTP_STATUS_CODES = new Set([
|
|
144
|
+
400, 401, 402, 403, 404, 405, 406, 408, 409, 410, 413, 414, 415, 416, 417, 418,
|
|
145
|
+
421, 422, 423, 425, 426, 428, 429, 431, 451,
|
|
146
|
+
500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511,
|
|
147
|
+
]);
|
|
148
|
+
/**
|
|
149
|
+
* Issue numbers referenced by a branch name, deduplicated. Pure.
|
|
150
|
+
*
|
|
151
|
+
* A number counts ONLY under a defensible convention:
|
|
152
|
+
* - explicit "#N" marker in the branch;
|
|
153
|
+
* - an issue-prefix token immediately before it ("fix-9483-wsdl", "issue-61");
|
|
154
|
+
* - first token of a branch segment ("feat/61-resume-block", "123-fix-x");
|
|
155
|
+
* - trailing token at the end of the branch when it is a 3+ digit,
|
|
156
|
+
* non-status-code number preceded by a word ("fix/studio-sms-...-6435");
|
|
157
|
+
* - bare mid-branch token when it is a 3+ digit, non-status-code number
|
|
158
|
+
* ("pay-852-partial-...").
|
|
159
|
+
* Never counted: GitHub web-UI "patch-N" names (patch-1, inian-patch-1),
|
|
160
|
+
* version numbers (1.0, 2.73.0-rc.5), sub-counters after another number
|
|
161
|
+
* (claude/task-22584764486-1), bare counters under 100 mid/trailing
|
|
162
|
+
* (bundle-size-3-migrate, ...-try-2), and alphanumerics like "oauth2"
|
|
163
|
+
* (letter boundaries are not delimiters).
|
|
164
|
+
*
|
|
165
|
+
* Found by the maintainer-seeding runs: the seeding reports document 6
|
|
166
|
+
* false-positive same-issue clusters, all from branch numbers that are not
|
|
167
|
+
* issue references (supabase #1/#2 from patch-N branches, posthog #500
|
|
168
|
+
* from "500 errors" branches, n8n #1 from "prior-to-1.0" and a bot task
|
|
169
|
+
* counter, langfuse #1/#3 from patch-1 and bundle-size-3-migrate). Each is
|
|
170
|
+
* pinned by a regression test naming the source.
|
|
171
|
+
*/
|
|
172
|
+
export function branchIssueRefs(branch) {
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
const out = [];
|
|
175
|
+
const add = (n) => {
|
|
176
|
+
if (n > 0 && n <= 999999 && !seen.has(n)) {
|
|
177
|
+
seen.add(n);
|
|
178
|
+
out.push(n);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
for (const segment of branch.split("/")) {
|
|
182
|
+
// Word tokens with raw offsets (letters/digits; "oauth2" stays one token).
|
|
183
|
+
const tokens = [];
|
|
184
|
+
const re = /[A-Za-z0-9]+/g;
|
|
185
|
+
let m;
|
|
186
|
+
while ((m = re.exec(segment)) !== null) {
|
|
187
|
+
tokens.push({ text: m[0], index: m.index });
|
|
188
|
+
}
|
|
189
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
190
|
+
const { text: w, index } = tokens[i];
|
|
191
|
+
if (!/^\d+$/.test(w))
|
|
192
|
+
continue;
|
|
193
|
+
const n = Number(w);
|
|
194
|
+
const prev = i > 0 ? tokens[i - 1].text.toLowerCase() : "";
|
|
195
|
+
const after = segment.slice(index + w.length, index + w.length + 2);
|
|
196
|
+
if (/^\.\d/.test(after))
|
|
197
|
+
continue; // version: "1.0", "2.73.0-rc.5"
|
|
198
|
+
if (prev === "patch")
|
|
199
|
+
continue; // GitHub web-UI branch: patch-1, inian-patch-1
|
|
200
|
+
if (/^\d+$/.test(prev))
|
|
201
|
+
continue; // sub-counter after a number: task-22584764486-1
|
|
202
|
+
const marked = new RegExp(`#${n}\\b`).test(branch) || ISSUE_PREFIX_TOKENS.has(prev);
|
|
203
|
+
if (marked) {
|
|
204
|
+
add(n);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (i === 0) {
|
|
208
|
+
// Issue-first branch convention: "feat/61-resume-block", "123-fix-x".
|
|
209
|
+
add(n);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (HTTP_STATUS_CODES.has(n))
|
|
213
|
+
continue; // unmarked status code: an error code.
|
|
214
|
+
if (n >= 100)
|
|
215
|
+
add(n); // bare 3+ digit token, mid-branch or trailing.
|
|
216
|
+
// Bare 1-2 digit tokens mid-branch or trailing ("-3-", "-try-2") are
|
|
217
|
+
// counters, not issues.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The issue a PR is really working on: the referenced issue number for
|
|
224
|
+
* which classifyLinkStrength says "closing". Bare mentions (pdfcn #91
|
|
225
|
+
* listing sibling issues) never cluster. Candidate numbers come from
|
|
226
|
+
* title/body #N refs AND branchIssueRefs (the tightened branch rule set,
|
|
227
|
+
* so "oauth2", "patch-N", status codes, versions, and counters never
|
|
228
|
+
* yield candidates); both go through the same classifyLinkStrength
|
|
229
|
+
* gate. Pure.
|
|
230
|
+
*/
|
|
231
|
+
export function closingIssueForPR(pr) {
|
|
232
|
+
const candidates = [];
|
|
233
|
+
for (const n of extractIssueRefs(`${pr.title}\n${pr.body}`)) {
|
|
234
|
+
if (n !== pr.number && !candidates.includes(n))
|
|
235
|
+
candidates.push(n);
|
|
236
|
+
}
|
|
237
|
+
for (const n of branchIssueRefs(pr.branch)) {
|
|
238
|
+
if (n !== pr.number && !candidates.includes(n))
|
|
239
|
+
candidates.push(n);
|
|
240
|
+
}
|
|
241
|
+
for (const n of candidates) {
|
|
242
|
+
if (classifyLinkStrength({ title: pr.title, body: pr.body, branch: pr.branch, issueNumber: n }) ===
|
|
243
|
+
"closing") {
|
|
244
|
+
return n;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
const STOPWORDS = new Set("the,a,an,and,or,of,to,in,for,on,with,by,from,fix,fixes,fixed,add,adds,added,update,updates,updated,support,bug,issue,feature,request,via,into,vs,new,old,all,use,used,this,that,these,those,are,is,was,were,be,been,as,at,it,its,it’s,i".split(","));
|
|
250
|
+
/** Significant lowercase title tokens for the cheap overlap pre-filter. Pure. */
|
|
251
|
+
export function titleTokens(title) {
|
|
252
|
+
return title
|
|
253
|
+
.toLowerCase()
|
|
254
|
+
.split(/[^a-z0-9]+/)
|
|
255
|
+
.filter((t) => t.length >= 3 && !STOPWORDS.has(t));
|
|
256
|
+
}
|
|
257
|
+
function jaccard(a, b) {
|
|
258
|
+
if (a.size === 0 && b.size === 0)
|
|
259
|
+
return 0;
|
|
260
|
+
let inter = 0;
|
|
261
|
+
for (const x of a)
|
|
262
|
+
if (b.has(x))
|
|
263
|
+
inter++;
|
|
264
|
+
return inter / (a.size + b.size - inter);
|
|
265
|
+
}
|
|
266
|
+
const topLevel = (path) => path.split("/")[0] ?? "";
|
|
267
|
+
/**
|
|
268
|
+
* Two PRs are overlap candidates when their title tokens overlap enough.
|
|
269
|
+
* Pure; the expensive file fetch happens only past this gate.
|
|
270
|
+
*/
|
|
271
|
+
export function titlePrefilterHit(a, b) {
|
|
272
|
+
const ta = new Set(titleTokens(a.title));
|
|
273
|
+
const tb = new Set(titleTokens(b.title));
|
|
274
|
+
if (ta.size === 0 || tb.size === 0)
|
|
275
|
+
return false;
|
|
276
|
+
return jaccard(ta, tb) >= JACCARD_TITLE_PREFILTER;
|
|
277
|
+
}
|
|
278
|
+
/** Full file-set Jaccard; requires a shared top-level directory too. Pure. */
|
|
279
|
+
export function filesOverlap(a, b) {
|
|
280
|
+
const sa = new Set(a);
|
|
281
|
+
const sb = new Set(b);
|
|
282
|
+
const j = jaccard(sa, sb);
|
|
283
|
+
if (j < JACCARD_FILE_THRESHOLD)
|
|
284
|
+
return { overlap: false, jaccard: j };
|
|
285
|
+
const dirsA = new Set(a.map(topLevel));
|
|
286
|
+
const shared = b.map(topLevel).find((d) => dirsA.has(d) && d !== "");
|
|
287
|
+
if (!shared)
|
|
288
|
+
return { overlap: false, jaccard: j };
|
|
289
|
+
return { overlap: true, jaccard: j, sharedDir: shared };
|
|
290
|
+
}
|
|
291
|
+
/** Connected components over PR numbers. Pure. */
|
|
292
|
+
export function clusterPairs(members, pairs) {
|
|
293
|
+
const parent = new Map();
|
|
294
|
+
const find = (x) => {
|
|
295
|
+
const p = parent.get(x) ?? x;
|
|
296
|
+
if (p !== x)
|
|
297
|
+
parent.set(x, find(p));
|
|
298
|
+
return parent.get(x) ?? x;
|
|
299
|
+
};
|
|
300
|
+
const union = (a, b) => parent.set(find(a), find(b));
|
|
301
|
+
for (const n of members)
|
|
302
|
+
parent.set(n, n);
|
|
303
|
+
for (const [a, b] of pairs)
|
|
304
|
+
union(a, b);
|
|
305
|
+
const groups = new Map();
|
|
306
|
+
for (const n of members) {
|
|
307
|
+
const r = find(n);
|
|
308
|
+
const g = groups.get(r) ?? [];
|
|
309
|
+
g.push(n);
|
|
310
|
+
groups.set(r, g);
|
|
311
|
+
}
|
|
312
|
+
return [...groups.values()].filter((g) => g.length > 1);
|
|
313
|
+
}
|
|
314
|
+
/** Heuristic keep pick: earliest opened, then most files, then greenest CI. */
|
|
315
|
+
export function suggestKeep(cluster, ciState) {
|
|
316
|
+
const sorted = [...cluster].sort((a, b) => {
|
|
317
|
+
if (a.createdAt !== b.createdAt)
|
|
318
|
+
return a.createdAt < b.createdAt ? -1 : 1;
|
|
319
|
+
return a.number - b.number;
|
|
320
|
+
});
|
|
321
|
+
const byFiles = [...sorted].sort((a, b) => (b.changedFiles?.length ?? 0) - (a.changedFiles?.length ?? 0));
|
|
322
|
+
const oldest = sorted[0];
|
|
323
|
+
const mostFiles = byFiles[0];
|
|
324
|
+
const reasons = [`earliest opened (${oldest.createdAt.slice(0, 10)})`];
|
|
325
|
+
let keep = oldest.number;
|
|
326
|
+
if (mostFiles.number !== oldest.number) {
|
|
327
|
+
reasons.push(`touches most files (#${mostFiles.number}: ${mostFiles.changedFiles?.length ?? 0})`);
|
|
328
|
+
}
|
|
329
|
+
// Greenest CI only among the top-2 age candidates (budget-capped).
|
|
330
|
+
const green = sorted.slice(0, 2).find((p) => ciState.get(p.number) === "success");
|
|
331
|
+
if (green && green.number !== keep) {
|
|
332
|
+
reasons.push(`#${green.number} has green CI`);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
keep,
|
|
336
|
+
reason: `heuristic: ${reasons.join("; ")} — suggestion only, not a verdict`,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
const ageDays = (iso) => {
|
|
340
|
+
const t = Date.parse(iso);
|
|
341
|
+
if (!Number.isFinite(t))
|
|
342
|
+
return 0;
|
|
343
|
+
return Math.floor((Date.now() - t) / (24 * 60 * 60 * 1000));
|
|
344
|
+
};
|
|
345
|
+
/**
|
|
346
|
+
* The radar pipeline. Best-effort: a failed file fetch removes that PR
|
|
347
|
+
* from overlap consideration instead of failing the whole run.
|
|
348
|
+
*/
|
|
349
|
+
export async function radarPRs(owner, repo, opts = {}, deps = {}) {
|
|
350
|
+
const callsBefore = apiCallsUsed();
|
|
351
|
+
const limit = Math.max(1, Math.min(opts.limit ?? 25, 100));
|
|
352
|
+
const listOpenPRs = deps.listOpenPRs ?? defaultListOpenPRs;
|
|
353
|
+
const fetchFiles = deps.fetchFiles ?? defaultFetchFiles;
|
|
354
|
+
const fetchCombinedState = deps.fetchCombinedState ?? defaultFetchCombinedState;
|
|
355
|
+
const prs = await listOpenPRs(owner, repo, limit);
|
|
356
|
+
const byNumber = new Map(prs.map((p) => [p.number, p]));
|
|
357
|
+
// Pass A: same-issue clusters via closing-strength links.
|
|
358
|
+
const issueGroups = new Map();
|
|
359
|
+
for (const pr of prs) {
|
|
360
|
+
const issue = closingIssueForPR(pr);
|
|
361
|
+
if (issue === undefined)
|
|
362
|
+
continue;
|
|
363
|
+
const g = issueGroups.get(issue) ?? [];
|
|
364
|
+
g.push(pr);
|
|
365
|
+
issueGroups.set(issue, g);
|
|
366
|
+
}
|
|
367
|
+
const clustered = new Set();
|
|
368
|
+
const clusters = [];
|
|
369
|
+
for (const [issue, group] of issueGroups) {
|
|
370
|
+
if (group.length < 2)
|
|
371
|
+
continue;
|
|
372
|
+
for (const p of group)
|
|
373
|
+
clustered.add(p.number);
|
|
374
|
+
const ciState = await ciStateFor(owner, repo, group, fetchCombinedState);
|
|
375
|
+
const { keep, reason } = suggestKeep(group, ciState);
|
|
376
|
+
clusters.push({
|
|
377
|
+
kind: "same-issue",
|
|
378
|
+
issue: { number: issue, url: `https://github.com/${owner}/${repo}/issues/${issue}` },
|
|
379
|
+
prs: group.map((p) => p.number).sort((a, b) => a - b),
|
|
380
|
+
authors: [...new Set(group.map((p) => p.author))],
|
|
381
|
+
evidence: group.map((p) => `#${p.number} "${p.title}" by @${p.author} (${ageDays(p.createdAt)}d old) closes #${issue} (${p.url})`),
|
|
382
|
+
suggestedKeep: keep,
|
|
383
|
+
suggestedKeepReason: reason,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// Pass B: file-overlap clusters among the rest. Files fetched only for
|
|
387
|
+
// title-prefiltered pairs; a PR failing its fetch just sits out.
|
|
388
|
+
const rest = prs.filter((p) => !clustered.has(p.number));
|
|
389
|
+
const pairs = [];
|
|
390
|
+
const evidenceForPair = new Map();
|
|
391
|
+
const fileCache = new Map();
|
|
392
|
+
const filesOf = async (p) => {
|
|
393
|
+
if (fileCache.has(p.number))
|
|
394
|
+
return fileCache.get(p.number);
|
|
395
|
+
try {
|
|
396
|
+
const f = await fetchFiles(owner, repo, p.number);
|
|
397
|
+
p.changedFiles = f;
|
|
398
|
+
fileCache.set(p.number, f);
|
|
399
|
+
return f;
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
fileCache.set(p.number, null);
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
for (let i = 0; i < rest.length; i++) {
|
|
407
|
+
for (let j = i + 1; j < rest.length; j++) {
|
|
408
|
+
const a = rest[i];
|
|
409
|
+
const b = rest[j];
|
|
410
|
+
if (!titlePrefilterHit(a, b))
|
|
411
|
+
continue;
|
|
412
|
+
const fa = await filesOf(a);
|
|
413
|
+
const fb = await filesOf(b);
|
|
414
|
+
if (!fa || !fb)
|
|
415
|
+
continue;
|
|
416
|
+
const fo = filesOverlap(fa, fb);
|
|
417
|
+
if (!fo.overlap)
|
|
418
|
+
continue;
|
|
419
|
+
pairs.push([a.number, b.number]);
|
|
420
|
+
evidenceForPair.set(`${Math.min(a.number, b.number)}-${Math.max(a.number, b.number)}`, `file overlap ${(fo.jaccard * 100).toFixed(0)}% (Jaccard) in ${fo.sharedDir ?? "?"}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
for (const group of clusterPairs(rest.map((p) => p.number), pairs)) {
|
|
424
|
+
for (const n of group)
|
|
425
|
+
clustered.add(n);
|
|
426
|
+
const members = group.map((n) => byNumber.get(n));
|
|
427
|
+
const ciState = await ciStateFor(owner, repo, members, fetchCombinedState);
|
|
428
|
+
const { keep, reason } = suggestKeep(members, ciState);
|
|
429
|
+
const ev = [];
|
|
430
|
+
for (let i = 0; i < group.length; i++) {
|
|
431
|
+
for (let j = i + 1; j < group.length; j++) {
|
|
432
|
+
const k = `${Math.min(group[i], group[j])}-${Math.max(group[i], group[j])}`;
|
|
433
|
+
if (evidenceForPair.has(k))
|
|
434
|
+
ev.push(`#${group[i]} ~ #${group[j]}: ${evidenceForPair.get(k)}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
clusters.push({
|
|
438
|
+
kind: "file-overlap",
|
|
439
|
+
prs: [...group].sort((a, b) => a - b),
|
|
440
|
+
authors: [...new Set(members.map((p) => p.author))],
|
|
441
|
+
evidence: ev,
|
|
442
|
+
suggestedKeep: keep,
|
|
443
|
+
suggestedKeepReason: reason,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
const unclustered = prs.map((p) => p.number).filter((n) => !clustered.has(n));
|
|
447
|
+
return {
|
|
448
|
+
repo: `${owner}/${repo}`,
|
|
449
|
+
prsScanned: prs.length,
|
|
450
|
+
clusters: clusters.sort((a, b) => a.prs[0] - b.prs[0]),
|
|
451
|
+
unclustered,
|
|
452
|
+
apiCalls: apiCallsUsed() - callsBefore,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
async function ciStateFor(owner, repo, group, fetchCombinedState) {
|
|
456
|
+
const out = new Map();
|
|
457
|
+
const byAge = [...group].sort((a, b) => a.number - b.number).slice(0, 2);
|
|
458
|
+
for (const p of byAge) {
|
|
459
|
+
try {
|
|
460
|
+
out.set(p.number, await fetchCombinedState(owner, repo, p.headSha));
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
out.set(p.number, "unknown");
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=radar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radar.js","sourceRoot":"","sources":["../src/radar.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"@modelcontextprotocol/sdk": "^1",
|
|
7
7
|
"zod": "^3"
|
|
8
8
|
},
|
|
9
|
-
"description": "Pre-flight
|
|
9
|
+
"description": "Pre-flight and coordination layer for open-source contribution: issue verdicts, Timeline X-Ray, PR radar and review briefs. CLI + MCP server.",
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@types/node": "^20",
|
|
12
12
|
"tsx": "^4",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"test": "tsx --test test/*.test.ts"
|
|
26
26
|
},
|
|
27
27
|
"type": "module",
|
|
28
|
-
"version": "0.
|
|
28
|
+
"version": "0.15.1",
|
|
29
29
|
"mcpName": "io.github.CloudCorpRecords/greenlight",
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
package/server.json
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
"url": "https://github.com/CloudCorpRecords/greenlight",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.15.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
14
14
|
"identifier": "@reneturcios/greenlight",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.15.1",
|
|
16
16
|
"runtimeHint": "npx",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{
|