Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/rules/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ These project-level rules govern all operations inside this workspace. Antigravi
- `webjs-scaffold-sync`: Trigger when changing the CLI generators, the scaffold templates, or the agent teaching skill.
- `webjs-blog-write`: Trigger when writing, drafting, or editing a WebJs blog post under `blog/`.
- `webjs-instagram-post`: Trigger when publishing an SEO post to the WebJs Instagram account.
- `pr-review`: Trigger when asked to review a PR. The review runs INLINE in the session that was asked, never in a subagent and never as a multi-round cycle, and posts through the GitHub review API as one review object (a summary plus line-anchored comments with suggestion blocks). It only reviews: it does not fix findings, resolve threads, or wait on CI.
- `use-railway`: Trigger when interacting with Railway deploys.
- The framework teaching skill at `.agents/skills/webjs/` is a real directory rather than a symlink, and is the reference for building WebJs apps rather than a workflow trigger.
- Always use the `view_file` tool on the matched skill's `SKILL.md` before executing its tasks.
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/pr-review
22 changes: 10 additions & 12 deletions .claude/hooks/route-skills.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,22 +192,20 @@ if printf '%s' "$lc" | grep -q 'instagram' \
add_match "webjs-instagram-post: the request is to publish to the WebJs Instagram account. Invoke the webjs-instagram-post skill. Every post is SEO-only, so ALWAYS create a fresh branded image plus a keyword-rich caption, host the JPEG at a public HTTPS URL, and CONFIRM the image and caption with the user before the public publish. Never print or commit the access token."
fi

# --- code-review: review the diff before a PR is ready ------------------
# --- pr-review: review a PR inline, posted via the GitHub API -----------
# Triggers: review the PR/diff/branch/changes, code review, look it over
# for bugs. Reviewing every change before it is marked ready is a standing
# expectation, and a review is the CYCLE the webjs-start-work skill
# defines: one reviewer over the whole diff, a minor-or-must-fix call, and
# at most one delta round on the fixes, escalating to delta rounds plus a
# final whole-diff review only when the change shows it needs them. This
# directive and the skill must not resolve a case differently, which they
# once did for the final fix-check; test/hooks/review-loop-exit.test.mjs
# asserts the shared wording of the rules that decide when the cycle ends.
# code-review is a built-in Claude Code skill (no in-repo SKILL.md, so the
# portability test that guards project skills does not cover it).
# for bugs. The owner reviews every PR themselves before merge; when they
# ask THIS agent for a review, the pr-review skill runs it like a human
# reviewer working over the GitHub API instead of the dashboard: one
# inline read, one posted review object (summary plus line-anchored
# comments with suggestion blocks). Review only: no reviewer subagent,
# no multi-round cycle, no fixing findings, no waiting on CI. The skill
# is agent-agnostic (plain gh/REST) and committed at
# .claude/skills/pr-review, exposed cross-agent via .agents/skills/.
if has '(review|audit) (the |my |this )?(pr|diff|branch|change|changes|code|commit)' \
|| has 'code ?review' \
|| has '(review|look) .{0,20}(over )?for (bug|issue|correctness|regression)'; then
add_match "code-review: the request is to review code. Invoke the code-review skill (it reviews the diff for correctness bugs plus reuse and simplification). A review of a PR runs the webjs-start-work review cycle, which is ONE fresh reviewer over the whole diff, never a fleet, and which defaults to its FAST shape because every round costs about 10 minutes and the owner should not have to ask for a short cycle. Fast shape: round 1 reads the whole diff; only a MUST-FIX fix buys a round and it buys exactly ONE, delta-scoped to those fix commits alone; a round whose fixes were all minor ends the cycle instead; then stop. A clean or minor-only round 1 therefore finishes with ONE review, which is the intended common case. Speed comes from running fewer rounds, never from lowering the bar inside a round. Escalate to the THOROUGH shape when the owner asks for a thorough or full review, or round 1 produced two or more must-fix findings or the one delta round produced any, or the diff touches the serializer, SSR or action dispatch, auth or session, the client router, or the elision analyser. Thorough shape: each later round is delta-scoped to the previous round's fix commits, and the first round that produces no fixes, whether it found nothing must-fix or everything it found was rejected or deferred, buys a FINAL review over the whole diff again. The code-review skill's findings feed that cycle as auxiliary input, not as a round of it. Every reviewer is spawned with the Agent tool as subagent_type general-purpose, model opus (Opus 5, never fable), run_in_background true, and isolation worktree. Judge each finding MINOR or MUST-FIX by SURFACE, never by importance: must-fix when it touches source, a test's ability to observe the defect it claims to cover, or a factual claim about runtime behavior in docs; minor for wording, naming, comment style, and nits about the review artifacts; when it could go either way it is must-fix. Only a FIX buys another round, since only a fix changed the branch, and a delta chain that keeps producing fixes stops after the fifth delta round, unfinished, rather than continuing; a rejection buys one refuter instead, and a deferral and a minor finding buy nothing. After the final review, a must-fix finding is fixed and gets ONE delta check of that fix alone; the cycle ends when nothing must-fix is left open, meaning the check came back with nothing or there was no fix to check because every must-fix finding the final review raised was rejected or deferred; a check that does find something must-fix gets that fixed and one more check of the same shape, and only if that one also finds something must-fix do you stop and report the PR unfinished. Do not file follow-up issues for review findings that are out of scope; report them to the owner, who decides, and fold a small same-file tweak into the PR. Never report the PR ready off a round that found something must-fix."
add_match "pr-review: the request is to review code. Invoke the pr-review skill and perform the review YOURSELF, inline in this session. NEVER spawn a reviewer subagent and NEVER run a multi-round review cycle. When the target is a pull request, post the review through the GitHub review API as ONE review object, a summary plus line-anchored comments that highlight the code to fix and carry suggestion blocks where a concrete replacement is obvious, exactly as the skill specifies. The reviewer ONLY reviews: it does not fix findings, does not resolve threads, and never waits on or reports CI. For a local diff with no PR, review inline and report the findings in the conversation instead. The owner decides what gets fixed, and fixing is separate work on a separate ask."
fi

