Auto-PR workflow — Stop hook that ships and wakes you on review
Q16 · Parallelism & automation How are your PRs created when work finishes?
Max-score answer: “A Stop hook that auto-opens the PR and re-wakes me on review feedback.”
Why it matters: An auto-PR + review-fix loop turns “code written” into “PR shipped” without human bookkeeping in the middle.
Why this matters in 2026
Section titled “Why this matters in 2026”In 2026 the bottleneck has moved. AI-assisted coding pushed PR volume up roughly 29% year over year, and the slowdown is no longer “writing the code” — it’s the bookkeeping ring around the code: branching off the right base, pushing, opening the PR with a sensible title, watching for the CodeRabbit summary, fixing the Copilot review nits, re-pushing, waiting on CI, and finally merging. A solo developer who writes a feature in 25 minutes can easily spend another 25 minutes on that ring. Multiply that by 8–12 PRs per day and the “shipping tax” becomes the single largest leak in your week. The agents became uncannily good at writing the patch; they remained terrible at handing off the patch unless you told them how, every single time. The Stop hook closes that gap. When the agent finishes substantive work, it doesn’t wait for “now make a PR” — it stages, commits, pushes, opens the PR via gh pr create, watches for review comments, and wakes the agent up again the moment something actionable lands. Combined with gh pr merge --auto --squash --delete-branch, the loop closes itself when CI goes green and reviews approve. That’s the difference between an agent that produces code and one that produces merged code.
What “max score” actually looks like
Section titled “What “max score” actually looks like”You get the top mark on Q16 only when all five of these are true on a given day:
- The agent doesn’t ask permission to ship. When substantive work produces commits or staged changes, the agent pushes a feature branch and opens the PR automatically — no “shall I open a PR now?” prompt. The decision lives in code (hook + workflow rule), not your working memory.
- A Stop hook fires the workflow. When the agent’s main turn ends, a Stop hook (typically
~/.claude/hooks/auto-pr-watch.sh) runs, decides whether the repo needs ashipaction, afixaction, or neither, and either drives the action or re-wakes the agent with a tightly-scoped prompt. - Review feedback wakes the agent back up. When CodeRabbit, Copilot Code Review, a human reviewer, or a failing CI check posts new comments after your last push, the hook detects new activity (by comparing comment IDs and head SHAs against a stored signature) and triggers a
fixcycle. The agent reads every actionable comment, fixes, commits, and pushes — then waits a bounded interval (4 min → 12 min is the field-tested pair) for late comments. - Auto-merge is queued, not forced. Once the re-check loop settles, the agent runs
gh pr merge --squash --delete-branch --auto. The--autoflag queues the merge for whenever required checks pass — it does not override branch protection, skip CI, or--adminpast a real blocker. If checks fail or reviewers request changes, the loop surfaces the blocker. - The loop ends naturally. State is tracked per-repo as a signature
(action, branch/PR#, head_sha, max_comment_id). Once the agent has acted on a state, the hook won’t re-wake it for the same state — only for genuinely new activity. The loop terminates because comments stop arriving.
Anything weaker — “I run a script that creates the PR but I check it manually,” or “I have a hook that opens a PR but doesn’t watch for reviews,” or “I let the agent merge whenever it feels like it” — is mid-tier on Q16.
Current landscape (web-search-verified)
Section titled “Current landscape (web-search-verified)”Claude Code released its hooks system in early 2026 with 27 lifecycle events; the Stop hook fires whenever the agent finishes responding and is the natural hand-off point for auto-PR work. Hooks are deterministic shell commands wired into ~/.claude/settings.json — they aren’t asked by the model, they’re enforced by the harness. That’s the trick: hooks turn “the agent should remember to open a PR” (which it won’t) into “the harness opens the PR” (which it does). In parallel, GitHub Copilot Code Review and CodeRabbit became the two dominant review bots, and both post machine-readable feedback an agent can ingest. The pieces are there. The job is to wire them together.
Phase 1: Ship (commit, push, gh pr create)
Section titled “Phase 1: Ship (commit, push, gh pr create)”The ship phase runs the moment the agent’s main turn ends with uncommitted work or unpushed commits. The hook script resolves the repo’s default branch (do not hardcode main — some repos use master, preview, develop), creates a feature branch if you’re sitting on the default, commits any staged work with a conventional message, and opens the PR.
# Resolve default branch — never hardcode.default=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)current=$(git rev-parse --abbrev-ref HEAD)
# If we're on the default branch, branch off before pushing.if [ "$current" = "$default" ]; then git checkout -b "feat/$(date +%s)-auto"fi
# Stage and commit (conventional message lifted from the diff).git add -Agit commit -m "feat: <agent-summarised change>" || true
# Push and open the PR. gh defaults --base to the repo's configured base.git push -u origin HEADgh pr create \ --title "feat: <agent-summarised change>" \ --body "$(cat <<'EOF'## Summary- <bullets lifted from commit messages>
## Test plan- [ ] CI passes- [ ] Manual smoke
🤖 Generated with Claude CodeEOF)"The critical rules: don’t pause for confirmation before pushing, don’t hardcode the base branch, and don’t use --no-verify to skip pre-commit hooks — if a hook fails, the commit didn’t happen, so fix the issue and create a new commit. Never --amend after a hook failure (it would silently rewrite the previous good commit).
Phase 2: Watch + fix review comments
Section titled “Phase 2: Watch + fix review comments”Phase 2 is the part most people skip — and the reason their Q16 score caps at “I have a script that opens PRs”. The agent has to come back to the PR after the bots and humans land their notes. The hook handles the watching; the agent handles the fixing.
For each open PR your hook is responsible for, pull every feedback surface:
# Top-level conversationgh pr view "$PR" --comments
# Inline review commentsgh api "repos/$OWNER/$REPO/pulls/$PR/comments"
# Review summaries (CodeRabbit, Copilot, human reviewers)gh api "repos/$OWNER/$REPO/pulls/$PR/reviews"
# CI statusgh pr checks "$PR"gh run view "$RUN_ID" --log-failed # for any failing jobTreat all four as in-scope: human reviewers, CodeRabbit (which now spawns its own coding agent via Autofix on Pro plans, April 2026), Copilot Code Review (multi-agent architecture, ~71% actionable feedback rate), and the GitHub Actions log. Skip acknowledgements and off-topic chatter.
For each actionable comment, the agent analyses the request, implements the fix, commits with a clear message, and pushes. Do not pause for confirmation. Do not squash-rewrite history — push new commits so reviewers see the deltas they asked for.
The re-check loop is the secret sauce. Late comments are the norm — CodeRabbit often follows up 30–90 seconds after your push, Copilot Code Review can take 2–5 minutes, and humans drift in over hours. The 4 min → 12 min backoff pair is field-tested: 4 minutes (240s) stays within the prompt-cache TTL so the agent doesn’t pay a cold-cache tax on the second pass; 12 minutes covers slower CI and human follow-up. If a wait surfaces new actionable comments, loop back and reset the backoff. If both waits return nothing, exit to Phase 3.
Phase 3: Auto-merge with --auto and --delete-branch
Section titled “Phase 3: Auto-merge with --auto and --delete-branch”When the re-check loop settles with no actionable comments, check merge readiness and queue the merge:
# Are there blockers?gh pr view "$PR" --json statusCheckRollup,reviewDecision,mergeable,mergeStateStatus# Real blockers: FAILURE/CANCELLED checks, CHANGES_REQUESTED, CONFLICTING.# Pending checks are NOT a blocker — --auto waits them out.
# Discover which merge methods the repo allows.gh api "repos/$OWNER/$REPO" --jq '{squash:.allow_squash_merge,merge:.allow_merge_commit,rebase:.allow_rebase_merge}'
# Queue the merge.gh pr merge "$PR" --squash --delete-branch --auto--auto queues the merge for whenever required checks pass. If the repo doesn’t have auto-merge enabled and the flag errors, drop --auto and merge only when everything is already green. If --squash isn’t allowed, retry with --merge or --rebase. After the merge lands, restore a clean local state: git checkout "$default" && git pull --ff-only && git branch -d <merged-branch>. The lowercase -d refuses to delete unmerged work, so it’s safe.
On any real blocker (failing CI, conflicts, requested changes), surface it. Never --admin-override. That single line is the difference between an automation that helps and an automation that ships broken code on a Friday.
State tracking (signature files, deduping)
Section titled “State tracking (signature files, deduping)”The hook needs to remember what it’s already done, or it will re-fire on every Stop event and either spam the PR or, worse, re-trigger the agent into an infinite loop. The pattern that works is a per-repo signature file in ~/.claude/auto-pr-state/<repo-hash>.json containing the tuple (action, branch/PR#, head_sha, max_comment_id). The hook computes the current signature, compares against the stored one, and:
- If the signature is unchanged, do nothing — the agent has already handled this state.
- If only
max_comment_idadvanced, wake the agent with afixprompt. - If
head_shaadvanced or the PR is new, this is a fresh state — store it and decide whether to wake.
To reset a single repo: rm ~/.claude/auto-pr-state/<hash>.json. To clear all: rm ~/.claude/auto-pr-state/*.json. To opt out globally: comment out the hook in ~/.claude/settings.json.
Show real hook script
Section titled “Show real hook script”A minimal working ~/.claude/settings.json entry:
{ "hooks": { "Stop": [ { "matcher": "*", "hooks": [ { "type": "command", "command": "~/.claude/hooks/auto-pr-watch.sh" } ] } ] }}The auto-pr-watch.sh script itself is short: it reads repo state, computes a signature, decides between ship, fix, or noop, and uses asyncRewake (or your harness’s equivalent) to wake the agent with a tightly-scoped prompt like “Phase 2 fix: PR #421 has 3 new CodeRabbit comments, please address them.” The hook never does the work — it only notifies. Workflow logic stays in the agent’s prompt, where it belongs.
Step-by-step: building auto-PR-watch
Section titled “Step-by-step: building auto-PR-watch”-
Verify
ghis installed and authenticated. Rungh auth status. If it reports nothing, rungh auth login. The Stop hook is useless without this. -
Pick the trigger states your hook will recognise. Minimum:
ship(uncommitted work or unpushed commits on a non-default branch) andfix(an open PR has comments newer than the stored signature). Optional:mergeandnoop. -
Write
~/.claude/hooks/auto-pr-watch.sh. Make it executable (chmod +x). It computes the current signature, reads~/.claude/auto-pr-state/<hash>.json, decides an action, writes the new signature back, and either drivesghdirectly or calls your harness’s wake-agent API. -
Wire the Stop hook into
~/.claude/settings.json. Use the JSON snippet above. Restart your Claude Code session so the config loads. -
Add a project-level workflow rule. In
CLAUDE.md, document the Phase 1/2/3 contract explicitly: when work finishes, push and open the PR without asking; when review arrives, fix and re-push without asking; when the loop settles, queue auto-merge. -
Test with a throwaway PR. Pick a tiny change, let the agent finish, watch Phase 1. Add a comment yourself, wait, watch Phase 2 fire. Approve the PR, watch Phase 3 queue the merge.
-
Tune the backoff to your review tempo. 4 min → 12 min is the sane default. With CodeRabbit Pro Autofix, add a third 25-minute wait. Solo, keep it tight.
-
Add an opt-out phrase. “don’t make a PR for this” or “this is exploratory” — when you say it, the agent skips Phase 1. Safety valve for spikes and prototypes.
Common pitfalls
Section titled “Common pitfalls”- Pushing straight to the default branch. The single most expensive mistake. Always resolve the default with
gh repo view --json defaultBranchRef --jq .defaultBranchRef.nameand branch off it if you’re sitting on it. If commits leaked onto the default, reset it to origin before pushing — never force-push the default branch. --no-verifyto skip pre-commit hooks. If a hook fails, the commit didn’t happen. Skipping it silently lands broken code. Fix the underlying issue and create a new commit. Same rule for--no-gpg-sign— investigate, don’t bypass.- Force-pushing to main / master / preview. Never. Even with
--auto-merge. If your hook reaches forgit push -fagainst the default branch, kill it before it lands. Force-push to your own feature branch is fine; force-push to a shared branch is how you lose other people’s work. --amendafter a failing pre-commit hook. The commit didn’t happen, so--amendmodifies the previous commit — usually destroying earlier work. Always create a new commit after a hook failure.--admin-override ongh pr merge. Bypasses branch protection. Never the right move from an automated hook. If checks fail or reviewers requested changes, the loop must surface the blocker and stop.- Forgetting to dedupe with a signature file. Without a signature, the Stop hook fires on every turn and either spams the PR or wedges the agent into an infinite “Phase 2 again” loop. The signature is what terminates the loop.
- Treating every comment as actionable. Acknowledgements (“LGTM!”) and off-topic chatter shouldn’t trigger a fix cycle. Filter for comments that explicitly request changes.
- No opt-out phrase. Spikes and prototypes don’t want a PR. If you can’t say “skip Phase 1,” the hook will fight you on throwaway work.
How to verify you’re there
Section titled “How to verify you’re there”- You have a Stop hook script at
~/.claude/hooks/auto-pr-watch.sh(or your harness’s equivalent) and it’s wired into~/.claude/settings.json. - When the agent finishes substantive work on a feature branch, a PR appears on GitHub without you running
gh pr createmanually. - The Stop hook resolves the default branch dynamically — you can move it from
maintopreviewand the workflow still works. - When CodeRabbit, Copilot Code Review, a human reviewer, or a failing GitHub Actions check posts new comments, the agent wakes up and addresses them within ~5 minutes — without you re-pasting the comments.
- After the re-check loop settles with no actionable comments,
gh pr merge --squash --delete-branch --autois queued automatically. - You have a per-repo signature file in
~/.claude/auto-pr-state/and the hook does not re-fire on the same state twice. - You have an opt-out phrase (“don’t make a PR for this”) and it actually skips Phase 1.
- You have never seen the hook reach for
--no-verify,--admin, orgit push -fto a shared branch. If you did, you killed the hook before it landed.