Review a PR and post findings as inline comments to GitHub. Shows all feedback for approval before posting.
Resources
1Install
npx skillscat add olivoil/om-skills/code-review Install via the SkillsCat registry.
Code Review
Review a pull request, analyze changes with parallel specialized agents, and post findings as inline GitHub review comments after user confirmation.
Phase 1: Resolve PR & Detect Re-review
Accept input as any of: PR URL, #123, a number, a branch name, or nothing (auto-detect current branch's PR).
Resolve the PR number:
# From URL: extract owner/repo and number from the URL
# From #123 or plain number:
gh pr view 123 --json number --jq '.number'
# From branch name:
gh pr view branch-name --json number --jq '.number'
# From nothing (current branch):
gh pr view --json number --jq '.number'Detect re-review: Check if we already reviewed this PR.
# Get our GitHub username
gh api user --jq '.login'
# Fetch all reviews on this PR
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '.[] | {id, user: .user.login, commit_id: .commit_id, state: .state}'If a previous review from our user exists:
- Record the
commit_idfrom our most recent review - Fetch comments from that review:
gh api repos/{owner}/{repo}/pulls/{number}/reviews/{review_id}/comments - This is now a re-review — Phase 3 will diff against the previous review
Phase 2: Fetch PR Context
Gather everything needed for the review:
# 1. PR metadata
gh pr view {number} --json number,title,body,author,baseRefName,headRefName,headRefOid,files,additions,deletions,url
# 2. Full diff
gh pr diff {number}
# 3. Changed files list
gh pr view {number} --json files --jq '.files[].path'Then read:
- Root
CLAUDE.mdif it exists - Any
CLAUDE.mdin directories containing changed files
If re-review, also identify what changed since last review:
gh api repos/{owner}/{repo}/compare/{last_reviewed_sha}...{current_head_sha} --jq '.files[].filename'Finally, launch a Haiku agent to produce a 2-3 sentence summary of the PR's purpose from the diff and PR body. This summary is provided to each review agent for context.
Phase 3: Analyze Changes (Multi-Agent)
Launch 7 parallel agents via the Task tool (subagent_type: "general-purpose"). Provide each agent with:
- The full PR diff
- CLAUDE.md contents (root + directory-level)
- The PR summary from Phase 2
- The list of changed files
Each agent focuses on one review dimension. Instruct each to return a JSON array of issues.
General Review Agents
| # | Model | Focus | Instructions |
|---|---|---|---|
| 1 | Sonnet | CLAUDE.md compliance | Audit changes against project conventions in CLAUDE.md. Only flag violations of rules explicitly stated in CLAUDE.md. If no CLAUDE.md exists, return an empty array. |
| 2 | Sonnet | Bug detection | Shallow scan of the diff for obvious bugs: logic errors, off-by-one, null/undefined risks, race conditions, resource leaks. Avoid stylistic nitpicks. |
| 3 | Sonnet | Git history context | Run git log and git blame on modified files. Identify bugs or regressions only visible with historical knowledge (e.g., reverted fixes, violated assumptions from past commits). |
| 4 | Haiku | Previous PR comments | Use gh pr list --state merged --search to find previous PRs that touched these files. Check their review comments for patterns that may apply here too. |
| 5 | Haiku | Code comment compliance | Read the full source files (not just the diff) for inline comments: // TODO, // IMPORTANT, // NOTE, // HACK, // FIXME. Ensure changes respect these annotations. |
Specialized Review Agents
| # | Model | Focus | Instructions |
|---|---|---|---|
| 6 | Sonnet | Security | OWASP top 10: injection (SQL, command, XSS), auth bypass, credential exposure, insecure deserialization, SSRF, path traversal. Check for hardcoded secrets, unsafe eval, unvalidated redirects, missing input sanitization. |
| 7 | Sonnet | Error handling | Empty catch blocks, swallowed errors, broad exception handling (catch(e) {}), missing error propagation, fallback behavior that hides real problems. Ensure callers get actionable feedback on failures. |
| 8 | Sonnet | Performance | N+1 queries, unnecessary re-renders, memory leaks, O(n²) where O(n) is possible, large/unnecessary imports, missing pagination, unbounded loops or allocations. |
Each agent must return issues in this exact JSON format:
[
{
"path": "src/app.js",
"line": 42,
"severity": "critical",
"body": "Markdown explanation of the issue with a suggested fix",
"category": "bug"
}
]Where:
path— file path relative to repo rootline— line number in the new version of the file (from the diff's+side)severity— one ofcritical,important,suggestionbody— markdown explanation, including a fix suggestion when possiblecategory— one ofclaude-md,bug,history,previous-pr,code-comment,security,error-handling,performance
Phase 4: Score & Filter
Collect all issues from the 9 agents. Deduplicate issues that flag the same line with the same concern.
For each unique issue, launch a parallel Haiku agent (model: "haiku") that independently scores confidence (0–100):
- 0 — False positive, doesn't survive scrutiny, or the issue is pre-existing (not introduced by this PR)
- 25 — Might be real, might be false positive. Stylistic preference not backed by CLAUDE.md
- 50 — Real but minor nitpick, low practical impact
- 75 — Very likely real, will impact functionality, or directly violates a CLAUDE.md rule
- 100 — Confirmed real, will happen frequently, evidence is clear in the diff
Provide each scoring agent with:
- The issue object (path, line, severity, body, category)
- The relevant diff hunk (±10 lines around the flagged line)
- CLAUDE.md contents (for
claude-mdcategory issues, the agent must verify the specific rule exists)
Filter rules — discard issues that:
- Score below 80
- Flag lines the PR author didn't modify (pre-existing issues)
- Would be caught by a linter or type checker (eslint, tsc, mypy, etc.)
- Describe intentional behavior changes that match the PR's stated purpose
- Are silenced by lint-ignore comments (
// eslint-disable,# noqa, etc.)
Phase 5: Re-review Delta
Skip this phase if this is a first review.
If this is a re-review (previous review detected in Phase 1), categorize surviving issues:
- Addressed — issues from our previous review that no longer appear (author fixed them)
- Still outstanding — issues from our previous review that remain unfixed (match by file path + similar body text)
- New issues — issues on code that changed since our last review commit
Build a delta summary for inclusion in the review body.
Tone & Language
All posted comments (both inline and the summary) must read as if written by a human reviewer — natural, conversational, and collegial. Never sound like an automated tool.
Inline comments:
- Use hedging language: "may", "might", "worth double-checking", "could be worth", "it looks like"
- Avoid absolutist phrasing like "this is broken", "this will fail", "you must fix this"
- Prefer "this might cause X" or "worth confirming whether Y is intentional"
- Always provide a concrete suggestion or fix when possible (code snippet, alternative approach)
- Suggest adding a test to verify when the issue is uncertain
- Keep a helpful, non-confrontational tone — you're pointing things out, not issuing mandates
Summary comment (the --body text):
- Write it naturally, as if the reviewer is casually summarizing what they noticed
- Use first person ("A few things I noticed", "I'd suggest", "Worth checking")
- Use bullet points for a quick scannable list of the key findings
- End with "See inline comments for details." to direct attention to the specifics
- Do NOT include "Generated with Claude Code" or any AI attribution footer
- Do NOT use a formal header like "## Code Review:" — just start talking
Example summary tone:
A few things I noticed while going through this:
- The `menuItems` array might need to be a `computed` — otherwise the `locked` flags won't react when user data arrives from the loader
- `username` now maps to `userEmail`, which changes the avatar fallback display
- Minor perf thing — the inline user object in the template could be a `computed`
See inline comments for details.Phase 6: Preview & Confirm
Present all surviving findings to the user, grouped by severity.
First-review format:
## Code Review: {owner}/{repo}#{number}
**{title}** | Author: {author} | Changes: +{additions} -{deletions} across {N} files
### Critical ({count})
- **src/app.js:42** — [Bug] Description (confidence: 92)
### Important ({count})
- **src/utils.ts:15** — [CLAUDE.md] Description (confidence: 85)
### Suggestions ({count})
- **src/helper.js:88** — [Performance] Description (confidence: 80)
**Summary:** 1-2 sentence overall assessment of the PR.Re-review format — additionally show before the summary:
### Since Last Review
- {N} issues addressed (fixed by author)
- {N} issues still outstanding
- {N} new issues foundThen ask the user to choose how to post the review:
- Pending — draft review, only visible to PR author (default for first review)
- Comment — visible review, no approve/reject
- Approve — approve the PR
- Request changes — block the PR
- Cancel — don't post anything
If there are zero issues after filtering, still allow the user to post an approval with a clean summary.
Phase 7: Post Review
If the user chose Cancel, stop here.
Otherwise, write the comments array to a temporary JSON file and run:
bash skills/code-review/scripts/post-github-review.sh \
--owner "{owner}" \
--repo "{repo}" \
--pr {number} \
--commit "{head_sha}" \
--event "{APPROVE|REQUEST_CHANGES|COMMENT}" \
--body "Review summary text" \
--comments /tmp/review-comments-{number}.jsonOmit --event entirely to create a pending (draft) review.
The comments JSON file format:
[
{
"path": "src/app.js",
"line": 42,
"body": "**[Bug]** Markdown explanation with fix suggestion"
}
]Clean up the temporary JSON file after posting.
Phase 8: Confirm
Report success to the user:
Review posted: {review_url}
{N} inline comments + summary on {owner}/{repo}#{number}If re-review, also mention:
Delta: {N} addressed, {N} outstanding, {N} new