plugins/elixir-phoenix/skills/pr-review/SKILL.md
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.
npx skillsauth add oliver-kriska/claude-elixir-phoenix pr-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.
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 open
gh 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:
cat > /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:
Read code at path:line; check the suggestion against Iron Laws
Apply fix with a user-visible diff (only with --fix or explicit ok)
Draft reply (templates: ${CLAUDE_SKILL_DIR}/references/response-patterns.md)
STOP — show diff + reply, get confirmation
Post reply — REST, targeting the thread's root comment:
gh api --method POST \
"repos/$OWNER/$REPO/pulls/$PR/comments/$FIRST_COMMENT_ID/replies" \
-f body="$REPLY_TEXT"
Resolve the thread (skip with --no-resolve):
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.
PR 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 passestools
Compatibility alias for the Elixir/Phoenix plugin's LiveView assigns audit. Invoke explicitly with /lv:assigns.
development
Trace Elixir call trees from entry points via mix xref. Use when debugging data flow, planning signature changes, or understanding how a bug reaches code.
tools
Compatibility alias for the Elixir/Phoenix plugin's N+1 query checker. Invoke explicitly with /ecto:n1-check.
tools
Compatibility alias for the Elixir/Phoenix plugin's Ecto constraint debugger. Invoke explicitly with /ecto:constraint-debug.