pr-rescue: find stalled pull requests in other repositories

A command-line tool that lists your pull requests in repositories you do not own, sorted by who has to act next, and finds the ones a stale bot closed.

The problem

Pull requests to other people's projects can stall in two ways. Some wait for the author (a merge conflict, a failing check, a reviewer's question) and the author does not notice. Others are closed by a stale bot after weeks without review, sometimes after they had already been approved. GitHub's notifications do not separate these cases, and a bot closure looks like any other closed pull request.

Stale bots are a recurring complaint (reaction counts as of 2026-10-20):

Dashboards such as gh-dash show open pull requests. No existing tool was found that finds bot-closed pull requests or says who has to act on each open one.

How it works

pr-rescue uses the login of the GitHub CLI and a few GraphQL search queries to fetch the user's open, closed and merged pull requests in repositories they do not own. It sorts them into three groups:

  • Waiting on you: a merge conflict, requested changes, failing checks, a draft, or a comment from a person since your last commit that has no answer. This group also holds pull requests a bot closed without merging, marked APPROVED when they had an approval, with a note whether the branch still exists, which decides whether a reopen can work.
  • Waiting on maintainers: approved but not merged, no review yet, or cancelled checks that need a rerun a fork cannot trigger, each with the days idle.
  • Already handled: bot-closed pull requests that a later pull request of yours references, for example with "Replaces #123".

Pull requests closed by a person, or by the author, are left out. Checks are read one by one. A status that only says a deployment preview needs approval (Vercel's "Authorization required to deploy" on fork pull requests) does not count as a failure. A gate job such as enforce-all-checks that fails only because another job was cancelled is not reported separately. Failing checks are named in the output.

This is the example output from the README, with illustrative repository names:

$ pr-rescue
Waiting on you (3)
  acme/router#5059  checks failing: test (3.11)  [open, 78d idle]
      Add retry budget to the proxy
      https://github.com/acme/router/pull/5059
  acme/sdk#1472  closed by github-actions, branch still exists: reopen or resubmit  [closed, 68d idle]
      fix(cli): pass keyword arguments to the start function
      https://github.com/acme/sdk/pull/1472
  acme/storage#9066  merge conflict  [open, 34d idle]
      feat: per-test repeat pass rate threshold
      https://github.com/acme/storage/pull/9066

Waiting on maintainers (2)
  acme/gateway#1638  approved, not merged  [open, 0d idle]
  ...

Already handled (1)
  acme/serving#5636  closed by github-actions, resubmitted as #6421  [closed, 60d idle]

--json prints the list with the fields waiting_on, reason, approved and idle_days.

Testing

The tests build pull request records in the shape the GraphQL query returns and pass them to the classifier. They cover bot-closed pull requests with and without a branch or a successor, closures by a person, merged pull requests, the open states and comments from a person, a bot and the author. A fake GitHub client returns result pages for the three searches, so a test can check that resubmissions are linked, the results are sorted and every query excludes the user's own repositories. Four tests cover the check rules: the Vercel status, a cancelled job with a failing gate job, a gate job that fails alone, and a real failure next to a cancellation. The tests make no network calls. CI runs them on Python 3.10 to 3.13, runs ruff and calls --version and --help.

Limitations

  • Bot detection uses the account type and a short list of well-known bot logins. A person who closes pull requests from a personal account counts as a person.
  • A reopen can still fail when the base branch moved on or the head branch was force-pushed. A new pull request that mentions the old one is then listed under "Already handled".
  • A check that fails because of an external timeout is reported as failing. The tool cannot distinguish it from a failure caused by the change.
  • GitHub search returns at most 1000 results per query.

The code and installation instructions are at github.com/Jwrede/pr-rescue.