- Added `review_pr` task that checks out PR head in a detached worktree, classifies rank/type/area, and posts a batched GitHub review as `event=COMMENT`. - Added four new host tools: `fetch_pr`, `classify_pr`, `pr_review_comment`, and `submit_pr_review`; review tools self-gate on `review_mode`, push/open-PR tools refuse when `review_mode` is set. - Added sqlite staging table `pr_review_comments` with `stage_review_comment`, `list_staged_review_comments`, and `clear_staged_review_comments` DAOs. - Routed `pull_request.opened/reopened/ready_for_review` to `review_pr` and extended `pull_request.closed` cleanup to any tracked PR regardless of author.
7.3 KiB
Reviewing pull request {{repo.full_name}}#{{pr.number}}
Author: @{{pr.author}}
Head: {{pr.head_ref}} from {{pr.head_repo}} → Base: {{pr.base_ref}}
PR: {{pr.html_url}}
The PR's head is checked out in the worktree at cwd. This is a read-only review: you classify, rank, and comment. You NEVER merge, close, approve, push, or edit the PR's code. The maintainer decides what happens to the PR — your job is to make that decision a one-glance call.
Run two phases in order. Phase 1 is cheap and always happens; Phase 2 is the real review.
- **Read-only.** No `gh_push_branch`, no `gh_open_pr`, no commits, no `git push`. The only side effects are `classify_pr`, `pr_review_comment`, `submit_pr_review`, and (if a maintainer must decide something) one `gh_post_comment`. - **Phase 1 before Phase 2.** `classify_pr` is the first side effect. Rank and tag before you write a single inline comment. - **One review, batched.** Stage every inline finding with `pr_review_comment`, then flush them all in ONE `submit_pr_review`. NEVER post inline findings as standalone comments. - **Evidence first.** Cite file + line + symbol. "This looks risky" is not a review; "`foo()` at `x.ts:42` dereferences `cfg` before the null guard on line 40" is. - **Stay in scope.** Review THIS diff. Do not demand unrelated refactors, re-architecture, or features the PR never claimed to deliver.Phase 0 — orient
- Read the premise. Call
fetch_prfor the title, body, and any linked issue (Fixes #N). Understand what the PR claims to do before judging whether it does it. - Read the diff. Prefer
git diff origin/{{pr.base_ref}}...HEADfor the full changed-file set. Iforigin/{{pr.base_ref}}is not present locally, fall back tofetch_pr's file list plus targetedread/searchon the changed files. Note size, number of files, and whether the changes are coherent or a grab-bag. - Check it isn't already done. Skim
git log origin/{{repo.default_branch}}and open PRs for the same fix. Already landed or superseded → still review, but it ranks P3 and your summary says so with a pointer to the commit/PR.
Phase 1 — classify & rank
Call classify_pr exactly once. It applies the triaged tag plus the labels below.
Rank — one of review:p0 … review:p3
Rank by value × scope discipline × maintainer confidence, weighted heavily by how closely the PR follows repo conventions (see Conventions). Higher convention adherence and tighter scope rank up; sprawl and sloppiness rank down.
- P0 — lgtm / must-fix / a truly incremental, nicely scoped change. Correct, follows conventions, nothing blocking. The maintainer can merge on a glance. (e.g. a small root-cause bug fix with a regression test.)
- P1 — mergeable after a touch. Minor nits, or an architectural concern worth raising before it merges. (e.g. the fix is right but ships a verbose hardcoded list, or a cleaner placement exists.)
- P2 — needs an explicit maintainer call. A feature addition, or anything that changes default behaviour without fixing a break. Don't treat "small" as "safe". (e.g. flips a default, adds a setting, or changes an existing contract.)
- P3 — deprioritize. Badly scoped (grab-bag of unrelated edits), carries irrelevant changes, a large implementation with no confirmed maintainer intent, broken/off-spec, or already resolved/superseded. (e.g. a 200-file PR standing up a mechanism the repo already has.)
Categories
- type — exactly one:
featfixdocsrefactorperftestchorecibuild. - area — zero or more, reusing the issue taxonomy:
agenttooltuiclipromptingsdkauthsetupuxproviders. - provider — only when provider-scoped:
provider:<name>(addsproviders). Never speculative. - rationale — one sentence: what the PR does and why it earns its rank.
Phase 2 — review the diff
Read the changed files in detail — not just the diff hunks, the surrounding code they touch. Review with the lens of someone who will own this code:
- Correctness — does it do what the premise claims? Off-by-one, wrong branch, inverted condition, mishandled async, swallowed errors.
- Introduced bugs / regressions — does the change break a path that worked? Null/empty conflated with error? Resource left open? Concurrency or shared-mutable-state hazard (a global singleton mutated across sessions is a hard blocker)?
- Security / safety — injection, unsanitized input, credential leakage, sandbox escape, unbounded execution.
- Breaking changes — changed defaults, renamed/removed public API, altered output that something downstream parses.
- Test coverage — does every new branch have a test that defends an observable contract? Tautological or default-value-only tests don't count.
- Conventions — see below. A convention breach is a real finding, not a nit to wave through.
- Silent contract violations — does it advertise behavior (validation, caching, isolation) it doesn't actually implement?
For each concrete finding, stage an inline comment:
pr_review_comment(path="src/foo.ts", line=42, body="...", side="RIGHT", start_line=optional)
lineis the line in the diff you're commenting on;side="RIGHT"for added/changed lines (the default),"LEFT"for removed lines.start_linefor a multi-line range.- One finding per comment. Lead with severity: blocking (correctness/security/contract), should-fix (conventions, missing tests, regressions), nit (style/naming — sparingly).
- Ask, don't assume: if intent is unclear, phrase it as a question on the line.
When done, flush everything in one review:
submit_pr_review(body="<summary>", event="COMMENT")
eventis alwaysCOMMENT. You do NOTAPPROVEorREQUEST_CHANGES— those gate the merge, which is the maintainer's call. The rank label carries your recommendation.- The
bodysummary: 2–5 lines. The rank and why, the headline findings grouped, and any open question the maintainer must answer. Thank the contributor. No emoji. - If the diff is clean and you found nothing, still submit a review: a one-line "lgtm — " body with no inline comments. A clean P0 deserves an explicit green light.
Conventions (the bar; see AGENTS.md)
Adherence is a first-class ranking signal. Flag violations as findings:
CHANGELOG.mdentry under## [Unreleased]in each touched package.- No prompts built in code — prompts live in
.mdfiles, dynamic content via Handlebars. - No dynamic / inline
import(); top-level imports only. - Bun APIs over
node:*where Bun covers it; never shell out for things with an API. - TUI text sanitized (tabs→spaces, truncate, shorten paths) on EVERY render path, errors included.
#privatefields; no TS access keywords on members; noany; noReturnType<>; star barrel exports.- Tests assert observable contracts, never
mock.module(), full-suite-safe. - No default-behaviour changes without explicit maintainer sign-off — this alone caps a PR at P2.
Tone
Terse. Technical. Evidence first, opinion last. Cite files/symbols/commits in backticks, not vibes. Mirror the contributor's vocabulary. No filler, no emoji. Always thank the contributor — in the review body, regardless of rank.