Table of Contents7 sections

AI code review is one of those things that's simultaneously awesome and exhausting.
Awesome because it catches real bugs. Exhausting because every PR now grows a small forest of comments before another engineer even opens it.
A typical review for me looks something like this:
A couple of genuinely useful bug reports.
A few good suggestions that are worth considering.
Several "you should extract this helper" comments.
A handful of confidently incorrect observations.
None of those are individually a problem. The problem is the loop:
Read every comment. Figure out which ones actually matter. Fix the real issues. Reply to the wrong ones. Request another review. Repeat.
At some point I realized I was spending more time triaging AI review comments than writing the code they were reviewing.
So I decided to automate the boring part. Not the coding. The triage.
My first instinct: use GitHub's MCP
The obvious solution seemed to be the official GitHub MCP. Claude Code supports MCP. GitHub has an official server. Perfect.
Except… I never even got to call a tool.
Claude Code currently expects OAuth Dynamic Client Registration during authentication, while GitHub's Copilot MCP endpoint doesn't support that flow. The OAuth handshake failed before I could do anything useful.
There are workarounds. PATs. Different authentication paths. People have gotten it working. But every workaround meant managing a token I'd have to secure, rotate, and pray nobody ever commits to a repo. For reading PR comments.
Somewhere halfway through the OAuth docs the cost stopped matching the task, and I asked myself a much simpler question:
Why am I trying so hard to build an integration? What do I actually need?
The answer wasn't "an MCP." The answer was: I need Claude to read my PR comments.
That's a much smaller problem.
Turns out GitHub solved that years ago
I'd somehow forgotten the most boring tool sitting on my machine.
gh pr view 42 --json reviews,comments,latestReviews
gh api repos/{owner}/{repo}/pulls/42/comments
gh api repos/{owner}/{repo}/pulls/42/reviewsThat's the entire data layer.
gh was already installed. Already authenticated. Already knew how to talk to GitHub. No MCP server, no PAT to generate, no secret to babysit. It just rides whatever session gh auth login already gave me.
If you want to follow along, the only setup is confirming gh is logged in:
gh auth statusAnd if it isn't:
gh auth loginThat's the whole authentication story. No tokens, no config files holding secrets.
Claude Code doesn't particularly care whether the JSON comes from an MCP tool or a shell command. It just needs structured input. So instead of spending another afternoon fighting authentication, I let Claude shell out to gh, collect the review data, and moved on.
Ironically, that ended up being the least interesting part of the project.
Fetching comments wasn't the hard problem
Originally my prompt asked Claude one question:
Should I fix this review comment?
It sounded reasonable. It also produced mediocre results, because review comments aren't binary.
A reviewer can be technically correct while suggesting something completely out of scope for the PR.
A reviewer can be wrong while still deserving a polite explanation instead of silence.
A reviewer can identify a real issue but propose a fix that's worse than the original code.
Those are different decisions. So I split them into two independent axes.
1. Is the comment correct? Legit · Wrong · No-impact
2. What should I do? Fix · Push back · Skip · Fix with caveat
That one change made a surprisingly big difference, because it let a comment carry two truths at once:
Legit + Skip: "This helper could be extracted." True. Not worth expanding the scope of this PR.
Wrong + Push back: "This can be null." Actually no, the framework guarantees it isn't, and here's why.
Legit + Fix with caveat: "This query could be optimized." Yes, but it also makes the code much harder to read, so maybe that's a separate PR.
Separating correctness from action forced the model to think more like an engineer instead of treating every review comment as a TODO item.
Then I added guardrails
Once the judgment layer looked good, the rest of the work became surprisingly boring. The command now refuses to do a bunch of things:
It only edits files already included in the PR.
It drafts replies but never posts them.
It commits locally but never pushes.
It stops after presenting the review table so I explicitly approve what gets changed.
I trust Claude to help me think. I don't trust it to decide my Git history.
The workflow
Now my review flow looks something like this:
/fix-pr-review 4821Or I leave the number off entirely and let it figure out which PR I mean from the branch I'm currently on:
/fix-pr-review
Either way, Claude pulls every review comment using gh, then produces something like this:
Author | Review comment | Correctness | Action | Draft reply |
|---|---|---|---|---|
Copilot | Add a null check here | Wrong | Push back | Explains why the value is guaranteed non-null |
Alice | This SQL should be parameterized | Legit | Fix | - |
Copilot | Extract this helper | Legit | Skip | Out of scope for this PR |
Bob | This query could be cached | Legit | Fix with caveat | Discuss tradeoff |
Nothing gets edited. Nothing gets committed. Nothing gets pushed. Claude waits for me to say which rows should actually become code changes.
That part is intentional. The fastest way to lose trust in an AI workflow is letting it confidently apply every suggestion it sees.
It also keeps honest track of what's been fixed and what hasn't.

The command itself
There's no plugin here. A Claude Code slash command is just a markdown file in ~/.claude/commands/, and the filename becomes the command. So ~/.claude/commands/fix-pr-review.md gives you /fix-pr-review, available in every repo.
The whole thing is one prompt with the policy baked in:
Fetch all reviews and review comments on PR $ARGUMENTS using the gh CLI:
- gh pr view $ARGUMENTS --json reviews,comments,latestReviews (overview)
- gh api repos/{owner}/{repo}/pulls/$ARGUMENTS/comments (inline review comments)
- gh api repos/{owner}/{repo}/pulls/$ARGUMENTS/reviews (full review bodies)
Include human and bot reviewers. Tag each comment with its author.
If no PR number is given, run gh pr view --json number to get the open PR for the
current branch. If there's none, tell me and stop.
Only triage UNRESOLVED comments. Skip threads already marked resolved unless I say otherwise.
For each comment, evaluate:
CORRECTNESS: Legit / Wrong / No-impact
ACTION: Fix / Push back / Skip / Fix-with-caveat
REPLY (draft only when it adds value, otherwise "-"):
- Push back: required. Terse for bots, collegial and reasoned for humans.
- Fix-with-caveat: explain the tradeoff taken.
- Fix: skip reply unless the author asked a question.
Output a table: author / comment / correctness / action / drafted reply.
Then STOP. Do not change anything until I explicitly tell you which comments to act on.
Only edit files in the PR diff. Only act on items I approve.
After I confirm and you make fixes, ask: "Want me to commit this locally?"
- Only commit if I say yes. Commit LOCALLY ONLY, NEVER push.
- Read the last commits with git log and match their message format.
- Do NOT add Co-Authored-By or any attribution footer. That's it. Drop it in the file, run /fix-pr-review 42, and the table above is what comes back. Everything I described in the guardrails section is just sentences in that prompt: the STOP, the only edit files in the PR diff, the NEVER push. It reads paranoid because it should. Claude Code will absolutely interpret "ok we're good" as license to push if you let it.
The lesson wasn't "use gh instead of MCP"
That's the funny part. If GitHub fixes the OAuth issue tomorrow, I might switch the implementation over to MCP. I genuinely don't care.
Because I eventually realized the integration wasn't the product. The integration is replaceable. The judgment isn't.
The valuable part of this project isn't the three GitHub commands. It's teaching the model the difference between:
"This comment is technically correct."
and
"This comment deserves a code change."
Those are not the same sentence.
I started this project thinking I needed a better way to fetch review comments. I finished it realizing GitHub had already solved that problem years ago. The real work was teaching the model how to think like a reviewer instead of another reviewer-shaped noise generator.
Sometimes the most valuable automation isn't replacing human judgment. It's getting the machine to stop before it replaces yours.