# --- verify: prove the change works by running the app ------------------
Expand Down
173 changes: 173 additions & 0 deletions .claude/skills/pr-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
name: pr-review
description: Review a GitHub pull request the way a human reviewer would, entirely inline, and post the review through the GitHub review API as ONE review object, a summary plus line-anchored comments that highlight the code to fix and carry suggestion blocks where a concrete replacement is obvious. Trigger whenever the user asks to review a PR ("review the PR", "review #123", "look over this pull request", "review the branch/changes" when the branch has an open PR). The reviewer ONLY reviews. It never fixes findings, never waits on or reports CI, never resolves threads, and never delegates to a subagent or another agent.
when_to_use: |
Examples that should trigger this skill:
"review the PR"
"review #1453"
"can you review this pull request"
"review my changes" (when the branch has an open PR)
"look over the diff for bugs" (when the diff is a PR)
Do NOT trigger for: fixing review findings (that is normal branch work,
a separate task from the review), responding to someone else's review
comments, merging, or reviewing a local uncommitted diff with no PR
(review that inline and report the findings in the conversation).
---

# Review a pull request (inline, posted via the GitHub API)

Review a PR like a human reviewer who happens to use the GitHub API
instead of the dashboard. Same output a person would produce on
github.com: one submitted review carrying a summary and inline comments
anchored to the exact lines, with GitHub suggestion blocks where the fix
is concrete enough to spell out.

This skill is agent-agnostic on purpose. Everything below is plain `gh`
CLI (or raw REST with any HTTP client and a token), no harness-specific
tools, so it works the same from any agent or harness that can run shell
commands.

## The contract (read first, all four are hard rules)

1. **Inline.** The agent that received the ask performs the review
itself, in the same session. Never spawn a reviewer subagent, a
fleet, or a background task for it, and never run a multi-round
review cycle. One read over the whole diff, one posted review, done.
2. **Review only.** The reviewer never fixes what it finds: no commits,
no pushes, no code edits, no resolving of threads. Findings are the
deliverable. Fixing them is separate work for whoever owns the
branch, on a separate ask.
3. **No CI.** Never wait on, read, or report CI or check status. Checks
are the merge gate's business, not the reviewer's, and a review that
stalls on a pending check has failed its one job of being fast
feedback on the code.
4. **Read-only on git.** Reviewing needs no checkout. Do not run any
git command that changes branch, HEAD, the index, or the working
tree (`checkout`, `switch`, `reset`, `restore`, `stash`, `merge`,
`rebase`, `clean`). Read-only inspection (`git log`, `git show`,
`git diff`, `git status`, `git blame`) is fine.

## Steps

### 1. Resolve the PR

An explicit `#N` wins. Otherwise resolve the current branch's open PR
over REST (owner/repo from the git remote):

