Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Address feedback left on a GitHub pull request: fetch unresolved review threads, make agreed Elixir/Phoenix code fixes, reply, and resolve. Use for a PR URL/number or reviewer comments. NOT for pre-PR review, findings triage, or CI monitoring.
.claude/skills/oliver-kriska-pr-review/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 24% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 100% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 152% | 0% |
| case-21 | ✗→✓ | ▲ Improved | 90% | 0% |
Close the review loop: fetch unresolved threads → fix → reply → resolve. GitHub's isResolved is the state — re-runs are idempotent, handled threads drop out automatically.
/phx:pr-review 42 # Triage unresolved threads on PR #42
/phx:pr-review 42 --fix # Triage + apply approved code fixes
/phx:pr-review https://... # Full URL also works (repo parsed from URL)
/phx:pr-review 42 --bots-only # Triage only CI bot threads (Copilot, Codex...)
/phx:pr-review 42 --no-resolve # Reply but leave threads opengh pr view "$PR" --json number,title,state,baseRefName,headRefName,url,author (accepts number or URL; URL also yields owner/repo). Then fetch ALL review threads with thread IDs + resolved status — REST alone cannot do this:
bashcat > /tmp/review_threads.graphql <<'GQL' query($owner:String!, $repo:String!, $pr:Int!, $cursor:String) { repository(owner:$owner, name:$repo) { pullRequest(number:$pr) { reviewThreads(first:50, after:$cursor) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line originalLine comments(first:20) { nodes { databaseId body createdAt author { login __typename } } } } } } } } GQL gh api graphql --paginate -F owner="$OWNER" -F repo="$REPO" -F pr="$PR" \ -F query=@/tmp/review_threads.graphql \ --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {threadId: .id, isOutdated, path, line: (.line // .originalLine), firstCommentId: .comments.nodes[0].databaseId, author: .comments.nodes[0].author.login, isBot: (.comments.nodes[0].author.__typename == "Bot"), body: .comments.nodes[0].body}'
Also fetch review summaries (gh api "repos/$OWNER/$REPO/pulls/$PR/reviews") — they are NOT threads and cannot be resolved; surface CHANGES_REQUESTED bodies separately. Bot detection: __typename == "Bot" / user.type == "Bot" (the [bot] login suffix is NOT reliable across endpoints).
Group by file, one row per thread. With --bots-only, keep only isBot rows.
| # | file:line | author | category | proposed action | |---|-----------|--------|----------|-----------------|
Categories: code-change ("should be", "use X instead") · question ("why", "how does") · nitpick ("nit:", style) · praise (no action) · discussion (architecture) · bot-finding (CI bot inline comment — verify before accepting, many are false positives) · outdated (isOutdated: true — line moved; default: reply "addressed in {commit}" + resolve). Present the table and let the user greenlight threads.
For each greenlit thread:
path:line; check the suggestion against Iron Laws--fix or explicit ok)${CLAUDE_SKILL_DIR}/references/response-patterns.md)bash gh api --method POST \ "repos/$OWNER/$REPO/pulls/$PR/comments/$FIRST_COMMENT_ID/replies" \ -f body="$REPLY_TEXT"
--no-resolve):bash gh api graphql -f query='mutation($threadId:ID!){ resolveReviewThread(input:{threadId:$threadId}){ thread { id isResolved } }}' -F threadId="$THREAD_ID"
Mistake recovery: unresolveReviewThread takes the same input shape.
mix compile --warnings-as-errors && mix test scoped to changed files. Do NOT commit or push — leave that to the user.
Print rollup: # | thread | action | status (replied/resolved/skipped). List changed files. Optionally post a top-level conversation comment (gh api --method POST "repos/$OWNER/$REPO/issues/$PR/comments" -f body=...) with the rollup — only on user approval.
textPR receives review → /phx:pr-review {number} ← YOU ARE HERE ↓ fetch unresolved threads (GraphQL, paginated) ↓ triage table → user greenlights ↓ per thread: fix (diff) → reply → resolve ↓ verify (mix compile + test) → summary Push changes → user handles git push
/phx:plan — if findings reveal scope gaps/phx:verify — full verification before pushing/phx:pr-review after the next review round (idempotent)${CLAUDE_SKILL_DIR}/references/response-patterns.md — Response templates and tone${CLAUDE_SKILL_DIR}/references/gh-commands.md — Full gh command reference (3 comment surfaces, pagination, bot detection)${CLAUDE_SKILL_DIR}/references/bot-triage.md — Batch-triaging CI bot review passes| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | pass→pass | 11,194 | 6,153 | -45% | 1 | 1 | 0% | 2,163 | 2,864 | +32% | 0 | 0 | — |
case-01 | fail→fail | 14,669 | 3,530 | -76% | 1 | 1 | 0% | 1,588 | 2,003 | +26% | 0 | 0 | — |
case-02 | fail→fail | 18,141 | 5,880 | -68% | 1 | 1 | 0% | 2,516 | 1,974 | -22% | 0 | 0 | — |
case-03 | fail→fail | 5,329 | 5,597 | +5% | 1 | 1 | 0% | 308 | 1,899 | +517% | 0 | 0 | — |
case-04 | pass→pass | 4,270 | 4,782 | +12% | 1 | 1 | 0% | 742 | 2,433 | +228% | 0 | 0 | — |
case-05 | pass→pass | 7,084 | 5,799 | -18% | 1 | 1 | 0% | 1,348 | 2,597 | +93% | 0 | 0 | — |
case-06 | pass→fail | 7,053 | 4,838 | -31% | 1 | 1 | 0% | 1,140 | 1,801 | +58% | 0 | 0 | — |
case-08 | pass→pass | 8,100 | 7,939 | -2% | 1 | 1 | 0% | 1,519 | 3,196 | +110% | 0 | 0 | — |
case-09 | fail→pass | 15,285 | 9,266 | -39% | 1 | 1 | 0% | 2,553 | 3,175 | +24% | 0 | 0 | — |
case-10 | fail→pass | 9,777 | 5,208 | -47% | 1 | 1 | 0% | 1,596 | 2,494 | +56% | 0 | 0 | — |
case-11 | pass→pass | 5,456 | 2,192 | -60% | 1 | 1 | 0% | 1,091 | 2,023 | +85% | 0 | 0 | — |
case-12 | pass→pass | 3,507 | 1,888 | -46% | 1 | 1 | 0% | 631 | 1,956 | +210% | 0 | 0 | — |
case-13 | pass→pass | 6,407 | 3,904 | -39% | 1 | 1 | 0% | 1,304 | 2,330 | +79% | 0 | 0 | — |
case-14 | fail→pass | 5,289 | 1,380 | -74% | 1 | 1 | 0% | 900 | 1,801 | +100% | 0 | 0 | — |
case-15 | pass→pass | 4,748 | 3,078 | -35% | 1 | 1 | 0% | 786 | 2,068 | +163% | 0 | 0 | — |
case-16 | fail→pass | 6,145 | 6,495 | +6% | 1 | 1 | 0% | 1,014 | 2,552 | +152% | 0 | 0 | — |
case-17 | pass→pass | 8,668 | 3,210 | -63% | 1 | 1 | 0% | 1,395 | 2,097 | +50% | 0 | 0 | — |
case-18 | pass→pass | 8,049 | 3,062 | -62% | 1 | 1 | 0% | 1,261 | 2,143 | +70% | 0 | 0 | — |
case-19 | pass→pass | 9,799 | 6,655 | -32% | 1 | 1 | 0% | 1,449 | 2,575 | +78% | 0 | 0 | — |
case-20 | pass→pass | 11,739 | 5,071 | -57% | 1 | 1 | 0% | 1,665 | 2,433 | +46% | 0 | 0 | — |
case-21 | fail→pass | 6,813 | 3,603 | -47% | 1 | 1 | 0% | 1,153 | 2,196 | +90% | 0 | 0 | — |
case-22 | pass→pass | 13,227 | 5,640 | -57% | 1 | 1 | 0% | 2,315 | 2,593 | +12% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 19 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +18 percentage points is the difference between those two pass rates over the 19 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.