review-external-pr
Triage an external contributor's PR and prepare it for merge. Detects push permission, base branch, draft state, size, and history quality, then picks one of…
npx skills add https://github.com/microsoft/vscode-documentdb --skill review-external-prReview External PR Workflow
A triage-first workflow for handling community PRs. The skill inspects first, asks two questions, then executes. It never creates branches preemptively.
When to Use
- A contributor PR is open and you want to review and merge it
- Trigger phrases: "prepare this external PR", "review PR #N", "let's review this contribution", or invocation while the user is on a
pr/<owner>/<PR_NUMBER>branch (created bygh pr checkout) - You want a recommendation on push path and merge strategy before doing anything
Phase 1 — Triage (read-only, no prompts)
Identify the PR
Resolve, in order:
- The PR number the user mentioned.
- Current branch matches
pr/<owner>/<PR_NUMBER>→ extract<PR_NUMBER>. gh pr status→ active PR for current branch.
Fetch metadata in one call
gh pr view <PR_NUMBER> --json number,title,author,url,state,isDraft,\
headRefName,baseRefName,headRepositoryOwner,maintainerCanModify,\
mergeable,mergeStateStatus,additions,deletions,changedFiles,commits,labels
Derive signals
| Signal | Rule |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----- | -------------- | ---- | ------ | ----- |
| canPushToHead | headRepositoryOwner.login == "microsoft" OR maintainerCanModify |
| baseBranch | baseRefName (do not hardcode main) |
| isDraft | warn if true |
| mergeable | warn if mergeable != "MERGEABLE" (values: MERGEABLE, CONFLICTING, UNKNOWN) |
| mergeReady | warn if mergeStateStatus is not CLEAN (other values: DIRTY, BLOCKED, BEHIND, UNSTABLE, HAS_HOOKS, UNKNOWN) |
| commitCount | commits.length |
| messyHistory | any commits[].messageHeadline (the first line of the commit message, returned by gh pr view --json commits) matches /wip | fixup | address review | typo | merge( | $)/i |
| changedLines | additions + deletions |
| sizeBucket | small ≤ 50 changed lines, medium ≤ 300, large > 300 (uses changedLines) |
Squash recommendation
| Condition | Recommend |
|---|---|
commitCount == 1 | No squash (rebase or merge) — history already clean |
commitCount ≤ 3 AND no messy subjects AND small | Ask, default no squash |
commitCount > 3 OR messy subjects detected | Squash (default) |
Print the triage report
PR #<PR_NUMBER> — <title>
Author: <login> (<fork|same-repo>)
Base: <baseBranch>
State: <state>, <draft?>, mergeable=<mergeable>, mergeStateStatus=<mergeStateStatus>
Push to head: <✅ allowed reason | ❌ blocked reason>
Size: +<additions> / -<deletions> across <changedFiles> file(s), <commitCount> commit(s)
History: <clean | messy: "<sample messageHeadline>">
Recommendation:
• Path: <direct push | reviews/ branch | review-only>
• Merge: <--squash | --merge | --rebase> (<reason>)
Stop here and present the report.
Phase 2 — Two questions
Ask only these. Pre-select the recommended option.
Q1: Do you need to add changes before merging?
- No → Path A (review & merge)
- Yes, small tweaks → Path B (direct push) if
canPushToHead, otherwise Path C - Yes, heavy rework / contributor unresponsive → Path C (
reviews/staging branch)
If canPushToHead == false, omit the "direct push" option and explain: "Contributor disabled maintainer edits; we must use a reviews/ branch."
Q2: Merge strategy?
Offer --squash, --merge, --rebase with the recommended option marked. Justify the default in one short sentence (e.g., "4 commits including 'fix typo' — squash recommended").
Phase 3 — Execute
Run commands non-interactively, echoing each one. After merge, print a one-line summary with the merged commit/PR URL.
Path A — Review & merge (no maintainer changes)
gh pr checkout <PR_NUMBER> # optional, for local inspection
# review, leave comments via the PR UI or `gh pr review`
gh pr merge <PR_NUMBER> --<strategy> # against the PR's actual base
Path B — Direct push to the contributor's branch
Requires canPushToHead == true.
gh pr checkout <PR_NUMBER> # sets up a remote tracking the fork branch
# make changes, commit
git push # updates the existing PR in place
gh pr merge <PR_NUMBER> --<strategy>
The existing PR updates in place; the contributor keeps authorship of their commits and maintainer commits are attributed to the maintainer. No second PR is needed.
Path C — reviews/ staging branch
Use when push to head is blocked, or when the maintainer explicitly wants to isolate rework.
Branch slug sanitization — derive <slug> from the PR title:
- Lowercase.
- Replace every run of non-
[a-z0-9]characters with a single-. - Trim leading/trailing
-. - Truncate to 30 characters; trim trailing
-again if the cut left one.
Example: "fix(tree): sort _id_ index first / cleanup" → fix-tree-sort-id-index-first-c.
Full branch name: reviews/<slug>-pr-<PR_NUMBER>.
git fetch origin
git checkout -b reviews/<slug>-pr-<PR_NUMBER> origin/<baseBranch>
git push -u origin reviews/<slug>-pr-<PR_NUMBER>
Retarget the contributor's PR:
gh pr edit <PR_NUMBER> --base reviews/<slug>-pr-<PR_NUMBER>
gh pr view <PR_NUMBER> --json baseRefName # verify
⚠️
gh pr edit --basemay print a deprecation warning about Projects (classic). Cosmetic only — the base change succeeds.
Merge the contributor's PR into the review branch:
gh pr merge <PR_NUMBER> --<strategy>
Pull and create the finalization PR back to the original base:
git checkout reviews/<slug>-pr-<PR_NUMBER>
git pull origin reviews/<slug>-pr-<PR_NUMBER>
gh pr create \
--base <baseBranch> \
--head reviews/<slug>-pr-<PR_NUMBER> \
--title "<original title> [reviewed]" \
--body "Finalizes review of @<author>'s contribution in #<PR_NUMBER>.
Original PR: <PR_URL>"
Comment on the original PR:
gh pr comment <PR_NUMBER> \
--body "Thanks for the contribution! Review continues in #<NEW_PR_NUMBER> where maintainer changes are finalized before merging to \`<baseBranch>\`."
Merge Strategy Reference
| Strategy | When to use |
|---|---|
--squash | Default for messy/multi-commit external PRs. One revert undoes the change. Contributor still gets authorship credit. |
--merge | Large feature where individual commits are meaningful and worth preserving. |
--rebase | Single clean commit, or a series of clean atomic commits you want linear on the base. |
Hard Rules
- Never hardcode
mainas the base — always readbaseRefName. - Never create a
reviews/branch in Phase 1. - Never force-push to a contributor's branch.
- If the PR is a draft, refuse to merge and report it back to the maintainer.
- If
mergeable == "CONFLICTING"ormergeStateStatus != "CLEAN", stop and surface that before any merge command.
Summary
| Phase | What happens | Output |
|---|---|---|
| 1 | Read PR metadata, derive push capability + recommendations | Triage report |
| 2 | Ask Q1 (path) and Q2 (merge strategy) | Decision |
| 3 | Execute the chosen path with the chosen merge strategy | Merged PR / finalization PR |