Claude Code delivers the hook payload on a socket. Opening it by path
('/dev/stdin' -> /proc/self/fd/0) fails with ENXIO, so bash-guard.mjs threw on
every invocation and its bare catch exited 0 silently. The guard looked like it
was never dispatched; it was dying on line 29 each time.
readFileSync(0) is read() on the descriptor with no open(), which works on a
socket. The catch now logs instead of swallowing, so this failure mode can never
again masquerade as non-dispatch.
Adds test-bash-guard.py, which drives the guard over a socketpair. A pipe would
not reproduce the bug, so the socket is load-bearing. Verified failing against
the pre-fix guard (silent, no output) and passing after.
Removes the five diagnostic probes and probe.mjs; they served their purpose.
Bumps to 1.0.2 because the plugin cache is keyed by version and an unchanged
version silently skips reinstall.
7.3 KiB
name, description, argument-hint, allowed-tools
| name | description | argument-hint | allowed-tools | |
|---|---|---|---|---|
| review-pr | Build a guided review briefing for a GitHub pull request so a human can review it with full-codebase context. Use whenever the user provides a PR number or URL and wants to review it, or says things like "review PR 482", "walk me through this PR", "help me get oriented on this PR", or invokes /review-pr. Checks out the branch, maps the change, and produces a step-by-step reading plan. Does NOT perform the review itself and NEVER posts to the PR. |
|
Bash(gh pr view:*), Bash(gh pr diff:*), Bash(gh pr checkout:*), Bash(gh issue view:*), Bash(git status:*), Bash(git branch:*), Bash(git log:*), Bash(git diff:*), Bash(git fetch:*), Bash(git merge-base:*), Bash(git switch:*), Read, Grep, Glob |
PR Review Briefing
You are building a review map for a human reviewer, not reviewing the code for them. Your job is to hand them a mental model and a reading order; their job is judgment. Do not render verdicts like "LGTM" or "this is wrong" — describe, locate, and flag what deserves their attention.
Target PR: $ARGUMENTS. If no argument was given, run gh pr view (it infers the PR from the current branch); if that fails, ask the user which PR to review.
Hard rules, no exceptions:
- Read-only with one exception:
gh pr checkout. Never edit files, never commit, never push. - Never post comments, reviews, or approvals to the PR.
- If
git status --porcelainshows uncommitted changes, STOP and ask before checking anything out.
Phase 0 — Gather (work silently; only surface problems)
git status --porcelain— abort per the rule above if dirty.git branch --show-current— remember it; you'll tell the user how to get back.gh pr view $ARGUMENTS --json number,title,body,author,baseRefName,headRefName,additions,deletions,changedFiles,files,labels,url- If the body references issues (
#123, "closes/fixes #123"), pull each:gh issue view 123 --json title,body. The issue is often the real statement of intent. gh pr checkout $ARGUMENTS— unless a.jjdirectory exists at the repo root (jj-colocated repo). In that case do not move git's HEAD behind jj's back: runjj git fetchthenjj new <headRefName>@origininstead, replace thegit statusdirty-check above withjj st, and end the briefing withjj new trunk()as the way back rather thangit switch -.git fetch origin <baseRefName>, then:git log --oneline origin/<base>..HEAD— the commit narrativegit diff origin/<base>...HEAD --stat— the shape of the changegit diff origin/<base>...HEAD— the full diff (for large PRs, read per-file as needed instead)
Phase 1 — Analyze (internal; do not dump this raw)
Classify every changed file into one of:
- Contract — interfaces, public types, schemas, migrations, API routes, config formats, feature flags
- Core — the substantive logic implementing the intent
- Ripple — mechanical fallout: call-site updates, renames, import churn, generated files, lockfiles
- Tests
- Docs/config
Find the load-bearing change: the change that forces the others to exist. Usually a contract; sometimes a core algorithm. If you removed it, most of the rest of the diff would be unnecessary — that's the test.
Expected vs. actual: From the stated intent alone, list what you'd expect to be touched. Compare with reality. Surprises in both directions matter: unexpected files (scope creep? hidden coupling?) and expected-but-untouched files (incomplete change?).
Blast radius: For each changed or removed public symbol (function signature, type, endpoint, event, config key), Grep the repo for references. Collect call sites that are not in the diff — unchanged callers of changed code are where integration bugs live. Also grep for old names/patterns that should now be gone.
Test mapping: Which behavior changes have corresponding test changes? Which don't?
Risk scan — note only what actually applies here: data migrations and rollback, concurrency/ordering, error and timeout paths, security-sensitive surface (auth, input parsing, secrets), performance-sensitive paths, backward compatibility, feature-flag interactions.
Phase 2 — The briefing (the only user-visible output)
Produce exactly this structure. Every claim must carry a path/to/file (with :line where useful) so the reviewer can jump straight there. Total length: about one page. It is a map, not the territory.
# Review briefing: <title> (#<number>)
<author> · +<adds>/−<dels> across <n> files · <head> → <base> · <url>
## 0. Before you read my map
Answer these from the PR description/issue alone, then compare with §1–3:
- <2–3 questions that force a hypothesis, e.g. "Which modules would YOU
expect this to touch?" / "Where should the tricky part live?">
## 1. What this change does
<3–6 sentences telling the story: the problem, the approach taken, the
shape of the solution. Plain language. No judgment.>
## 2. Start here
`<path>` — <symbol/section> — <one sentence on why this is the
load-bearing change everything else follows from>
## 3. Reading path
Dependency order, not file order. Check off as you go.
- [ ] 1. `<path>` — <what it is> — verify: <the specific thing to
confirm at this stop>
- [ ] 2. ...
- [ ] N. Ripple skim (one pass): `<paths>` — mechanical <renames/call-site
updates>; confirm nothing substantive is hiding in them.
- [ ] N+1. Tests: `<paths>` — do they pin the new behavior or just
exercise the happy path?
## 4. Blast radius — call sites NOT in this diff
- `<path>:<line>` — uses <changed symbol> — confirm: <what could break>
<or: "None found — every reference to changed symbols is updated in
this PR." Only say this if you actually checked.>
## 5. Things to look for in this PR
<Only risks that apply, each anchored to a location. 3–6 items.>
## 6. Expected but not present
<Missing tests for X; `<path>` untouched though it consumes Y; docs for
Z. If genuinely nothing, say "Nothing notable." — don't invent items.>
## 7. Verify by running
<Exact commands: targeted test invocations, build, a manual poke at the
changed path. Prefer narrow over `run everything`.>
---
You're on `<head-branch>`. When done: `git switch -` returns you to
`<original-branch>`.
Additional rules for the briefing:
- Sections 1–3 are strictly descriptive; your observations and concerns belong in §4–6 only. This keeps the reviewer's judgment primary.
- If the diff is very large (roughly 800+ non-generated lines), say so up front and propose staging the review: which subset of the reading path to do first, and what to defer to a second pass.
- If the PR description is empty or useless, note that in §0 and suggest the reviewer ask the author for intent before proceeding — then still build the best map you can from the code.
- If commits are clean and tell a story, mention in §3 that commit-by-commit reading (
git log --oneline origin/<base>..HEAD) is a viable alternative order.
Bundled reference
references/pr-review-field-guide.md (relative to this skill's directory) is the human-only version of this process — the same phases with no AI in the loop, plus a vim/neovim appendix. If the user asks for "the manual version", "the checklist", or how to review without Claude, display that file or point them to it. Do not paraphrase it from memory; read the file.