A Claude Code skill that sweeps every repo in a GitHub organization for open pull requests and loudly flags the ones nobody has reviewed.
An unreviewed PR is not merely unfinished — it is invisible. In a plain listing
it looks identical to a reviewed one, so it drifts. /scanprs exists to make
that impossible to miss.
In the Claude Code CLI or desktop app:
/plugin marketplace add QuasarApps/scanprs-skill
/plugin install scanprs@scanprs-skill
Some environments — Claude Code on the web, for instance — do not offer the
/plugin command. Declare the same thing in settings instead:
{
"extraKnownMarketplaces": {
"scanprs-skill": {
"source": { "source": "github", "repo": "QuasarApps/scanprs-skill" }
}
},
"enabledPlugins": { "scanprs@scanprs-skill": true }
}Put that in ~/.claude/settings.json to cover every project on your machine, or
commit it to a repo's .claude/settings.json to cover everyone working in that
repo — including cloud sessions, since the file arrives with the clone.
Either way, run /scanprs afterward. Updates arrive when the marketplace
refreshes, so you do not reinstall to get changes.
Given an organization — named by you, or inferred from the current repo's
origin remote — it discovers every repository, lists all open PRs, and sorts
each one by review coverage:
| State | Meaning |
|---|---|
| ✅ Reviewed | A review exists at the current head commit |
| ⏳ Stale | Reviewed, but at an older commit — the code changed afterward |
| 🚩 Missing | No review at all |
| 🚩 Pending | Review requested, none delivered yet |
The stale case is the one most tools miss. A review of a commit that has since been rewritten reads as reassurance while providing none, which is more dangerous than an obviously absent review.
It also reports deltas against your last scan: new PRs, PRs that left the open set, timestamps that moved, coverage that flipped, and repos added to or removed from the org.
## 🚩 Needs review — 3 of 5 open PRs
- **api-gateway [#85](https://github.com/acme/api-gateway/pull/85)** — `add retry budget to upstream calls` — missing
- **billing [#50](https://github.com/acme/billing/pull/50)** — `resolve config path from repo root` — stale since a1b2c3d
- **web [#98](https://github.com/acme/web/pull/98)** — `document the deploy trap` — pending 3d
| Repo | PR | Coverage | Updated |
|---|---|---|---|
| api-gateway | [#85](https://github.com/acme/api-gateway/pull/85) | 🚩 Missing | 19:16Z |
| billing | [#50](https://github.com/acme/billing/pull/50) | ⏳ Stale | 18:39Z |
| web | [#98](https://github.com/acme/web/pull/98) | 🚩 Pending | 18:38Z |
| web | [#94](https://github.com/acme/web/pull/94) | ✅ Reviewed | 18:23Z |
| infra | [#12](https://github.com/acme/infra/pull/12) | ✅ Reviewed | Jul 29 |
**Deltas:** 3 new since last scan; web #94 gained a review; infra #9 no longer open
**Swept:** 34 repos in acme; 4 had open PRs/scanprs # infers the org from the current repo
/scanprs acme # sweeps a specific org or user account
/scanprs acme/web acme/api # scopes to specific repos
By default it looks for GitHub Copilot code review. If you care about a different reviewer — another bot, a specific person, a team, or simply "anyone at all" — say so and it matches that instead. The four coverage states are the useful part; which reviewer counts is a parameter, not a fixture.
GitHub access through any one of: the GitHub MCP server, the gh CLI, or a
Claude Code session with repos attached. The skill loads deferred MCP tools on
demand and falls back across these, so it works in interactive sessions, remote
sessions, subagents, and scheduled runs alike.
If it cannot reach GitHub at all it says so explicitly, rather than reporting an empty scan — "I could not check" must never look like "no open PRs".
/scanprs does not push, commit, comment, request reviewers, mark drafts ready,
approve, or merge. It surfaces gaps and leaves every decision to you.
This is deliberate. A scan that correctly diagnoses "this PR needs a review request" is one API call away from acting on it, and that call is not the skill's to make.
Scan history lives in ~/.claude/scanprs-state/<owner>.json, keyed by owner so
different orgs keep separate histories. Delete it to reset to a baseline. If the
path is unwritable — a sandboxed subagent, say — the scan still runs and reports
a baseline instead of failing.
MIT