/** * Deterministic PR-for-issue resolution shared by `fix-setup` and * `verify-gather`. * * Both scripts previously resolved the PR with `gh pr list --search * "linked:"`, but `linked:` is **not** a valid GitHub search * qualifier (only `linked:pr` / `linked:issue` exist, and only for *issue* * searches). The qualifier was therefore ignored, `gh pr list` returned every * open PR, and `-q '.[0]...'` picked the newest one — not the PR that actually * references the issue. * * This resolver removes that guesswork. It resolves the PR in two tiers: * 1. GitHub's own linked-PR data (`gh issue view --json * closedByPullRequests`) — the authoritative closing-keyword / * Development-panel links. * 2. A closing-keyword-only body scan of open PRs (`Fixes|Closes|Resolves * [owner/repo]#N`) — bare `#N` prose mentions do NOT count. * Candidates from both tiers are merged and deduped by PR number (a Tier-1 * `linked` match beats a Tier-2 `body` match for the same number), scoped by * the caller's `openOnly` preference, and the single best match is chosen with * a stable tie-break (`linked` beats `body`; within a tier the lowest PR number * wins). Same repo state always yields the same PR, independent of PR creation * order. * * Nothing here logs to stdout — the single JSON object on stdout is owned by * each caller, and the loud "No PR found" failure is the caller's * responsibility (so the existing messages are preserved verbatim). */ export type PrMatchKind = 'linked' | 'body'; export interface ResolvedPr { number: number; headRefName: string; /** 'OPEN' | 'CLOSED' | 'MERGED' — empty string when gh didn't report it. */ state: string; matchKind: PrMatchKind; } type GhRunner = (args: string[], options?: { cwd?: string; }) => Promise<{ stdout: string; stderr: string; code: number; }>; export interface ResolveOptions { /** * `true` → only OPEN PRs are acceptable (the caller must check the branch * out into a worktree); nothing else resolves to `null`. * `false` → prefer OPEN but fall back to the best closed/merged match (the * caller only reads metadata/diff). */ openOnly: boolean; cwd?: string; /** Injected gh runner (defaults to {@link runGh}); overridden in tests. */ gh?: GhRunner; } /** An internal candidate before scoping / tie-break. */ interface Candidate { number: number; headRefName: string; /** `undefined` when unknown (e.g. an old `gh` omitted it). */ state?: string; matchKind: PrMatchKind; } /** * True when a PR body CLOSES the given issue via a GitHub closing keyword * (`Fixes`/`Closes`/`Resolves`, optional `owner/repo` prefix, optional `:`/`,` * separator). A bare `#N` prose mention is intentionally NOT matched. The * trailing `([^0-9]|$)` boundary keeps `#747` from matching `#7471`. * * `issue` is the already-validated numeric string the callers pass (gated by * `/^[0-9]/`), so it is only ever digits — no regex-escaping is required. */ export declare function prBodyMatchesIssue(body: string, issue: string): boolean; /** * Pure selection step: given the merged candidate set, apply the caller's * scoping and the stable tie-break, returning the single best match or `null`. * * Scoping: * - `openOnly:true` → pick among OPEN candidates. If none is known to be * OPEN, fall back to unknown-state candidates (an old `gh` that didn't * report `state`); known CLOSED/MERGED are never acceptable. So an * unknown-state candidate is only ineligible when a known-OPEN one exists. * - `openOnly:false` → if any OPEN candidate exists, pick among OPEN; else * pick among everything that remains (closed/merged/unknown). * * Tie-break within the chosen pool: `linked` (0) before `body` (1), then the * lowest PR number. */ export declare function selectPr(candidates: Candidate[], openOnly: boolean): ResolvedPr | null; /** * Resolve the single best-matching PR for an issue, or `null` when nothing * matches (the caller then fails loudly with its own message). */ export declare function resolvePrBranchForIssue(issueNumber: string, opts: ResolveOptions): Promise; export {}; //# sourceMappingURL=resolve-pr.d.ts.map