Shepherd to Merge
Guide a pull request through code review, feedback resolution, CI validation, and auto-merge. This skill orchestrates the full lifecycle from review to merge without circumventing any branch protection rules or security constraints.
Important: Never use admin privileges to bypass branch protections, force-merge, or dismiss reviews. The goal is to satisfy all merge requirements legitimately.
Multi-agent safety: Multiple agents may be shepherding different PRs in parallel. Expect the base branch to move frequently as other agents merge. The rebase retry loop in step 8 handles this. Each agent works on its own PR branch and does not modify branches belonging to other sessions.
Input
$ARGUMENTS should be one of:
- •A full PR URL:
https://github.com/owner/repo/pull/123 - •A repo-qualified reference:
owner/repo#123 - •A bare PR number:
123— only works when your current directory is inside a git repo with a GitHub remote.
If $ARGUMENTS is empty, ask the user which PR to shepherd — request the full URL or
owner/repo#number.
Steps
1. Identify the PR
Parse $ARGUMENTS to determine the owner, repo, and PR number:
- •Full URL (
https://github.com/owner/repo/pull/123): extract owner, repo, and number from the URL path. - •Qualified reference (
owner/repo#123): split on/and#to get owner, repo, and number. - •Bare number (
123): attempt to detect the repo from git context:- •Run
git remote get-url originto find the GitHub remote. - •Parse
owner/repofrom the remote URL. - •If this fails (not in a git repo), ask the user for the full
owner/repo#number.
- •Run
Once you have owner, repo, and number, set the repo reference (-R owner/repo) for all subsequent
gh commands.
Fetch PR details and confirm the PR is open:
gh pr view <number> -R <owner>/<repo> --json number,title,state,headRefName,baseRefName,url,reviews,statusCheckRollup
If the PR is already merged, print a friendly summary (title, URL, merge status, CI results) and stop — no further action needed. If the PR is closed but not merged, inform the user and stop.
2. Check out the PR branch
Ensure you're in the repo and check out the PR branch:
gh pr checkout <number>
3. Spawn a review subagent
Use the Task tool to launch a subagent that performs a thorough code review of the PR. The subagent should:
- •Read and understand every changed file in the PR diff (
gh pr diff <number>) - •Review for correctness, security issues, performance concerns, and style consistency
- •Return a structured summary: a list of issues found (with file, line, and description) and an overall assessment (approve, request changes, or comment)
Wait for the subagent to return its findings before proceeding.
4. Address review feedback
After the subagent returns, check for any existing review comments and GitHub Copilot suggestions on the PR:
gh api repos/<owner>/<repo>/pulls/<number>/comments --jq '.[] | {id, node_id, path, line, body, user: .user.login}'
gh api repos/<owner>/<repo>/pulls/<number>/reviews --jq '.[] | {id, node_id, state, body, user: .user.login}'
For each piece of actionable feedback (from the subagent review, human reviewers, or Copilot):
- •Fix the issue in the local checkout — edit the file, make the correction
- •Commit the fix with a clear message referencing the feedback
- •Resolve the review thread if it's a PR review comment thread. First, find the thread ID for
the comment, then resolve it:
sh
# Get the thread ID from a review comment's node_id gh api graphql -f query='query { node(id: "<comment-node-id>") { ... on PullRequestReviewComment { pullRequestReviewThread { id } } } }' --jq '.data.node.pullRequestReviewThread.id' # Resolve the thread gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<thread-id>"}) { thread { isResolved } } }' - •Push the fixes to the PR branch
If any feedback requires clarification or is outside the scope of the PR, leave a reply comment explaining why it wasn't addressed rather than ignoring it.
5. Resolve all review threads
Before proceeding, ensure every review thread on the PR is resolved. Unresolved threads block auto-merge on repos with branch protection.
- •
List all review threads and check for unresolved ones:
shgh api graphql -f query='query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <number>) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 1) { nodes { body path } } } } } } }' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)' - •
For each unresolved thread where the feedback was addressed in a commit, resolve it:
shgh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<thread-id>"}) { thread { isResolved } } }' - •
For threads where the feedback was not addressed, leave a reply comment explaining why.
6. Re-review after fixes
If changes were made in step 4, do a quick self-review of the new commits to make sure the fixes are correct and don't introduce new issues. If new problems are found, go back to step 4.
7. Verify CI checks
Wait for all CI status checks to complete:
gh pr checks <number> -R <owner>/<repo> --watch
If any checks fail:
- •Inspect the failure logs:
sh
gh run view <run-id> --log-failed
- •Fix the issue locally, commit, and push
- •Wait for checks to re-run and pass
- •Repeat until all checks are green
Do not skip or override failing checks. If a check is flaky and the failure is unrelated to the PR, note it in a comment but do not bypass it.
8. Rebase on base branch (with retry loop)
Concurrent merges from other agents move the base branch forward frequently. This step may need to run multiple times — up to 5 attempts. With many parallel agents, the base branch can move several times while CI is running.
For each attempt (max 5):
- •
Check merge state:
shgh pr view <number> -R <owner>/<repo> --json mergeStateStatus --jq .mergeStateStatus
- •
If the status is
CLEAN, exit the loop — proceed to step 9. - •
Otherwise (
BEHIND,DIRTY, or any other non-clean state): a. Fetch the latest base branch:shgit fetch origin <base-branch>
b. Rebase onto it:
shgit rebase origin/<base-branch>
c. If there are merge conflicts, resolve them carefully — read both sides before choosing. d. Force-push with lease (safe — only overwrites your own branch):
shgit push --force-with-lease
e. Wait for CI to re-run and pass before the next iteration. f. After CI passes, re-check
mergeStateStatus. If still notCLEAN, loop again. - •
If all 5 attempts fail to reach
CLEAN, inform the user — multiple agents are likely merging concurrently and manual coordination may be needed.
9. Enable auto-merge
Once all feedback is addressed and CI is green (or running), enable auto-merge so GitHub merges the PR automatically when all branch protection requirements are met:
gh pr merge <number> -R <owner>/<repo> --auto --squash
Use --squash by default. If the repo convention prefers merge commits or rebases, match the
convention instead.
Do not use --admin or any flag that bypasses branch protections. The PR must satisfy all
required reviews, status checks, and other branch protection rules before merging.
10. Confirm and summarize
Print a summary:
- •PR: title and URL
- •Review: findings from the subagent review
- •Fixes applied: list of commits pushed to address feedback
- •CI status: all checks passing
- •Merge: auto-merge enabled, will merge when all requirements are met
If auto-merge could not be enabled (e.g., the repo doesn't support it), inform the user and suggest they merge manually once requirements are satisfied.