@reneturcios/greenlight 0.0.0-stage → 0.3.3

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.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Friction index generator: `npm run index`.
3
+ *
4
+ * Builds data/friction-index.json for the repos we hand-vetted on
5
+ * 2026-10-13. For each repo it tries the live CONTRIBUTING.md parse first;
6
+ * when the API is unreachable or rate-limited it falls back to the
7
+ * hand-verified gate list below and labels the entry accordingly.
8
+ *
9
+ * Rule: fields we cannot compute live stay null with a note. We never
10
+ * fabricate numbers.
11
+ */
12
+ import { mkdirSync, writeFileSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { parseGates, hardGates } from "./gates.js";
16
+ import { scoreFriction } from "./friction.js";
17
+ import { fetchContributing } from "./github.js";
18
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
19
+ function gate(kind, evidence) {
20
+ return { kind, detected: true, evidence };
21
+ }
22
+ const HAND = [
23
+ {
24
+ owner: "PrefectHQ", repo: "fastmcp",
25
+ gates: [gate("assignment_required", "hand-verified 2026-10-13: issue must be assigned or carry a 'prs welcome' label (PrefectHQ/fastmcp PR #4557)")],
26
+ note: "hand-verified 2026-10-13",
27
+ },
28
+ {
29
+ owner: "modelcontextprotocol", repo: "inspector",
30
+ gates: [gate("collaborators_only", "hand-verified 2026-10-15: AGENTS.md states PRs are opened by repo maintainers only; outsiders file detailed issues (verified while working #2723)")],
31
+ note: "hand-verified 2026-10-15",
32
+ },
33
+ {
34
+ owner: "modelcontextprotocol", repo: "servers",
35
+ gates: [gate("collaborators_only", "hand-verified 2026-10-15: CONTRIBUTING.md states 'We accept issues, not pull requests'; outsiders share the prompt behind a local fix (verified while working #5153)")],
36
+ note: "hand-verified 2026-10-15",
37
+ },
38
+ {
39
+ owner: "triggerdotdev", repo: "trigger.dev",
40
+ gates: [gate("vouch_required", "hand-verified 2026-10-13: unvouched-author PRs auto-closed; vouch-request issue required first")],
41
+ note: "hand-verified 2026-10-13",
42
+ },
43
+ {
44
+ owner: "langchain-ai", repo: "langchain",
45
+ gates: [],
46
+ note: "hand-verified 2026-10-13: no codified gate found, but reporters routinely request assignment before coding",
47
+ },
48
+ {
49
+ owner: "langchain-ai", repo: "deepagents",
50
+ gates: [],
51
+ note: "hand-verified 2026-10-13: no codified gate found; assignment-request culture in comments",
52
+ },
53
+ {
54
+ owner: "openclaw", repo: "openclaw",
55
+ gates: [],
56
+ note: "hand-verified 2026-10-13: heavy bot triage (ClawSweeper) is advisory only; no hard pre-PR gate",
57
+ },
58
+ {
59
+ owner: "batrapulkit", repo: "squidbrake",
60
+ gates: [gate("claim_first", "hand-verified 2026-10-14: maintainer asks contributors to comment to claim before starting")],
61
+ note: "hand-verified 2026-10-13",
62
+ },
63
+ {
64
+ owner: "reticlehq", repo: "reticle",
65
+ gates: [],
66
+ note: "hand-verified 2026-10-13: PR policy 'all', no pre-PR gate",
67
+ },
68
+ {
69
+ owner: "shadcn-labs", repo: "pdfcn",
70
+ gates: [],
71
+ note: "hand-verified 2026-10-13: no formal gate, but good-first-issues are claimed within hours (claim speed is the game)",
72
+ },
73
+ {
74
+ owner: "GustavoBaranda", repo: "chatflow-agent",
75
+ gates: [],
76
+ note: "hand-verified 2026-10-13: no pre-PR gate observed",
77
+ },
78
+ {
79
+ owner: "modelcontextprotocol", repo: "typescript-sdk",
80
+ gates: [gate("assignment_required", "hand-verified 2026-10-13: SDKs expect a linked assigned or 'help wanted' issue, or an assignment comment")],
81
+ note: "hand-verified 2026-10-13",
82
+ },
83
+ {
84
+ owner: "modelcontextprotocol", repo: "python-sdk",
85
+ gates: [gate("assignment_required", "hand-verified 2026-10-13: same assignment culture as the other MCP SDKs")],
86
+ note: "hand-verified 2026-10-13",
87
+ },
88
+ {
89
+ owner: "modelcontextprotocol", repo: "servers",
90
+ gates: [gate("collaborators_only", "hand-verified 2026-10-13: PR creation policy collaborators_only")],
91
+ note: "hand-verified 2026-10-13",
92
+ },
93
+ ];
94
+ /** Merge live-parsed gates with hand-verified gates: hand data wins on kind, live can add new kinds. */
95
+ function mergeGates(hand, live) {
96
+ const byKind = new Map();
97
+ for (const g of live)
98
+ byKind.set(g.kind, g);
99
+ for (const g of hand)
100
+ byKind.set(g.kind, g); // hand-verified takes precedence
101
+ const all = [];
102
+ const seen = new Set();
103
+ for (const g of [...hand, ...live]) {
104
+ if (seen.has(g.kind))
105
+ continue;
106
+ seen.add(g.kind);
107
+ all.push(byKind.get(g.kind));
108
+ }
109
+ return all;
110
+ }
111
+ async function main() {
112
+ const reports = [];
113
+ let liveOk = 0;
114
+ let liveFailed = 0;
115
+ for (const entry of HAND) {
116
+ let contributing;
117
+ let liveNote = entry.note;
118
+ try {
119
+ contributing = await fetchContributing(entry.owner, entry.repo);
120
+ liveOk++;
121
+ liveNote = "live 2026-10-13, merged with hand-verified gates";
122
+ }
123
+ catch {
124
+ liveFailed++;
125
+ contributing = undefined;
126
+ }
127
+ const liveGates = parseGates(contributing);
128
+ const gates = mergeGates(entry.gates, liveGates);
129
+ const report = scoreFriction({
130
+ owner: entry.owner,
131
+ repo: entry.repo,
132
+ gates,
133
+ externalPRMergeRate: null,
134
+ medianFirstReviewHours: null,
135
+ maintainerIssueResponseHours: null,
136
+ openGoodFirstIssueCount: null,
137
+ note: liveNote,
138
+ });
139
+ // Surface the hard gates in reasons even when score math already did.
140
+ const hard = hardGates(gates);
141
+ if (hard.length > 0)
142
+ report.reasons.unshift(`HARD GATE: ${hard.map((g) => g.kind).join(", ")}`);
143
+ reports.push(report);
144
+ }
145
+ reports.sort((a, b) => b.score - a.score);
146
+ const outDir = join(ROOT, "data");
147
+ mkdirSync(outDir, { recursive: true });
148
+ writeFileSync(join(outDir, "friction-index.json"), JSON.stringify({ generated: new Date().toISOString(), repos: reports }, null, 2), "utf8");
149
+ console.log(`wrote data/friction-index.json with ${reports.length} repos (live fetch ok: ${liveOk}, fallback: ${liveFailed})`);
150
+ }
151
+ await main();
152
+ //# sourceMappingURL=friction-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friction-index.js","sourceRoot":"","sources":["../src/friction-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Repo friction score: 0-100, where 100 = easiest place for an outsider
3
+ * to land a PR.
4
+ *
5
+ * Formula (documented, deterministic):
6
+ * start at 100
7
+ * gates:
8
+ * assignment_required -25
9
+ * vouch_required -35
10
+ * collaborators_only -50
11
+ * unsolicited_prs_paused -40
12
+ * external_prs_closed -50
13
+ * discussion_first -10
14
+ * claim_first -5
15
+ * cla_required -5
16
+ * dco_required -5
17
+ * outcome signals (only when computed, never guessed):
18
+ * external PR merge rate: +(rate - 0.5) * 40
19
+ * (a 100% external merge rate adds 20; 0% subtracts 20)
20
+ * median hours to first review: -min(hours / 24, 14)
21
+ * (a week of waiting costs 7 points, capped at 14)
22
+ * maintainer hours to first issue response: -min(hours / 48, 10)
23
+ * opportunity:
24
+ * open good-first-issue count: +min(count, 20) * 0.5 (max +10)
25
+ * clamp to [0, 100], rounded.
26
+ *
27
+ * Null signals are simply skipped: the index never fabricates numbers.
28
+ */
29
+ import type { FrictionReport, RepoSignals } from "./types.js";
30
+ export declare function scoreFriction(signals: RepoSignals): FrictionReport;
@@ -0,0 +1,56 @@
1
+ import { gateLabel } from "./gates.js";
2
+ const GATE_WEIGHTS = {
3
+ assignment_required: -25,
4
+ vouch_required: -35,
5
+ collaborators_only: -50,
6
+ unsolicited_prs_paused: -40,
7
+ external_prs_closed: -50,
8
+ discussion_first: -10,
9
+ claim_first: -5,
10
+ cla_required: -5,
11
+ dco_required: -5,
12
+ };
13
+ function clamp(n, lo, hi) {
14
+ return Math.max(lo, Math.min(hi, n));
15
+ }
16
+ export function scoreFriction(signals) {
17
+ let score = 100;
18
+ const reasons = [];
19
+ for (const gate of signals.gates) {
20
+ if (!gate.detected)
21
+ continue;
22
+ const w = GATE_WEIGHTS[gate.kind] ?? 0;
23
+ score += w;
24
+ reasons.push(`${gateLabel(gate.kind)} (${w})`);
25
+ }
26
+ if (signals.externalPRMergeRate !== undefined && signals.externalPRMergeRate !== null) {
27
+ const delta = (signals.externalPRMergeRate - 0.5) * 40;
28
+ score += delta;
29
+ reasons.push(`external PR merge rate ${Math.round(signals.externalPRMergeRate * 100)}% (${delta >= 0 ? "+" : ""}${delta.toFixed(1)})`);
30
+ }
31
+ if (signals.medianFirstReviewHours !== undefined && signals.medianFirstReviewHours !== null) {
32
+ const penalty = -Math.min(signals.medianFirstReviewHours / 24, 14);
33
+ score += penalty;
34
+ reasons.push(`median first review ${signals.medianFirstReviewHours.toFixed(1)}h (${penalty.toFixed(1)})`);
35
+ }
36
+ if (signals.maintainerIssueResponseHours !== undefined && signals.maintainerIssueResponseHours !== null) {
37
+ const penalty = -Math.min(signals.maintainerIssueResponseHours / 48, 10);
38
+ score += penalty;
39
+ reasons.push(`maintainer issue response ${signals.maintainerIssueResponseHours.toFixed(1)}h (${penalty.toFixed(1)})`);
40
+ }
41
+ if (signals.openGoodFirstIssueCount !== undefined && signals.openGoodFirstIssueCount !== null) {
42
+ const bonus = Math.min(signals.openGoodFirstIssueCount, 20) * 0.5;
43
+ score += bonus;
44
+ if (bonus > 0)
45
+ reasons.push(`${signals.openGoodFirstIssueCount} open good-first-issues (+${bonus.toFixed(1)})`);
46
+ }
47
+ return {
48
+ owner: signals.owner,
49
+ repo: signals.repo,
50
+ score: Math.round(clamp(score, 0, 100)),
51
+ gates: signals.gates,
52
+ reasons,
53
+ signals,
54
+ };
55
+ }
56
+ //# sourceMappingURL=friction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friction.js","sourceRoot":"","sources":["../src/friction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CONTRIBUTING.md gate parser.
3
+ *
4
+ * Problem this solves: repos have unwritten rituals before an outside PR is
5
+ * even allowed, and they differ per repo. FastMCP requires assignment
6
+ * (PrefectHQ/fastmcp#4557). trigger.dev auto-closes PRs from unvouched
7
+ * authors. modelcontextprotocol/servers and
8
+ * openai/openai-agents-python are collaborators-only for PR creation.
9
+ * shadcn-labs/pdfcn asks contributors to comment before starting work:
10
+ * that is a soft claim-first ritual, satisfied by posting a comment, not a
11
+ * hard assignment gate. This parser extracts those gates from contributing
12
+ * text so the verdict engine can flag GATED instead of letting you code
13
+ * first and get rejected, while soft gates like claim_first only add a note.
14
+ */
15
+ import type { Gate, GateKind } from "./types.js";
16
+ export interface ParseOptions {
17
+ /** Repo-level policy passed in when detectable (e.g. from repo metadata). */
18
+ collaboratorsOnlyPolicy?: boolean;
19
+ }
20
+ /** One policy file's raw text: CONTRIBUTING variants, AGENTS.md, CLAUDE.md. */
21
+ export interface PolicySource {
22
+ file: string;
23
+ text: string;
24
+ }
25
+ /**
26
+ * Parse several policy files at once. Binding rules increasingly live in
27
+ * house-rules files that CONTRIBUTING only links to (reticle keeps its
28
+ * claim-first, DCO, .changes and `pnpm verify` rules in AGENTS.md), so the
29
+ * same gate rules run over all of them. Sources are merged without
30
+ * duplicating kinds: CONTRIBUTING variants win ties, and each detected gate
31
+ * records the file its evidence came from.
32
+ */
33
+ export declare function parseGatesFromSources(sources: PolicySource[]): Gate[];
34
+ /**
35
+ * Parse contributing text into a gate list. Always returns all nine gates
36
+ * with detected true/false, plus an evidence snippet when detected.
37
+ */
38
+ export declare function parseGates(text: string | undefined, options?: ParseOptions): Gate[];
39
+ /**
40
+ * Parse a gatefile contribution-gates.json document into Greenlight gates.
41
+ * gatefile (the companion tool) extracts a repo's gates once and commits
42
+ * them as JSON; a repo that carries that file has already answered the gate
43
+ * question, so its verdicts should use the committed answer instead of
44
+ * re-deriving it from prose. Returns only the detected gates, each with its
45
+ * evidence prefixed so a verdict can say where the gate came from. Unknown
46
+ * kinds are skipped; malformed JSON throws.
47
+ */
48
+ export declare function parseGatefileJson(text: string): Gate[];
49
+ export declare function hardGates(gates: Gate[]): Gate[];
50
+ /** Human-friendly name for a gate kind. */
51
+ export declare function gateLabel(kind: GateKind): string;
52
+ /** One workflow file from .github/workflows: name plus raw text when fetchable. */
53
+ export interface WorkflowFile {
54
+ name: string;
55
+ content?: string;
56
+ }
57
+ /**
58
+ * Scan workflow files for auto-close automation aimed at outside PRs.
59
+ * Field case 2026-10-15: activepieces runs a workflow literally named
60
+ * "Close external PRs" that closed two finished outside PRs within about
61
+ * a minute. Returns a single external_prs_closed gate (detected-only);
62
+ * name match wins, but a content match catches innocuously named files
63
+ * whose steps close PRs from non-members.
64
+ */
65
+ export declare function detectWorkflowGates(files: WorkflowFile[]): Gate[];
package/dist/gates.js ADDED
@@ -0,0 +1,256 @@
1
+ const RULES = [
2
+ {
3
+ kind: "assignment_required",
4
+ regexes: [
5
+ /ask (to be|for) (assigned|assignment)/i,
6
+ /request (an )?assignment/i,
7
+ /must (also )?be assigned/i,
8
+ /assign.*before (submitting|opening|a PR)/i,
9
+ /please (wait|hold) (for|until).*assign/i,
10
+ /\bgood first issue\b.{0,40}\bassign/i,
11
+ ],
12
+ },
13
+ {
14
+ // Soft ritual: comment (or claim) on the issue before starting work.
15
+ // Satisfied by posting a comment; never a hard block.
16
+ kind: "claim_first",
17
+ regexes: [
18
+ /comment.{0,60}before (starting|working|opening)/i,
19
+ /claim.*before (you )?start/i,
20
+ /comment.{0,60}before you (start|code|submit)/i,
21
+ ],
22
+ },
23
+ {
24
+ kind: "vouch_required",
25
+ regexes: [/\bvouch/i, /vouched (author|contributor)/i],
26
+ },
27
+ {
28
+ kind: "cla_required",
29
+ regexes: [
30
+ /contributor license agreement/i,
31
+ /\bcla\b.{0,20}(sign|require)/i,
32
+ /sign.{0,20}\bcla\b/i,
33
+ ],
34
+ },
35
+ {
36
+ kind: "dco_required",
37
+ regexes: [
38
+ /\bdco\b/i,
39
+ /developer certificate of origin/i,
40
+ /sign-?off.*commit/i,
41
+ /signed-off-by/i,
42
+ ],
43
+ },
44
+ {
45
+ kind: "discussion_first",
46
+ regexes: [
47
+ /discuss.*before (opening|submitting|a PR)/i,
48
+ /open an issue before/i,
49
+ /proposal.*(first|before)/i,
50
+ /rfc.*(first|before)/i,
51
+ /start (a |with a )?discussion/i,
52
+ ],
53
+ },
54
+ {
55
+ kind: "collaborators_only",
56
+ regexes: [
57
+ /collaborators only/i,
58
+ /collaborators-only/i,
59
+ /only (repo )?collaborators/i,
60
+ /external (prs|pull requests) (are not|will not be) accepted/i,
61
+ // Maintainer-only PR shops (field cases 2026-10-15): the MCP inspector
62
+ // and servers repos take issues and prompts, not outside PRs, and say
63
+ // so in workflow language rather than gate language — "Pull requests
64
+ // against this repo are opened by the repo maintainers only" and
65
+ // "We accept issues, not pull requests". Both scored GO with "no
66
+ // blocking gates" until these phrasings were added. The optional
67
+ // asterisks tolerate the Markdown bold both repos wrap the words in.
68
+ /pull requests? (against|in|on) this (repo|repository) are opened by (the )?(\*\*)?(repo |repository )?maintainers(\*\*)? only/i,
69
+ /accept issues, not pull requests?/i,
70
+ ],
71
+ },
72
+ {
73
+ // Field case 2026-10-15: activepieces paused unsolicited outside PRs
74
+ // and auto-closed two finished PRs within about a minute. The policy
75
+ // sits in CONTRIBUTING in plain prose ("We've temporarily paused
76
+ // unsolicited pull requests from outside the core team"), blamed on
77
+ // AI-generated PR volume. This is a hard block: an outside PR will
78
+ // not survive even if the code is perfect.
79
+ kind: "unsolicited_prs_paused",
80
+ regexes: [
81
+ /paused.{0,60}(unsolicited|solicited|external)? ?(pull requests?|prs?)/i,
82
+ /not accepting (external )?(contributions|pull requests?)/i,
83
+ /unsolicited pull requests?/i,
84
+ ],
85
+ },
86
+ {
87
+ // The automation end of the same policy: prose that says outside PRs
88
+ // get closed automatically (trigger.dev's auto-close is caught by the
89
+ // vouch rule; this is the generic version for every other repo).
90
+ kind: "external_prs_closed",
91
+ regexes: [
92
+ /automatically.{0,60}(external|outside).{0,40}(pull requests?|prs?)/i,
93
+ /auto-?clos\w*.{0,80}(external|outside).{0,40}(pull requests?|prs?)/i,
94
+ /(external|outside).{0,40}(pull requests?|prs?).{0,60}(auto-?clos|will be closed)/i,
95
+ ],
96
+ },
97
+ ];
98
+ /** Cut a short evidence snippet around the first regex match. */
99
+ function snippet(text, regex) {
100
+ const match = regex.exec(text);
101
+ if (!match)
102
+ return "";
103
+ const idx = Math.max(0, match.index - 60);
104
+ const raw = text.slice(idx, idx + 160).replace(/\s+/g, " ").trim();
105
+ return raw.length > 140 ? raw.slice(0, 140) + "..." : raw;
106
+ }
107
+ /**
108
+ * Parse several policy files at once. Binding rules increasingly live in
109
+ * house-rules files that CONTRIBUTING only links to (reticle keeps its
110
+ * claim-first, DCO, .changes and `pnpm verify` rules in AGENTS.md), so the
111
+ * same gate rules run over all of them. Sources are merged without
112
+ * duplicating kinds: CONTRIBUTING variants win ties, and each detected gate
113
+ * records the file its evidence came from.
114
+ */
115
+ export function parseGatesFromSources(sources) {
116
+ const contributingFirst = (f) => (f.toLowerCase().includes("contributing") ? 0 : 1);
117
+ const ordered = [...sources].sort((a, b) => contributingFirst(a.file) - contributingFirst(b.file));
118
+ const merged = RULES.map((r) => ({
119
+ kind: r.kind,
120
+ detected: false,
121
+ evidence: "",
122
+ source: undefined,
123
+ }));
124
+ for (const src of ordered) {
125
+ const gates = parseGates(src.text);
126
+ for (const g of gates) {
127
+ if (!g.detected)
128
+ continue;
129
+ const m = merged.find((x) => x.kind === g.kind);
130
+ if (m && !m.detected) {
131
+ m.detected = true;
132
+ m.evidence = g.evidence;
133
+ m.source = src.file;
134
+ }
135
+ }
136
+ }
137
+ return merged;
138
+ }
139
+ /**
140
+ * Parse contributing text into a gate list. Always returns all nine gates
141
+ * with detected true/false, plus an evidence snippet when detected.
142
+ */
143
+ export function parseGates(text, options = {}) {
144
+ const body = text ?? "";
145
+ return RULES.map((rule) => {
146
+ const found = rule.regexes.find((re) => re.test(body));
147
+ if (rule.kind === "collaborators_only" && options.collaboratorsOnlyPolicy) {
148
+ return {
149
+ kind: rule.kind,
150
+ detected: true,
151
+ evidence: "repo policy: PR creation restricted to collaborators",
152
+ };
153
+ }
154
+ return {
155
+ kind: rule.kind,
156
+ detected: Boolean(found),
157
+ evidence: found ? snippet(body, found) : "",
158
+ };
159
+ });
160
+ }
161
+ /**
162
+ * gatefile's contribution-gates.json kinds mapped onto Greenlight kinds.
163
+ * The two tools share a taxonomy with a few aliases: gatefile's
164
+ * discuss_first and issue_first are Greenlight's discussion_first, and its
165
+ * no_direct_prs is the hard block Greenlight calls collaborators_only.
166
+ */
167
+ const GATEFILE_KIND_MAP = {
168
+ claim_first: "claim_first",
169
+ assignment_required: "assignment_required",
170
+ vouch_required: "vouch_required",
171
+ collaborators_only: "collaborators_only",
172
+ dco_required: "dco_required",
173
+ cla_required: "cla_required",
174
+ discuss_first: "discussion_first",
175
+ issue_first: "discussion_first",
176
+ no_direct_prs: "collaborators_only",
177
+ };
178
+ /**
179
+ * Parse a gatefile contribution-gates.json document into Greenlight gates.
180
+ * gatefile (the companion tool) extracts a repo's gates once and commits
181
+ * them as JSON; a repo that carries that file has already answered the gate
182
+ * question, so its verdicts should use the committed answer instead of
183
+ * re-deriving it from prose. Returns only the detected gates, each with its
184
+ * evidence prefixed so a verdict can say where the gate came from. Unknown
185
+ * kinds are skipped; malformed JSON throws.
186
+ */
187
+ export function parseGatefileJson(text) {
188
+ const doc = JSON.parse(text);
189
+ if (!doc || !Array.isArray(doc.gates)) {
190
+ throw new Error("not a contribution-gates.json document (no gates array)");
191
+ }
192
+ const out = [];
193
+ for (const g of doc.gates) {
194
+ const kind = g.kind ? GATEFILE_KIND_MAP[g.kind] : undefined;
195
+ if (!kind || out.some((x) => x.kind === kind))
196
+ continue;
197
+ out.push({
198
+ kind,
199
+ detected: true,
200
+ evidence: `gatefile: ${g.evidence ?? g.kind}`,
201
+ source: g.source ?? "contribution-gates.json",
202
+ });
203
+ }
204
+ return out;
205
+ }
206
+ /** Gates that block an outside PR before it can even be opened. */
207
+ const HARD_GATES = ["assignment_required", "vouch_required", "collaborators_only", "unsolicited_prs_paused", "external_prs_closed"];
208
+ export function hardGates(gates) {
209
+ return gates.filter((g) => g.detected && HARD_GATES.includes(g.kind));
210
+ }
211
+ /** Human-friendly name for a gate kind. */
212
+ export function gateLabel(kind) {
213
+ switch (kind) {
214
+ case "assignment_required": return "assignment required before PR";
215
+ case "claim_first": return "comment first before starting";
216
+ case "vouch_required": return "vouch required before PR";
217
+ case "cla_required": return "CLA signature required";
218
+ case "dco_required": return "DCO sign-off required";
219
+ case "discussion_first": return "discussion first";
220
+ case "collaborators_only": return "PR creation restricted to collaborators";
221
+ case "unsolicited_prs_paused": return "unsolicited PRs paused";
222
+ case "external_prs_closed": return "external PRs closed by automation";
223
+ }
224
+ }
225
+ /**
226
+ * Scan workflow files for auto-close automation aimed at outside PRs.
227
+ * Field case 2026-10-15: activepieces runs a workflow literally named
228
+ * "Close external PRs" that closed two finished outside PRs within about
229
+ * a minute. Returns a single external_prs_closed gate (detected-only);
230
+ * name match wins, but a content match catches innocuously named files
231
+ * whose steps close PRs from non-members.
232
+ */
233
+ export function detectWorkflowGates(files) {
234
+ const NAME_RE = /clos(e|ing|ure).*(external|outside)|(external|outside).*clos(e|ing|ure)/i;
235
+ // Order-agnostic: "close ... outside contributors' PRs" and "close PRs
236
+ // from outside contributors" both count. "prs?\b" catches "PR", "prs",
237
+ // and the standalone in "gh pr close" without eating "press".
238
+ const PR_RE = "(pull.?requests?|prs?\\b)";
239
+ const CONTENT_RE = new RegExp(`(close)[\\s\\S]{0,200}((external|outside)[\\s\\S]{0,80}${PR_RE}|${PR_RE}[\\s\\S]{0,80}(external|outside))`, "i");
240
+ const hit = files.find((f) => NAME_RE.test(f.name)) ??
241
+ files.find((f) => f.content !== undefined && CONTENT_RE.test(f.content));
242
+ if (!hit)
243
+ return [];
244
+ const byName = NAME_RE.test(hit.name);
245
+ return [
246
+ {
247
+ kind: "external_prs_closed",
248
+ detected: true,
249
+ evidence: byName
250
+ ? `workflow file: ${hit.name}`
251
+ : `workflow file: ${hit.name} (closes external PRs: "${snippet(hit.content ?? "", CONTENT_RE)}")`,
252
+ source: `.github/workflows/${hit.name}`,
253
+ },
254
+ ];
255
+ }
256
+ //# sourceMappingURL=gates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAqBA,MAAM,KAAK,GAAe;IACxB;QACE,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE;YACP,wCAAwC;YACxC,2BAA2B;YAC3B,2BAA2B;YAC3B,2CAA2C;YAC3C,yCAAyC;YACzC,sCAAsC;SACvC;KACF;IACD;QACE,qEAAqE;QACrE,sDAAsD;QACtD,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE;YACP,kDAAkD;YAClD,6BAA6B;YAC7B,+CAA+C;SAChD;KACF;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,CAAC,UAAU,EAAE,+BAA+B,CAAC;KACvD;IACD;QACE,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE;YACP,gCAAgC;YAChC,+BAA+B;YAC/B,qBAAqB;SACtB;KACF;IACD;QACE,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE;YACP,UAAU;YACV,kCAAkC;YAClC,oBAAoB;YACpB,gBAAgB;SACjB;KACF;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE;YACP,4CAA4C;YAC5C,uBAAuB;YACvB,2BAA2B;YAC3B,sBAAsB;YACtB,gCAAgC;SACjC;KACF;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE;YACP,qBAAqB;YACrB,qBAAqB;YACrB,6BAA6B;YAC7B,8DAA8D;YAC9D,uEAAuE;YACvE,sEAAsE;YACtE,qEAAqE;YACrE,iEAAiE;YACjE,iEAAiE;YACjE,iEAAiE;YACjE,qEAAqE;YACrE,gIAAgI;YAChI,oCAAoC;SACrC;KACF;IACD;QACE,qEAAqE;QACrE,qEAAqE;QACrE,iEAAiE;QACjE,oEAAoE;QACpE,mEAAmE;QACnE,2CAA2C;QAC3C,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE;YACP,wEAAwE;YACxE,2DAA2D;YAC3D,6BAA6B;SAC9B;KACF;IACD;QACE,qEAAqE;QACrE,sEAAsE;QACtE,iEAAiE;QACjE,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE;YACP,qEAAqE;YACrE,qEAAqE;YACrE,mFAAmF;SACpF;KACF;CACF,CAAC;AAEF,iEAAiE;AACjE,SAAS,OAAO,CAAC,IAAY,EAAE,KAAa;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnE,OAAO,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AAC5D,CAAC;AAaD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAuB;IAC3D,MAAM,iBAAiB,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACnG,MAAM,MAAM,GAAW,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC,CAAC;IACJ,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,CAAC,QAAQ;gBAAE,SAAS;YAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;gBACrB,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAClB,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;gBACxB,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAwB,EAAE,UAAwB,EAAE;IAC7E,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IACxB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;YAC1E,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,sDAAsD;aACjE,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC;YACxB,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;SAC5C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,iBAAiB,GAA6B;IAClD,WAAW,EAAE,aAAa;IAC1B,mBAAmB,EAAE,qBAAqB;IAC1C,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;IACxC,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,kBAAkB;IACjC,WAAW,EAAE,kBAAkB;IAC/B,aAAa,EAAE,oBAAoB;CACpC,CAAC;AAQF;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA+B,CAAC;IAC3D,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;YAAE,SAAS;QACxD,GAAG,CAAC,IAAI,CAAC;YACP,IAAI;YACJ,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,aAAa,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE;YAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,yBAAyB;SAC9C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,GAAe,CAAC,qBAAqB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,qBAAqB,CAAC,CAAC;AAEhJ,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,qBAAqB,CAAC,CAAC,OAAO,+BAA+B,CAAC;QACnE,KAAK,aAAa,CAAC,CAAC,OAAO,+BAA+B,CAAC;QAC3D,KAAK,gBAAgB,CAAC,CAAC,OAAO,0BAA0B,CAAC;QACzD,KAAK,cAAc,CAAC,CAAC,OAAO,wBAAwB,CAAC;QACrD,KAAK,cAAc,CAAC,CAAC,OAAO,uBAAuB,CAAC;QACpD,KAAK,kBAAkB,CAAC,CAAC,OAAO,kBAAkB,CAAC;QACnD,KAAK,oBAAoB,CAAC,CAAC,OAAO,yCAAyC,CAAC;QAC5E,KAAK,wBAAwB,CAAC,CAAC,OAAO,wBAAwB,CAAC;QAC/D,KAAK,qBAAqB,CAAC,CAAC,OAAO,mCAAmC,CAAC;IACzE,CAAC;AACH,CAAC;AAQD;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAqB;IACvD,MAAM,OAAO,GAAG,0EAA0E,CAAC;IAC3F,uEAAuE;IACvE,uEAAuE;IACvE,8DAA8D;IAC9D,MAAM,KAAK,GAAG,2BAA2B,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,MAAM,CAC3B,0DAA0D,KAAK,IAAI,KAAK,mCAAmC,EAC3G,GAAG,CACJ,CAAC;IACF,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3E,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO;QACL;YACE,IAAI,EAAE,qBAAqB;YAC3B,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,MAAM;gBACd,CAAC,CAAC,kBAAkB,GAAG,CAAC,IAAI,EAAE;gBAC9B,CAAC,CAAC,kBAAkB,GAAG,CAAC,IAAI,2BAA2B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,UAAU,CAAC,IAAI;YACnG,MAAM,EAAE,qBAAqB,GAAG,CAAC,IAAI,EAAE;SACxC;KACF,CAAC;AACJ,CAAC"}