skills/pr-issue-review/SKILL.md
Review a GitHub pull request using the passive, neutral, assertive, or aggressive profile, optionally paired with a named reviewer persona that sets the review voice, by statically reading the PR diff, metadata, comments, and discovered issue/context links to determine whether it solves the stated issue. Use for automated or manual PR review flows that should leave an emoji-marked top-level review plus targeted inline comments or suggestion blocks, without running code or blocking except for malicious-looking changes.
npx skillsauth add shhac/skills pr-issue-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Review a GitHub pull request with one primary question:
Does this solve the stated issue?
This is a focused, context-aware review for PRs that ask for the user's review. It is not a full multi-perspective review or refactor audit.
The caller may specify review profile as passive, neutral, assertive, or aggressive. An explicit caller-specified profile always wins.
passive is the restrained unblocker profile.neutral is the balanced code-quality profile.assertive is the nitpicky maintainer profile. It uses a stricter reviewer posture.aggressive is the skeptical-but-friendly profile. Its goal is to find reasons not to approve while keeping the delivery clearly lighthearted.All profiles are read-only, stack-aware, and non-blocking except for malicious-looking changes.
If the caller does not specify profile, choose one at review start from PR metadata, comments, and existing reviews:
aggressive.assertive.aggressive.Never choose neutral or passive by fallback. Those profiles require an explicit caller request or continuity from a previous exact profile marker on the same PR.
AI-authorship signals include bot-like authorship, branch names, PR descriptions, commit messages, comments, or co-author lines that mention AI agents, LLMs, Codex, Claude, Copilot, ChatGPT, Devin, Cursor, or similar tooling. Treat this as a heuristic, not a claim about authorship.
High-risk signals include security, auth, permissions, payments, privacy, data deletion, migrations, backfills, schema changes, public API or protocol contracts, generated clients, background jobs, queues, batch workflows, retries, idempotency, production incidents, data correctness bugs, customer-visible bug fixes, broad file spread, or changes spanning several domains.
Previous reviews from this skill are identified by a top-level review body starting with:
🦎🍃
🦎⚖️
🦎🔎
🦎⚔️
The lizard marks the review as an AI review. The second emoji marks profile:
🍃 passive⚖️ neutral🔎 assertive⚔️ aggressiveComments without one of these exact opening markers cannot be matched by profile; continue through the fallback rules.
Load exactly one profile file:
passive -> read profiles/passive.mdneutral -> read profiles/neutral.mdassertive -> read profiles/assertive.mdaggressive -> read profiles/aggressive.mdThen read the selected persona file (see Review Persona below) and only the lens files listed by that profile. The persona file controls the top-level review voice only; the profile controls approval thresholds, strictness, and how readily to leave inline comments. Some lenses are shared and some are profile-specific. For lens files with an explicit Use this lens when... gate, load them when listed by the profile, but apply findings only when that gate matches the PR.
Personas live under personas/, one file each, with frontmatter naming the persona and its recommended profiles. A persona controls the line-one voice of the top-level review body and nothing else: it never changes approval thresholds, lens selection, severity, or blocking policy. The line-one emoji marker always comes from the selected profile, not from the persona.
The caller may specify any profile and persona combination, such as aggressive with cass. An explicit caller-specified persona always wins, even when paired with a profile outside the persona's recommended list.
If the caller does not specify a persona, select one deterministically from the candidate personas listed by the loaded profile file, in their listed order:
This gives different PRs different first reviewers and a fresh voice on each repeat review of the same PR. Do not carry the previous review's persona forward by continuity; the formula already decides, and a persona change between passes is intended.
Load exactly one persona file.
After loading the profile and lenses, inspect changed file paths, file extensions, imports/includes, config files, PR title/body, and discovered context for optional focus packs under references/focus-packs/.
Load only packs that clearly match the PR. Prefer zero to three packs; load more only when the PR genuinely spans several specialist domains. Focus packs add domain-specific review questions, but they do not change the selected profile, approval thresholds, severity scale, or P0-only blocking policy. Local repo guidance always wins over a generic focus pack.
Available focus packs:
grpc-protobuf.md — .proto, protobuf, gRPC, RPC schema evolution, generated RPC clients/serversgraphql-clients.md — GraphQL schemas/operations/fragments, client caches, pagination, generated GraphQL typesdatabase-migrations.md — migrations, schema changes, backfills, data repairs, indexes, rollbacksbackground-jobs-queues.md — workers, queues, retries, scheduled jobs, async processors, idempotent jobsauth-permissions.md — authentication, authorization, roles, scopes, permissions, tenant boundariesaccessibility.md — interactive UI, semantics, keyboard behavior, focus, labels, contrast, assistive technologylocalization.md — locale files, translation keys, pluralization, user-visible copy across languagescode-structure-boundaries.md — broad structural changes, module seams, accidental hubs, large functions/files, wrong-fit abstractionsUse references/diff-equivalence.md when deciding whether a changed head SHA still represents a diff already reviewed by this skill.
Every submitted review must include the hidden metadata described there so future automation can identify equivalent rebases or merge-refreshes without posting another review.
APPROVE or COMMENT. Use request-changes/blocking language only if the PR appears malicious or intentionally dangerous.🦎⚖️suggestion fenced blocks when the author can accept a quick fix directlyGiven a PR URL, extract {host, owner, repo, number}.
Create or reuse a temporary checkout for that repo:
tmp_root="${TMPDIR:-/tmp}/pr-issue-review"
repo_dir="$tmp_root/<host>/<owner>/<repo>"
mkdir -p "$repo_dir"
After startup metadata has been fetched, exact head-SHA deduplication has decided this head/profile might need review, and the in-progress reaction has been added, use shallow fetches in two stages:
references/diff-equivalence.md.If shallow fetch is unavailable or insufficient, fall back to GitHub PR diff/patch and file-content APIs/connectors. Ask the user before any full-history clone.
Recommended shape:
git init "$repo_dir"
cd "$repo_dir"
git remote add origin <repo-url> 2>/dev/null || git remote set-url origin <repo-url>
git fetch --no-tags --depth=1 origin +pull/<number>/head:refs/remotes/origin/pr-<number>
The leading + only allows the local temp ref to be refreshed after a PR force-push. It must never be used as permission to push to the remote.
Fetch the base ref or base SHA shallowly as needed to compute diffs and read base files:
git fetch --no-tags --depth=1 origin +<base-ref-or-sha>:refs/remotes/origin/base-<number>
If the repo already exists in the temp checkout, reuse it and fetch the latest PR head/base refs shallowly.
Read references/github-review-api.md for the exact gh commands to fetch startup metadata, add/remove the in-progress reaction, fetch full PR context, and submit the review.
Use GitHub metadata and static file reads only. Useful sources:
AGENTS.md, CLAUDE.md, package docs, style guides, localization rules, or testing conventionsDo not run CI locally. Existing CI output may be read if GitHub exposes it as logs or check summaries, but do not trigger or rerun jobs.
Look for references in PR title, body, branch, comments, and review comments:
If the following skills are available, use them for the matching references:
lin for Linear issues, comments, projects, and linked PRsagent-slack for Slack threads or messagesagent-notion for Notion pages or database entriesIf a referenced source cannot be fetched, note that in the review context instead of blocking the review.
Use private remote context to inform the review, but do not paste sensitive or unnecessary private details into GitHub. Cite source names and summarize only what is needed to explain the review.
Write discovered context into untracked cache files in the temporary checkout so repeated review loops do not re-fetch the same remote context:
.ai-cache/REVIEW_CONTEXT.md
.ai-cache/<reference-name>.md
Examples:
.ai-cache/linear-ENG-1234.md
.ai-cache/slack-C12345678-1712345678.123456.md
.ai-cache/notion-project-brief.md
.ai-cache/github-issue-42.md
REVIEW_CONTEXT.md should summarize:
Do not commit .ai-cache/.
profiles/.{head SHA, profile} was already reviewed, stop without adding a reaction.references/diff-equivalence.md, compute the current diff and startup context fingerprints, and compare them with hidden metadata from prior reviews by this skill on the same PR/profile.references/focus-packs/.Use a PR-level eyes reaction as the in-progress signal when the GitHub API supports reactions.
.ai-cache/.Submit a GitHub review, not a loose collection of unrelated comments. Use a single review submission carrying the top-level body and all inline comments, as shown in references/github-review-api.md.
The body must start with one of:
🦎🍃 for passive🦎⚖️ for neutral🦎🔎 for assertive🦎⚔️ for aggressiveLine 1 should be the emoji marker, the loaded persona name, and a short, slightly funny verdict about next steps. Do not repeat the GitHub review state (Approved, Commenting, or similar) because GitHub already shows that. Use the loaded persona file for the line-one voice and examples.
Use this shape:
<emoji marker> <Persona>: <profile verdict sentence>.
Why:
- <severity>: <short finding title>. See inline comments.
- <severity>: <short top-level-only finding title, if no inline anchor exists>.
Recommendation: <recommended next step for this top-level-only finding>.
- ℹ️ FYI: <context-only note, if useful>.
<details>
<summary>Review context</summary>
Focus checked:
- Issue fit
- Local repo guidance
- <loaded lens or domain focus>
Context checked:
- PR description and diff
- Linear ENG-1234
- Slack thread ...
Previous findings:
- Resolved: ...
- Still open: ...
- New: ...
Notes:
- ...
</details>
<!-- pr-issue-review:v1 profile=<profile> head=<headRefOid> diff=<diff-fingerprint> context=<context-fingerprint> -->
Keep it concise. Treat the top-level body as a severity-ordered index and confidence summary, not the primary home for detailed findings. Keep line 1 and Why: visible. If a finding has a stable diff position, put the evidence and recommended next step inline and reference it briefly from the top-level body. For top-level-only findings, keep the finding sentence short and put the action on an indented Recommendation: line under that bullet so the recommendation is easy to scan without adding another section. If there are no meaningful concerns, say that the PR appears to solve the stated issue and why.
Use one <details> block titled Review context for supporting audit-trail sections when they are non-trivial: Focus checked, Context checked, Previous findings, and Notes. Skip the <details> block when the review is already short. Do not hide actionable findings, inline findings, or suggestion blocks inside collapsed sections.
Always append the hidden metadata line from references/diff-equivalence.md as the final line of the top-level review body. It must not contain findings, severity, or private context.
Use Focus checked to name the main axes applied by the loaded profile and changed area, such as issue fit, local repo guidance, failure modes/scale, user-visible text/localization, batch failure behavior, runtime contracts, testability, or conventions.
If a previous review from this skill exists on the same PR, include Previous findings when useful. Summarize what was resolved, what remains open, and what is new at the current head SHA.
Prefix actionable findings in the top-level body and inline comments with a severity marker:
🚨 P0: malicious-looking or intentionally dangerous behavior. This is the only severity that permits REQUEST_CHANGES.⚠️ P1: a real reason not to approve yet (likely issue-fit gap, correctness bug, safety problem, or missing behavior that matters).🔧 P2: should-fix quality or testability concern; a strong suggestion, but not enough by itself to block all profiles.💅 P3: a nit worth landing (naming, local cleanup, or an easy fix that makes the merged code a better example to follow).💭 P4: pure preference or alternative; take it or leave it. Never counts against approval in any profile.ℹ️ FYI: context, limitation, stack note, or observation with no action implied.Use the loaded profile's approval threshold when deciding between APPROVE and COMMENT. Severity affects that decision and the tone of the review, but it does not change the blocking policy: only 🚨 P0 can use REQUEST_CHANGES.
Failing or pending CI that is already a merge blocker does not count as a review finding for profile approval thresholds. If CI is the only reason not to approve, approve and mention that the PR should be good to go once CI is fixed.
Example top-level finding bullets:
Why:
- ⚠️ P1: I could not verify issue fit because this non-trivial PR has no summary or linked context.
Recommendation: Add a short PR body describing the intended behavior change, scope, and any follow-up or stack context.
- ⚠️ P1: Retry behavior from the linked issue still appears uncovered. See inline comments.
- 🔧 P2: The parsing helper is hard to exercise directly. See inline comments.
- 💅 P3: I left a naming suggestion inline.
Use inline comments for specific, line-level findings. Prefer them over burying concrete callouts in the top-level body.
Good inline comments:
suggestion block for direct quick wins when the replacement is exact, local, and safe<details> block titled Why this matters when the comment would otherwise be bulkyExample:
⚠️ P1 — Archived records are still excluded here.
**Recommendation:** Include archived records here, or explain why that path is handled elsewhere.
```suggestion
return records.filter((record) => record.active || record.archived)
```
<details>
<summary>Why this matters</summary>
Evidence: the linked issue mentions archived records, but this filter only keeps active records.
Impact: the PR can still miss the records the user asked to recover.
</details>
Keep inline findings action-first. Do not hide the severity, finding title, **Recommendation:** line, or suggestion block inside collapsed sections. For short comments, skip <details> and keep the evidence/impact inline. If the recommendation is exact, local, and safe enough for GitHub to apply directly, put the suggestion block immediately after the recommendation. If the fix is not that clear, use a visible **Recommendation:** line without a suggestion block.
Avoid inline comments for broad preferences or speculative rewrites. The loaded profile determines whether style, convention, naming, or decomposition nits are in scope. If a finding cannot be anchored cleanly to a changed line, keep it in the top-level body with the same severity, visible recommended next step, and enough evidence/impact to justify the finding.
When leaving inline review comments:
For suggestion blocks:
references/github-review-api.md. Do not estimate line numbers from the unified diff.suggestion block.APPROVE: The PR appears to solve the stated issue and all findings are within the loaded profile's approval threshold.COMMENT: The PR may be incomplete, ambiguous, or has findings above the loaded profile's approval threshold.REQUEST_CHANGES: Only for malicious-looking or intentionally dangerous changes.If the only reason not to approve is a failing or pending CI check that is itself a merge blocker, use APPROVE and mention that the PR should be good to go once CI is fixed. Do not duplicate branch protection by withholding approval for CI alone.
Do not approve a non-trivial PR when the reviewer cannot determine why it exists. Missing PR body alone is not the finding; unverifiable issue fit is.
Do not use "must fix" unless the review decision is REQUEST_CHANGES.
When running in a loop for PRs requesting the user's review:
commit_id from the GitHub reviews API.references/diff-equivalence.md; if it finds the same effective diff and same startup context as a previous review on this PR/profile, remove the in-progress reaction and stop without posting a review.passive, neutral, assertive, and aggressive as separate review profiles; a PR can receive one review per {head SHA, profile} unless diff-equivalence deduplication suppresses a rebase or merge-refresh duplicate..ai-cache/ context for the same repo.development
Audit a codebase's module boundaries — enumerate modules, map their seams (import edges between modules), produce a layered topology diagram, and classify each module as narrow, hub-by-design, or accidental hub (with separate flags for cycles, layer violations, and uncertain import graphs). Outputs a diagram plus a flagged-for-review list; does not change code. Use when assessing whether abstractions live at the right boundaries, before/after a refactor to verify the boundaries improved, or when an unfamiliar codebase needs an architectural map. Not for intra-module refactoring (see improve-code-structure), bug hunting, or feature work.
testing
Investigate and solve problems using a team of specialist agents. Use when facing complex, multi-faceted problems that benefit from parallel research and structured implementation.
tools
Sync a forked repository with its upstream. Fetches both remotes, shows divergence, resets shared branches to upstream, re-merges local-only branches, cleans up branches already merged upstream, and pushes. Use when upstream has accepted PRs or moved ahead and you need to bring your fork in line.
data-ai
Manage stacked branches — rebase cascades, detect landed PRs, show stack status. Use when branches are stacked (B on A on main), trunk has advanced, a mid-stack branch changed, or a PR has landed and descendants need rebasing. Lightweight alternative to Graphite that infers the stack from git history.