```sh
BRANCH=$(git branch --show-current)
gh api "repos/<owner>/<repo>/pulls?head=<owner>:$BRANCH&state=open" \
--jq '.[0].number'
```

If nothing resolves, ask the user which PR they mean rather than
guessing.

### 2. Fetch what a reviewer reads

All over REST (in repos that budget GraphQL, these cost nothing there):

```sh
gh api repos/<owner>/<repo>/pulls/<N> \
--jq '.title, .body, .head.sha, .head.repo.full_name, .user.login'
gh api repos/<owner>/<repo>/pulls/<N> -H "Accept: application/vnd.github.diff"
```

Then read every touched file in its PR-head state, not just the hunks,
so each edit is judged in context. Read locally if the branch happens to
be checked out, else through the contents API:

```sh
gh api "repos/<head-repo>/contents/<path>?ref=<head-sha>" --jq .content | base64 -d
```

Address the HEAD repo at the HEAD sha, not the base repo at a branch
name. A fork PR's branch does not exist in the base repo, so a
branch-name ref there 404s, and a sha pins every read to the exact
commit the review is posted against. Both values come from the first
call (`.head.repo.full_name`, `.head.sha`); on a same-repo PR the head
repo IS `<owner>/<repo>`, so one form covers both cases.

Capture `head.sha` from the first call for `commit_id` too.

### 3. Review the whole diff, yourself

Judge the change against what it claims to do (title + body) and the
project's own stated rules (root `AGENTS.md`, `CONVENTIONS.md`,
per-package variants, when present). Correctness first: wrong behavior,
broken edge cases, a test that cannot observe the defect it claims to
cover, security problems, a doc stating something the code does not do.
Problems, not style nits, and no checklist narrowing: read the diff and
report whatever is actually wrong. A finding states the problem the way
a reviewer flags it, not the fix baked in as if already applied.

### 4. Compose ONE review object

The summary plus every inline comment go in a single review, which is
what makes GitHub render them as a grouped unit with the
"reviewed these changes" trail. Shape:

```json
{
"commit_id": "<head-sha>",
"event": "COMMENT",
"body": "<summary: the overall take, findings that could not be line-anchored, or a short note that it is clean>",
"comments": [
{ "path": "src/x.js", "line": 42, "side": "RIGHT",
"body": "<the problem on this line>" },
{ "path": "src/y.js", "start_line": 10, "start_side": "RIGHT",
"line": 14, "side": "RIGHT",
"body": "<a multi-line finding, highlighting the whole range>" }
]
}
```

- **Highlight a range** with `start_line` + `line` (both `side: RIGHT`
for added code, `LEFT` for deleted). A single-line comment omits
`start_line`.
- **Suggest the fix** where a concrete, self-contained replacement
exists, using a GitHub suggestion block in the comment body. The
block replaces EXACTLY the commented line range, so it must contain
the full replacement for those lines, correctly indented:

````markdown
This drops id 0 too. Strict-compare against null instead:

```suggestion
if (id !== null) {
```
````

The user can then apply it with one click. Suggest only on lines the
diff adds or keeps (`side: RIGHT`), never on pure deletions, and skip
the suggestion when the real fix is bigger than the commented range;
state the problem and sketch the fix in prose instead.
- Every `line` must be part of the PR diff (a changed line or nearby
hunk context), or the API rejects the whole review. A finding on an
untouched line goes path-level into the summary body.
- Build the JSON with a real serializer (`jq -n`, python), never by
interpolating into a shell string, which mangles quotes and code
spans.

**Verdict.** `event: "COMMENT"` on a PR you (the account posting)
authored, since GitHub forbids `APPROVE` / `REQUEST_CHANGES` on your
own PR, and that is the common case when the agent reviews the account
owner's work. On someone else's PR, pick the honest verdict:
`APPROVE`, `COMMENT`, or `REQUEST_CHANGES`.

**Voice.** First person, plain, the way a person reviews code. Terse
inline findings, a summary that may go broad (what the change does
well, the one thing that matters). No AI or agent framing, no process
narration, no machinery tells (test counts, check status).

### 5. Post it and report back

```sh
gh api -X POST repos/<owner>/<repo>/pulls/<N>/reviews --input review.json
```

A clean review still posts: a short summary saying it is clean, with no
inline comments. Then tell the user the outcome in one or two
sentences, with the review's URL and the finding count. Stop there. No
fixing, no thread resolution, no follow-up issues, no re-review unless
they ask again.
Loading
Loading