@reneturcios/greenlight 0.0.0-stage → 0.3.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rene Turcios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,214 @@
1
- # Temporary Holding Version
1
+ # Greenlight
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Greenlight checks a GitHub issue before you spend hours on it, and tells you in plain words whether your pull request is likely to land.
4
+
5
+ ## The problem
6
+
7
+ You find an issue that looks perfect. You code all evening. Then one of these happens:
8
+
9
+ * Someone already claimed the work in a comment. The issue still says "unassigned", but three volunteers wrote "I'll take this" weeks ago and you never saw it.
10
+ * The repo wanted you to ask first. Your pull request gets closed because the maintainers require contributors to request assignment or get a vouch (a recommendation from someone they know) before anyone may open a PR, and nobody told you.
11
+ * Your commits get rejected on a technicality. The repo requires a "Signed-off-by" line on every commit (called DCO sign-off, short for Developer Certificate of Origin) or an entry in its changelog folder, and you find out after you push.
12
+
13
+ These are all contribution rituals: unwritten rules a repo expects before an outside pull request counts. Every repo has different ones, and they hide in different places, from CONTRIBUTING files to AGENTS.md house-rules files (plain-language instruction files that tell people and coding agents how to work in that repo). We hit all of these by hand while vetting real issues, so Greenlight automates the checking.
14
+
15
+ ## What Greenlight does
16
+
17
+ One engine, four plain steps:
18
+
19
+ 1. **Check before you claim.** Point it at any issue URL. It reads the issue, its comments, linked and mentioned pull requests, and the repo's rules, then answers: GO (yours to take), RISKY (possible, but know this first), TAKEN (someone is on it), GATED (a repo rule blocks you), or STALE (abandoned). It also spots partially-claimed work and issues where half the fix already shipped, so you scope to what remains.
20
+ 2. **Claim the right way.** It drafts the claim comment in the form the repo's rules actually want: a plain "I'll work on this" where that is enough, an assignment request where the repo requires one, or a refusal where a vouch is needed (no tool can vouch for you). It never posts without your say-so.
21
+ 3. **Prep your workspace with the repo's own rules.** It sets up your fork and branch, then writes a checklist that includes what it learned from the repo itself: the house-rules files, the verify command they insist on, whether the repo wants a changelog entry, and how its commits are styled.
22
+ 4. **Open the PR only when the basics pass.** It checks the branch is really ahead of the repo, and on repos that require DCO sign-off it verifies every commit carries the Signed-off-by line before it opens the pull request.
23
+
24
+ ## Use it when
25
+
26
+ * You found an issue you like and are about to spend real time on it.
27
+ * You want to know if an issue is really still yours to finish, or whether someone quietly claimed part of it in a comment.
28
+ * A repo's rules are unclear and you want to see them summarized before you start.
29
+ * You claimed something a while ago and want to know if that claim still counts.
30
+ * You are picking between several repos or issues and want the most winnable one first.
31
+
32
+ ## What it does, technically
33
+
34
+ One engine, four surfaces:
35
+
36
+ 1. **Availability verdicts** for an issue: `GO`, `RISKY`, `TAKEN`, `GATED`, `STALE`. Signals: assignees, linked open/closed/merged PRs (timeline + search + comment references), claim-comment heuristics including scoped claims (phrases like "please assign", "working on this", "taking the remaining fallback ask"), partially-addressed detection (merged PRs that reference the issue, maintainer comments like "half addressed ... leaving this open"), issue age and last activity.
37
+ 2. **Repo friction scores** (0-100, 100 = easiest): contribution gates parsed from CONTRIBUTING variants plus house-rules files (`AGENTS.md`, `CLAUDE.md`), with each gate recording which file its evidence came from (assignment-first, vouch, CLA, DCO, discussion-first, comment-first, collaborators-only), plus outcome signals where computable.
38
+ 3. **Scan mode**: rank a repo's open good-first-issue / help-wanted issues by verdict so a contributor (or agent) picks a winnable one first. Falls back to recent open unassigned issues when a repo has no labeled starter issues.
39
+ 4. **Contribution automation**: `claim`, `prep`, and `pr` commands (plus matching MCP tools) that run the pipeline after the verdict. All writes are dry-run by default and need a token plus an explicit go flag.
40
+
41
+ A validation corpus of ten real cases from the campaign (`test/fixtures/cases/`, run by `test/cases.test.ts`) pins every verdict rule to something that actually happened: a taken issue, a weak PR mention, claim-first gates, assignment and vouch gates, a scoped claim, a half-shipped issue, a clean GO, and a stale claim.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npx @reneturcios/greenlight check <issue-url>
47
+ ```
48
+
49
+ Or from source:
50
+
51
+ ```bash
52
+ npm install
53
+ npm run build
54
+ ```
55
+
56
+ Set `GITHUB_TOKEN` (a classic personal access token, no scopes needed for public repos) to raise the GitHub API rate limit from 60 to 5000 requests per hour. Greenlight caches API responses under `~/.cache/greenlight`.
57
+
58
+ Run the MCP server with `npx @reneturcios/greenlight mcp` (stdio), or point your agent at the `greenlight` command with the single argument `mcp`.
59
+
60
+ ## The toolkit
61
+
62
+ Greenlight is the front door of a four-tool lifecycle for open-source contribution work, all published under the same npm scope and cross-wired:
63
+
64
+ 1. **gatefile** (`npx @reneturcios/gatefile extract owner/repo`) extracts a repo's contribution gates into a committed `contribution-gates.json`. Greenlight consumes that file directly: `greenlight check <issue-url> --gates-file contribution-gates.json`, or automatically when the file sits in the working directory, so gates are answered once and reused instead of re-derived from prose.
65
+ 2. **greenlight** verdicts, scans, claims, prep, and PR opening (this tool).
66
+ 3. **claimcheck** (`npx @reneturcios/claimcheck claims owner/repo`) watches claimed issues for stale claims and finished fixes parked on forks, the two ways claimed work silently dies.
67
+ 4. **prwatch** (`npx @reneturcios/prwatch attention --user you`) triages your open PRs by what needs you right now: review threads, failing checks, conflicts, reviewer silence.
68
+
69
+ Each tool is a CLI and an MCP server (`<tool> mcp`).
70
+
71
+ ## CLI
72
+
73
+ ```bash
74
+ # Verdict for one issue
75
+ npx @reneturcios/greenlight check https://github.com/batrapulkit/squidbrake/issues/74
76
+
77
+ # [GO] batrapulkit/squidbrake#74
78
+ # Taint: tell the approver when an agent edits its own setup right after reading outside content
79
+ # - open, unassigned, no linked PRs, no claim signals, no blocking gates
80
+
81
+ # Rank a repo's starter issues, GO first (falls back to recent open
82
+ # unassigned issues when the repo does not use those labels; --no-fallback to opt out)
83
+ npx @reneturcios/greenlight scan PrefectHQ/fastmcp
84
+
85
+ # Friction score for a repo
86
+ npx @reneturcios/greenlight friction triggerdotdev/trigger.dev
87
+
88
+ # JSON output for scripting
89
+ npx @reneturcios/greenlight check <issue-url> --json
90
+ ```
91
+
92
+ Real examples from our vetting:
93
+
94
+ * `batrapulkit/squidbrake#74` scores **GO**: 0 comments, labeled help wanted, no claims, no gates.
95
+ * `shadcn-labs/pdfcn#58` scores **RISKY** (claimed flag): a volunteer asked for assignment the same day we vetted it.
96
+ * `langchain-ai/langchain#41185` scores **TAKEN** (claim race): 3 strong claimants while still "unassigned".
97
+ * `PrefectHQ/fastmcp#5718` scores **GATED**: FastMCP requires assignment or a "prs welcome" label before a PR.
98
+ * `triggerdotdev/trigger.dev#5016` scores **GATED**: unvouched-author PRs are auto-closed, a vouch request goes first.
99
+
100
+ ## How scoring works
101
+
102
+ The verdict engine is deterministic (no LLM) and checks, in order:
103
+
104
+ 1. Closed issue: **TAKEN** (never landable).
105
+ 2. Any assignee: **TAKEN**.
106
+ 3. An open linked PR that **closes** the issue: **TAKEN**. A PR that merely mentions the issue number (like pdfcn PR #91, which closes #70 but lists sibling issues in its body) only adds a `weak-pr-mention` flag and downgrades an otherwise-GO verdict to **RISKY**.
107
+ 4. Two or more strong claimants: **TAKEN** (claim race).
108
+ 5. One strong fresh whole-issue claim (under 45 days): **RISKY** with the `claimed` flag. A fresh claim scoped to part of the issue ("taking the remaining fallback ask"): **RISKY** with `claimed` + `partial-claim` flags and a reason naming the claimer and the scope, never TAKEN.
109
+ 6. One strong claim older than 45 days with no closing PR: **RISKY** with the `stale_claim` flag. The claimant probably walked away, but nobody knows for sure. Stale scoped claims get the same treatment.
110
+ 7. A hard gate (assignment, vouch, collaborators-only) with nobody assigned: **GATED**.
111
+ 8. No activity for 180+ days: **STALE**.
112
+ 9. A soft claim-first gate (the repo asks contributors to comment before starting, like pdfcn): never **GATED**; it caps an otherwise-GO verdict at **RISKY** with a note, the same downgrade shape as a weak PR mention.
113
+ 10. Otherwise: **GO**.
114
+ 11. An open PR referenced in a non-author comment whose title/body mentions the issue number: downgrades an otherwise-GO verdict to **RISKY** with the `comment-mentioned-pr` flag ("may already cover part of this issue"). Neither can produce **TAKEN**; only closing PRs do that.
115
+ 12. **Partially-addressed**: merged PRs that reference the issue number (comment-mentioned or linked), or maintainer comments signaling part of the work already shipped ("Half addressed by #905, leaving this open for half B", "Shipped in v3.1.0" next to the issue reference). A plain **GO** hides the scoping work this needs, so the flag `partially-addressed` is added, an otherwise-GO verdict is capped at **RISKY** with a reason like "part of this issue already shipped (PR #905, merged); scope your work to what remains". Never **TAKEN**.
116
+
117
+ Claim detection scans comments for explicit claim phrases, in-progress signals ("fix ready", "PR incoming", "opened a PR"), and soft offers ("happy to fix"). Bot authors are ignored, as are issue reporters unless they announce a ready fix. Scoped claims ("taking the remaining fallback ask", "claiming this half") are recorded as partial: a fresh partial claim downgrades an otherwise-GO verdict to RISKY with a reason naming the claimer and the scope, never TAKEN. PR references (#NNN) in non-author comments are cross-checked against the PR API: an open referenced PR that mentions the issue downgrades GO to RISKY with the `comment-mentioned-pr` flag, and a merged one feeds rule 12 above.
118
+
119
+ Friction score starts at 100 and subtracts gate weights (assignment -25, vouch -35, collaborators-only -50, discussion-first -10, claim-first -5, CLA/DCO -5 each), adjusts for external PR merge rate and review/response times when computable, and adds up to +10 for open good-first-issues. Unknown signals stay null and are skipped, never guessed. See `src/friction.ts` for the exact formula.
120
+
121
+ ## MCP server
122
+
123
+ The same engine is exposed over stdio for coding agents (Claude, Cursor, Cline). The wedge: agents waste the most tokens fixing already-taken issues. Have the agent pre-flight the issue before writing code.
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "greenlight": {
129
+ "command": "npx",
130
+ "args": ["-y", "@reneturcios/greenlight", "mcp"],
131
+ "env": { "GITHUB_TOKEN": "your-token" }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ Tools:
138
+
139
+ * `check_issue` - verdict for one issue URL
140
+ * `scan_repo` - rank a repo's open issues, GO first (label-tagged starter issues, falling back to recent open unassigned issues)
141
+ * `repo_friction` - friction score for a repo
142
+ * `claim_issue` - verdict-gated claim comment (dry run unless `execute: true`)
143
+ * `prep_issue` - fork and branch check plus a local PREP.md checklist with the repo's learned conventions (dry run unless `execute: true`)
144
+ * `open_pr` - DCO-aware PR opening from a fork branch (dry run unless `execute: true`)
145
+
146
+ Run it locally: `npm run dev` (MCP entry is `src/index.ts`; CLI is `src/cli.ts`).
147
+
148
+ ## Automation
149
+
150
+ Greenlight 0.2.0 automates the contribution pipeline its author ran by hand
151
+ for four real PRs (shadcn-labs/pdfcn #92-#95): verdict, claim comment, fork
152
+ branch, and opening the PR, with DCO awareness throughout.
153
+
154
+ ```bash
155
+ # 1. Claim the issue: verdict first, comment shaped by the repo's gates
156
+ greenlight claim https://github.com/shadcn-labs/pdfcn/issues/65
157
+
158
+ # 2. Prepare: fork/branch check (learning the repo's branch convention) plus a PREP.md checklist
159
+ greenlight prep https://github.com/shadcn-labs/pdfcn/issues/65 --dir /tmp/prep65
160
+
161
+ # 3. Open the PR from your fork branch (verifies DCO sign-off on DCO repos)
162
+ greenlight pr https://github.com/shadcn-labs/pdfcn/issues/65 --branch feat/quarterly-report-block
163
+ ```
164
+
165
+ Safety defaults, and this is a feature:
166
+
167
+ * **Dry run by default.** All three commands print exactly what they would
168
+ post or create and exit. Nothing is posted or created without `--yes`
169
+ (CLI) or `execute: true` (MCP).
170
+ * **Writes need a token.** Read ops work with no token at all. Write ops
171
+ require `GITHUB_TOKEN` (or `GH_TOKEN`); without one they print a clear
172
+ error naming the env var and exit non-zero.
173
+ * **Verdict-gated.** `claim` refuses TAKEN or STALE verdicts unless
174
+ `--force`, and refuses vouch-gated repos outright (a vouch cannot be
175
+ automated). Assignment-gated repos get an assignment request instead of
176
+ an intent comment; claim-first repos (comment before starting) get the
177
+ standard intent comment, which satisfies the ritual.
178
+ * **Repo-aware branch names.** `prep` reads the head branches of recent
179
+ closed PRs and follows a dominant convention (pdfcn's `feat/<slug>-block`)
180
+ while still showing the `feat/<slug>-<number>` default.
181
+ * **DCO-aware.** On DCO repos, `pr` verifies every commit on the branch
182
+ carries a `Signed-off-by:` line and refuses, listing the unsigned
183
+ commits, when one is missing.
184
+ * **Repo-aware prep.** `prep` also learns the repo's contribution rituals
185
+ for the PREP.md checklist: which house-rules files exist, the verify
186
+ command they insist on (like `pnpm verify`), whether the repo assembles
187
+ its changelog from a `.changes/` directory, which root package.json
188
+ scripts exist, and whether recent commits follow Conventional Commits.
189
+ Binding rules increasingly live in `AGENTS.md` rather than CONTRIBUTING,
190
+ and prep reads those too.
191
+
192
+ ## The friction index
193
+
194
+ `data/friction-index.json` is a checked-in, growing dataset of scored repos. It starts with the MCP/AI ecosystem we hand-vetted on 2026-10-13 (FastMCP, trigger.dev, langchain, deepagents, openclaw, squidbrake, reticle, pdfcn, chatflow-agent, and the three MCP SDK repos). Regenerate it with `npm run index`.
195
+
196
+ Entries labeled `hand-verified 2026-10-13` come from reading the repos' actual policies and issue trackers by hand. Entries labeled `live` were parsed from live house-rules files (CONTRIBUTING variants, AGENTS.md, CLAUDE.md). Each gate records which file its evidence came from. Computed outcome fields (merge rates, review times) stay `null` until we can compute them honestly, never fabricated.
197
+
198
+ ## Development
199
+
200
+ ```bash
201
+ npm test # tsx --test, includes fixture tests that reproduce our real verdicts
202
+ npm run build
203
+ ```
204
+
205
+ ## Roadmap (Later)
206
+
207
+ * Browser extension that scores issues inline on github.com
208
+ * Hosted dashboard of the friction index, refreshed on a schedule
209
+ * Maintainer GitHub Action that auto-labels claim comments
210
+ * Parked-fix detection and auto-nudges: scan forks for branches referencing open issues and nudge when the gate is satisfied
211
+
212
+ ## License
213
+
214
+ MIT. See LICENSE.
@@ -0,0 +1,184 @@
1
+ import type { IssueSnapshot, VerdictResult, Gate, RepoConventions } from "./types.js";
2
+ /** Thrown when an automation command refuses to proceed. CLI exits 1 on this. */
3
+ export declare class AutomationRefusal extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * The write-capable GitHub surface automation needs. Implementations log
8
+ * nothing on their own; callers record what they did from the results.
9
+ */
10
+ export interface GitHubClient {
11
+ getRepo(owner: string, repo: string): Promise<{
12
+ defaultBranch: string;
13
+ }>;
14
+ getViewer(): Promise<string>;
15
+ forkExists(viewer: string, repo: string): Promise<boolean>;
16
+ createFork(owner: string, repo: string): Promise<void>;
17
+ getBranchSha(viewer: string, repo: string, branch: string): Promise<string | undefined>;
18
+ createBranch(viewer: string, repo: string, branch: string, sha: string): Promise<void>;
19
+ compareCommits(owner: string, repo: string, base: string, headOwner: string, headBranch: string): Promise<{
20
+ aheadBy: number;
21
+ commits: Array<{
22
+ sha: string;
23
+ message: string;
24
+ }>;
25
+ }>;
26
+ /** Head branch names of recent closed PRs, used to learn the repo's branch convention. */
27
+ listClosedPullHeadRefs(owner: string, repo: string): Promise<string[]>;
28
+ /** Names of files and directories at the repo root (best-effort conventions detection). */
29
+ repoRootNames(owner: string, repo: string): Promise<string[]>;
30
+ /** Raw text of a repo-root file, or undefined when absent. */
31
+ fetchRootFile(owner: string, repo: string, path: string): Promise<string | undefined>;
32
+ /** First lines of the most recent commit messages on a branch. */
33
+ listRecentCommitMessages(owner: string, repo: string, branch: string, count: number): Promise<string[]>;
34
+ postIssueComment(owner: string, repo: string, number: number, body: string): Promise<string>;
35
+ createPullRequest(owner: string, repo: string, opts: {
36
+ title: string;
37
+ body: string;
38
+ head: string;
39
+ base: string;
40
+ }): Promise<string>;
41
+ }
42
+ /** Live REST implementation of GitHubClient. */
43
+ export declare function createRestClient(): GitHubClient;
44
+ /** Write commands need a token. Dry runs do not. */
45
+ export declare function requireWriteToken(): void;
46
+ /** Slugify an issue title: strip leading labels, lowercase, dashes, 40-char cap. */
47
+ export declare function titleSlug(title: string): string;
48
+ /**
49
+ * Branch name from an issue title: strip leading labels like "block:" or
50
+ * "feat:", slugify, cap at 40 chars, append the issue number.
51
+ * "block: Resume" -> "feat/resume-61"
52
+ */
53
+ export declare function branchSlugFor(title: string, number: number): string;
54
+ /** A branch-name convention learned from a repo's recent closed PRs. */
55
+ export interface BranchPattern {
56
+ /** Dominant prefix before the "/", e.g. "feat". */
57
+ prefix: string;
58
+ /** Dominant trailing token of the branch rest, e.g. "block", or null. */
59
+ suffix: string | null;
60
+ }
61
+ /**
62
+ * Learn a dominant branch naming pattern from recent closed PR head refs.
63
+ * Returns undefined when no prefix or suffix reaches the 3-branch
64
+ * threshold, so naming falls back to the default.
65
+ */
66
+ export declare function detectBranchPattern(refs: string[]): BranchPattern | undefined;
67
+ export interface BranchSuggestion {
68
+ /** The branch name to use. */
69
+ branch: string;
70
+ /** The default name, for comparison when a pattern was found. */
71
+ defaultName: string;
72
+ /** Human-readable note about the learned pattern, empty when none. */
73
+ note: string;
74
+ }
75
+ /**
76
+ * Suggest a branch name, learning the repo's convention from recent closed
77
+ * PR head refs. A dominant suffix token (e.g. "-block" from
78
+ * feat/business-letter-block) replaces the issue number; a dominant
79
+ * prefix style replaces "feat/". Falls back to feat/<slug>-<number>.
80
+ */
81
+ export declare function suggestBranchName(title: string, number: number, refs: string[]): BranchSuggestion;
82
+ /**
83
+ * Claim comment text shaped by the repo's gates. Assignment-gated repos get
84
+ * an assignment request and a promise not to start work early; every comment
85
+ * notes the comment-first convention; DCO repos get a sign-off commitment.
86
+ */
87
+ export declare function buildClaimComment(snapshot: IssueSnapshot, gates: Gate[], viewer?: string): string;
88
+ /** Required actions checklist rendered from detected gates. */
89
+ export declare function gateActions(gates: Gate[]): string[];
90
+ export interface ClaimOptions {
91
+ yes: boolean;
92
+ force: boolean;
93
+ }
94
+ export interface ClaimResult {
95
+ verdict: VerdictResult;
96
+ commentBody: string;
97
+ commentUrl: string | null;
98
+ executed: boolean;
99
+ viewer: string | undefined;
100
+ }
101
+ /**
102
+ * claim: verdict first, then post a claim comment shaped by the repo gates.
103
+ * Refuses on TAKEN or STALE verdicts unless forced, and refuses outright
104
+ * on vouch-gated repos (a vouch cannot be automated). Dry run by default.
105
+ */
106
+ export declare function claimIssue(client: GitHubClient, snapshot: IssueSnapshot, opts: ClaimOptions): Promise<ClaimResult>;
107
+ /** Pure renderer for the PREP.md checklist file. */
108
+ export declare function renderPrepDoc(args: {
109
+ snapshot: IssueSnapshot;
110
+ verdict: VerdictResult;
111
+ gates: Gate[];
112
+ branch: string;
113
+ branchDefault?: string;
114
+ branchNote?: string;
115
+ viewer: string;
116
+ issueUrl: string;
117
+ conventions?: RepoConventions;
118
+ }): string;
119
+ export interface PrepOptions {
120
+ dir?: string;
121
+ yes: boolean;
122
+ }
123
+ export interface PrepResult {
124
+ verdict: VerdictResult;
125
+ branch: string;
126
+ branchDefault: string;
127
+ branchPatternNote: string;
128
+ forkReady: boolean;
129
+ forkCreated: boolean;
130
+ branchReady: boolean;
131
+ branchCreated: boolean;
132
+ prepFile: string;
133
+ nextCommands: string[];
134
+ /** Repo rituals learned during prep (best-effort; undefined when nothing was detectable). */
135
+ conventions?: RepoConventions;
136
+ }
137
+ /**
138
+ * Extract backticked run commands quoted in house-rules text, e.g. the
139
+ * `pnpm verify` reticle's AGENTS.md insists on. Returns them deduplicated,
140
+ * in order of appearance, capped at five.
141
+ */
142
+ export declare function extractVerifyCommands(text: string): string[];
143
+ /**
144
+ * Learn the repo's contribution rituals for the PREP.md checklist:
145
+ * which house-rules files exist, the verify/test command they quote,
146
+ * whether the repo assembles its changelog from a .changes directory,
147
+ * which root package.json scripts exist, and whether recent commits
148
+ * follow Conventional Commits. Every probe is best-effort: a failing
149
+ * probe is skipped, never fatal.
150
+ */
151
+ export declare function gatherRepoConventions(client: GitHubClient, snapshot: IssueSnapshot): Promise<RepoConventions | undefined>;
152
+ /**
153
+ * prep: verdict, fork check (create with --yes), branch naming and creation,
154
+ * and a local PREP.md checklist. Never clones; reports the clone command.
155
+ * Writes the PREP.md file in both dry-run and execute modes.
156
+ */
157
+ export declare function prepIssue(client: GitHubClient, snapshot: IssueSnapshot, opts: PrepOptions): Promise<PrepResult>;
158
+ export interface PrOptions {
159
+ branch: string;
160
+ title?: string;
161
+ yes: boolean;
162
+ }
163
+ export interface PrResult {
164
+ verdict: VerdictResult;
165
+ branch: string;
166
+ head: string;
167
+ base: string;
168
+ title: string;
169
+ body: string;
170
+ checks: string[];
171
+ prUrl: string | null;
172
+ executed: boolean;
173
+ }
174
+ /**
175
+ * pr: preconditions, DCO verification, then open the PR. Preconditions:
176
+ * the branch must exist on the fork and be at least one commit ahead of
177
+ * upstream's default branch. When the repo requires DCO, every ahead
178
+ * commit must carry a Signed-off-by line; the first unsigned commit is a
179
+ * hard refusal (this exact failure mode burned real review time). Dry run
180
+ * prints the composed PR without posting it.
181
+ */
182
+ export declare function openPr(client: GitHubClient, snapshot: IssueSnapshot, opts: PrOptions): Promise<PrResult>;
183
+ /** PR body template: closes keyword, summary, and the checks at open time. */
184
+ export declare function composePrBody(snapshot: IssueSnapshot, result: VerdictResult, gates: Gate[]): string;