mcpField
P
pr-review-loop

pr-review-loop

github.com/xpepperテスト・品質6302 回インストール3 日前に更新
リポジトリ

pr-review-loop について

pr-review-loopは、未解決のプルリクエストのコメントを一件ずつ扱う、再開可能なスキルです。変更や返信の前に分類結果の承認を求めるワークフローを使いたい開発者向けです。

主な機能

  1. 01未解決のレビューと会話コメントを収集
  2. 02実装前にフィードバックを分類
  3. 03分類結果への承認を待機
  4. 04コメントごとに検証を行いながら処理
  5. 05通常モードとハンドオフモードに対応

活用シーン

  1. 01進行中のプルリクエストの指摘を整理
  2. 02承認されたレビュー指摘を順番に修正
  3. 03曖昧な依頼についてレビュアーに確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/xpepper/pr-review-agent-skill --skill pr-review-loop

インストール後、Claude Code で/pr-review-loopを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
handoff-mode.md
triage-guide.md

詳細

PR レビューループ

目的

オープンな PR レビューコメントに一つずつ対処する、独自の判断基準を備えた再開可能なワークフローです。人間やボットなど、あらゆるレビュアーのコメントに対応します。

一般的な呼び出し方

ユーザーは、次のようなプロンプトでこのスキルを呼び出します。

  • 「この PR の未対応レビューコメントすべてに対処してください」
  • 「PR #42 のコードレビューのフィードバックに順番に対応してください」
  • 「このプルリクエストに @alice が残したレビューコメントを修正してください」
  • 「PR #123 で pr-review-loop を使ってください」

前提条件

  • gh CLI(推奨)。利用できない場合は、GitHub とやり取りできる任意のツールに切り替えます。
  • PR のブランチがローカルにチェックアウトされている必要があります。

モードの選択

ユーザーが /pr-review-loop --handoff または「ハンドオフモードで PR レビューを続けてください」のような同等の表現で明示的に要求しない限り、通常モードを使用します。

  • ハンドオフモード: 以下の手順の代わりに、references/handoff-mode.md を読み、その指示に従います。ハンドオフモードは実験的なもので、ユーザーが新しいセッションを開始できるよう、明確に指定されたスコープを一つ処理した時点で意図的に停止します。
  • 通常モード: 以下の既存の手順を変更せずに実行します。.pr-review/HANDOFF.md が存在する場合は、通常モードによってその状態が古くなる可能性があることを一度だけ警告し、その後はファイルを無視します。ファイルを読んだり、更新したり、削除したりしないでください。

ハンドオフファイルが存在するというだけで、ハンドオフモードだと判断してはいけません。セッションを新しく開始するたびに、ユーザーが明示的にモードを選択する必要があります。

手順

ステップ 1 — 事前確認

次のファイルが存在する場合は確認し、プロジェクトの保護措置に関する規約を調べます。

  • CLAUDE.md、AGENTS.md
  • Makefile
  • .github/workflows/
  • README.md

必要な保護措置(テスト、コンパイル、リンティング、フォーマットなど)をすべて特定します。すべて実行してください。一つでも失敗した場合は、直ちに停止して報告し、問題のあるベースラインのまま先に進まないでください。

ステップ 2 — 現在の PR 番号と基本情報を取得
# Get current PR details
gh pr status

# View PR with all comments
gh pr view
ステップ 3 — 未解決の PR フィードバックをすべて収集

GitHub では、レビュアーからのフィードバックが二つの異なる形式で表示されます。両方を収集する必要があります。

種類場所「解決」できるか
レビューのスレッドファイルや行に紐づけられたインラインコメント。レビューの一部として送信されますはい — resolveReviewThread ミューテーション
Issue コメント通常の PR 会話コメント(トップレベルのコメントとして投稿されたレビューの要約など)いいえ — 返信によって完了とします

レビューのスレッドを取得(未解決のものだけを残します)。

gh api graphql -f query='
query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner, name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first:100) {
        nodes {
          id
          isResolved
          path
          line
          comments(first:20) {
            nodes {
              id
              databaseId
              author { login }
              body
              createdAt
              replyTo { databaseId }
            }
          }
        }
      }
    }
  }
}' -f owner='{owner}' -f name='{repo}' -F number={pr_number} \
  | jq '.data.repository.pullRequest.reviewThreads.nodes | map(select(.isResolved == false)) | map(. + {type: "review-thread"})'

Issue コメントを取得(トップレベルの PR 会話コメント)。

gh api repos/{owner}/{repo}/issues/{pr_number}/comments \
  | jq '[.[] | {id: .id, author: .user.login, body: .body, created_at: .created_at, type: "issue-comment"}]'

トリアージの前に、PR の作成者によるコメント(レビュアーからのフィードバックではありません)と、既知のボットアカウントによるコメントを除外してください。変換後の出力に含まれる author フィールドが役立ちます。

両方のリストにある項目をトリアージします。それぞれの項目がどちらの種類かを記録してください。返信(ステップ 6f)とクローズ(ステップ 6g)の方法に影響します。

ステップ 4 — トリアージ

具体的な分類の枠組みと例については、トリアージガイド を読んでください。ガイドを利用できない場合は、以下の定義を参考に、独自の判断で MUST_FIX / SHOULD_FIX / PARK / OUT_OF_SCOPE / NEEDS_CLARIFICATION に分類してください。

未解決のコメントをすべて、MUST_FIX、SHOULD_FIX、PARK、OUT_OF_SCOPE、NEEDS_CLARIFICATION のいずれかに分類します。

対応を始める前に、すべてのコメントをトリアージしてください。

実行可能な対応が不要なコメント(謝意、称賛、絵文字のみなど)は、OUT_OF_SCOPE に分類します。短い受領の返信と解決は、トリアージの承認後、処理中(ステップ 6)に行います。今は行いません。

コメントの意図が本当に曖昧で、複数の解釈があり、それぞれによって実質的に異なる変更が生じる場合は、推測せず NEEDS_CLARIFICATION に分類します。

Perplexity などの調査ツールを利用でき、コメントの分類に外部知識(ライブラリの慣用的な使い方や言語の規約など)が必要な場合は、それらを使って判断の参考にしてください。

ステップ 5 — トリアージを提示し、承認を待つ

ここで停止してください。ユーザーがトリアージを承認するまで、処理を開始しないでください。

すべてのコメントを分類したら、トリアージ全体をユーザーに提示し、承認を待ちます。ユーザーから返答があるまでは、コードに触れたり、計画ファイルを作成したり、返信を投稿したり、何かを解決済みにしたりしないでください。

未解決のコメントごとに 1 行の簡潔な表を提示します。

#RefClassRationale
1@author file:line(レビューのスレッド)、または短い抜粋(issue のコメント)MUST_FIX / SHOULD_FIX / PARK / OUT_OF_SCOPE / NEEDS_CLARIFICATIONステップ 1 で確認したコードベースの規約/エージェントガイドラインに基づく 1 行の理由

理由欄では、フィードバックを無批判に受け入れるのではなく、検討したうえで判断します。各コメントについて、プロジェクトの規約に沿った理由とともに、受け入れる、調整して受け入れる、または異議を唱えるのいずれかを示します。目指すのは、可能な限り質の高いマージです。フィードバックを真摯に受け止めつつ、プロジェクトで確立された規約に反する提案は、ここで明言せずに実装しないでください。

提示方法 — まず概要を示し、次に各行について判断を確認します。

  1. 概要。 トリアージ表全体を、チャット内にレンダリングされる Markdown で表示します。
  2. 判断。 ハーネスに対話型質問ツール(Claude Code の AskUserQuestion、GitHub Copilot CLI の ask_user、その他の環境における request_user_input または同等のツール)がある場合は、各行につき 1 つの質問(またはフォーム項目)を行い、1 回の呼び出しにバッチの全行を含めます。MUST_FIX の行を先に並べ、ツールの 1 回あたりの上限を超える場合に限り、複数回呼び出します(Claude Code:質問は 4 つまで)。ツール呼び出し前に表示した文章がユーザーに表示されない場合があります(Copilot CLI では、折りたたまれた思考ブロックに含まれることがあります)。これらのツールはプレーンテキストを表示するため、各質問は単独で意味が通る文章にし、表、パイプ、太字、バッククォートは使わないでください。
    • ラベル: 行番号と短い参照先。例:#3 api.rs。
    • 質問: レビュー担当者、場所、コメントの要旨を 1 行で示し、提案する分類とその理由を記載します。
    • 選択肢: 提案する分類を、ツールが対応する方法で推奨として示します(最初の選択肢に「(推奨)」を付けるか、フォームのデフォルトに設定します)。その後、最も妥当な代替案を 2〜3 個提示します。「その他」や「相談」の選択肢は追加しないでください。ツールの自由記述欄を使って、ユーザーが各行について話し合ったり、一覧にない分類を選んだりできます。
    • 共有メッセージ: ツールに共有メッセージ欄がある場合(Copilot のフォームの message など)、各行の概要を 1 行ずつプレーンテキストで記載します。
  3. 質問ツールがない場合: 表の後に次の 1 行を付けてターンを終了し、チャットでユーザーの返答を待ちます。「承認するには go と返信するか、変更する行を指定してください。」

質問がキャンセル、辞退された場合、または空の回答だった場合は、ツールがデフォルトで続行すると示していても、承認とはみなしません。ターンの最終出力として表を再表示し、ユーザーからの返答を待ちます。

各行について、ユーザーは次のいずれかを行えます。

  • 承認する: 提案された分類を承認します。
  • 変更する: 別の分類を指定します(例:「#5 は OUT_OF_SCOPE。規約は X です」)。変更を適用します。
  • 議論する: 分類の指定ではなく、質問や異議を自由記述で伝えます。回答した後、その行についてのみ再度確認します。

すべての行で分類が承認されてから、ステップ 6 に進みます。変更によってトリアージの内容が大きく変わった場合は、最初に更新後の表を再表示します。

ステップ 6 — コメントを1件ずつ処理する

順番に処理します。まず MUST_FIX をすべて処理し、その後 SHOULD_FIX を処理します。

OUT_OF_SCOPE の 対応不要 コメント(謝意、称賛、絵文字のみ)には、短い謝意の返信を投稿してスレッドを解決し(ステップ 6f~6g)、次に進みます。PARK と、対応が必要な OUT_OF_SCOPE の提案は、いったんスキップします。これらは保留し、概要で報告します。

NEEDS_CLARIFICATION コメント: スレッドへの返信として、焦点を絞った質問を1つ投稿し(以下の形式を参照)、スレッドは解決せず、次のコメントに進みます。何も実装しないでください。

連鎖するコメント: 複数のコメントが連鎖している場合(例:トレイトのシグネチャを変更するため、すべての実装、呼び出し元、テストを更新する必要がある場合)は、すべてのコメント ID を参照する単一のコミットにまとめます。連鎖全体を一括で実装してください。いずれか1つのコメントだけを適用すると、コードに不整合が生じるためです。

各コメント(または連鎖するコメントのグループ)について、次の手順を実行します。

6a. 複雑さを評価する

これは単純な変更ですか(例:関数名の変更、誤字の修正、書式の調整)?

  • はい → 計画は不要です。直接修正します
  • いいえ → コードに手を加える前に、.pr-review/plan-<comment-id>.md に計画ファイルを作成します

計画ファイルには次の内容を記述してください。

  • コメントで求められていること
  • 修正方法
  • 変更するファイル

6b. セーフガードを実行する

ステップ 1 で特定したセーフガードをすべて実行します。コードに手を加える前に、すべて通過する必要があります。 失敗した場合は、停止して報告します。

6c. 修正、保留、または確認を求める

  • 修正:変更を実装します
  • 調整して修正:レビュアーの提案の方向性は妥当でも、具体的なコードがコンパイルできない、または実現不可能な場合は、実際に動作する最も近い方法を実装します。提案と実装が異なる理由をレビュアーが理解できるよう、その制約を返信に記載します(ステップ 6f)
  • 保留(修正の途中で保留すべきだと判明した場合):理由を記録し、部分的な変更を元に戻します。コミットはしません
  • 確認を求める(評価の途中で意図が本当に曖昧だと判明した場合):スレッドに焦点を絞った質問を1つ投稿し(以下を参照)、解決せず、コミットもせずに次へ進みます

確認質問の形式(NEEDS_CLARIFICATION または評価途中で不確実な場合):

cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
Thanks for the feedback! Before I make a change, I want to make sure I understand what you're after:

<one specific, focused question — e.g. "Did you mean to rename this everywhere, or just at the call site?" or "Would you prefer approach A (…) or approach B (…)?">
EOF

jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json

gh api repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies \
  --input /tmp/pr-review-reply-{comment_id}.json

質問は必ず1つだけにしてください。質問の内容を明確にするために直接必要な場合を除き、選択肢を列挙しないでください。レビュアーの回答が再び通知されるよう、スレッドは未解決のままにします。

6d. セーフガードを再実行する

すべてのセーフガードを実行します。すべて通過する必要があります。 失敗した場合は、次に進む前に回帰を修正します。この手順を省略してはいけません。

6e. コミットしてプッシュする

各コメントについて、対象を絞った個別のコミットを作成します。コミットメッセージの本文でコメントの投稿者に言及してください。

git add <changed files>
git commit -m "<conventional commit message describing the fix>

Addresses PR comment from @<reviewer>."
git push

複数のコメントにまたがるコミットの流れの例:

# Comment 1: Add missing documentation
git commit -m "docs: add module-level documentation for MetricsRecorder

Addresses PR comment from @reviewer about missing module docs."
git push

# Comment 2: Use Duration instead of i64
git commit -m "refactor: use Duration type for timing parameters

Addresses PR comment from @reviewer - improves type safety."
git push

6f. PR コメントに返信する

次の内容を説明する返信を投稿します。

  • 実施した内容(修正の場合はコミットを参照)
  • 保留にした理由(延期する項目の場合)
  • 却下した理由(対応範囲外の項目の場合)

返信方法はコメントの種類によって異なります。

レビューのスレッドコメント(ファイルや行に紐づくインラインコメント)の場合:

cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
<reply text>
EOF

jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json

# POST the reply and capture the response — do NOT run this twice
gh api repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies \
  --input /tmp/pr-review-reply-{comment_id}.json > /tmp/pr-review-reply-{comment_id}-response.json

issue コメント(PR の会話にあるトップレベルのコメント)の場合:

cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
<reply text — quote or reference the original comment so context is clear>
EOF

jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json

gh api repos/{owner}/{repo}/issues/{pr_number}/comments \
  --input /tmp/pr-review-reply-{comment_id}.json > /tmp/pr-review-reply-{comment_id}-response.json

注:これにより、PR の会話に新しいトップレベルコメントが作成されます。元のコメントへのスレッド返信にはなりません。関連する抜粋を引用することで、文脈を保ちます。

6f.1 返信本文を確認する

投稿直後に、POST のレスポンスから取得した ID を使って GET を実行し、投稿内容を確認します(確認のために POST を再実行してはいけません。重複が作成されます)。

レビューのスレッドへの返信の場合:

NEW_REPLY_ID=$(jq '.id' /tmp/pr-review-reply-{comment_id}-response.json)
gh api repos/{owner}/{repo}/pulls/comments/$NEW_REPLY_ID | jq '{id, body, html_url}'

issue コメントへの返信の場合:

NEW_COMMENT_ID=$(jq '.id' /tmp/pr-review-reply-{comment_id}-response.json)
gh api repos/{owner}/{repo}/issues/comments/$NEW_COMMENT_ID | jq '{id, body, html_url}'

6g. コメントを解決する

レビューのスレッドコメントの場合 — GitHub 上で解決済みにします。

まず、コメントのスレッド ID を取得します。

gh api graphql -f query='
query {
  repository(owner: "{owner}", name: "{repo}") {
    pullRequest(number: {pr_number}) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          comments(first: 1) { nodes { body } }
        }
      }
    }
  }
}'

次に、スレッドを解決します。

gh api graphql -f query='
mutation {
  resolveReviewThread(input: {threadId: "{thread_id}"}) {
    thread { id isResolved }
  }
}'

解決済みであることを確認します。

gh api graphql -f query='
query {
  node(id: "{thread_id}") {
    ... on PullRequestReviewThread {
      id
      isResolved
    }
  }
}'

issue コメントの場合 — 「解決」する仕組みはありません。ステップ 6f で投稿した返信が終了の合図になります。ステップ 9 の概要に記載してください。

6h. 計画ファイルを削除する

計画ファイルを作成した場合は、削除します。

rm .pr-review/plan-<comment-id>.md
ステップ 7 — 処理ループの停止条件

MUST_FIX または SHOULD_FIX のコメントがなくなったら停止します。

スレッドを 1 つずつではなく、まとめて解決したい場合は、ここで実行できます。

gh api graphql -f query='
query {
  repository(owner: "{owner}", name: "{repo}") {
    pullRequest(number: {pr_number}) {
      reviewThreads(first: 50) {
        nodes { id isResolved comments(first: 1) { nodes { body } } }
      }
    }
  }
}' | jq -r '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .id' \
  | while read thread_id; do
    gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"$thread_id\"}) { thread { id } } }"
  done
ステップ 8 — PR 本文とのずれを確認する

概要を投稿する前に、PR のコミットによって、本文に記載されている内容からスコープがずれていないか確認します。この実行の開始時点ではなく、PR のベースブランチと比較してください。本文には PR 全体の内容を記載する必要があり、ベースブランチは新しいコンテキストで再開する際にも確認できます。

# The PR's base branch (e.g. main)
base=$(gh pr view {pr_number} --json baseRefName --jq '.baseRefName')

# All commits the PR adds on top of its base
git log "origin/$base..HEAD" --oneline

# The current PR body
gh pr view {pr_number} --json body --jq '.body'

ずれ = 本文が PR の内容について読者に誤解を与える状態です。動作の追加や削除、アプローチの変更、本文に記載されていないスコープなどが該当します。

ずれではないもの = PR に記載された意図を変えない、単なるバグ修正の微調整です。必要のない変更で本文をむやみに書き換えないでください。

ずれが見つかった場合は、現在も正確な部分を残したうえで PR の本文を更新します。

cat > /tmp/pr-body.md <<'EOF'
<updated PR body>
EOF

gh pr edit {pr_number} --body-file /tmp/pr-body.md

更新内容をステップ 9 の概要に記録します(「Updated PR body to reflect …」という行を追加)。この更新はすでにステップ 5 のトリアージ承認で許可されており、概要にも明記されるため、別途確認を得る必要はありません。

ステップ 9 — 概要

PR に最終コメントを投稿し、以下を要約します。

## PR Review Loop — Summary

### Fixed
- [commit abc1234] Renamed `foo` to `bar` (comment by @alice)
- ...

### Parked
- Refactor of X module deferred — tracked in #<issue> (comment by @bob)
- ...

### Rejected
- Suggestion to use Y rejected: project convention is Z (comment by @carol)
- ...

### Awaiting Clarification
- Asked @alice: "Did you mean to rename this everywhere, or just at the call site?" — thread left open
- ...

### PR Body
- Updated PR body to reflect <what changed> (or omit this section if no drift)

項目がないセクションは省略します。

シェルによる文字列展開を避けるため、本文ファイルを使います。

cat > /tmp/pr-review-summary.md <<'EOF'
<summary text>
EOF

gh pr comment {pr_number} --body-file /tmp/pr-review-summary.md

次に、投稿された概要の本文を確認します。

gh pr view {pr_number} --comments

再開可能性

このスキルは、いつでも中断し、新しいコンテキストで再開できるように設計されています。

起動時:

  1. 事前確認を実行します(ステップ 1)
  2. GitHub からレビューのスレッドと issue コメントを再取得します(ステップ 3)。すでに解決済みのスレッドは表示されません。issue コメントについては、元のコメントより後に投稿され、元のコメントを本文に引用しているか、返信テンプレートに従っているコメントを探します。これはベストエフォートの方法であり、自動フィルターの isResolved の仕組みより本質的に信頼性が低いため、確信が持てない場合はスキップせず、返信を再読するほうが安全です
  3. 既存の .pr-review/plan-*.md ファイルがあるか確認します。見つかった場合は、そのコメントの修正作業の途中なので、ステップ 6b から続行します
  4. 前回の実行が中断されていた場合は、続行する前に最新の issue/コメント本文とスレッドの状態を確認します(一部だけ投稿されたリモートへの書き込みや、形式が不正な書き込みを検出するため)
  5. 残りのコメントをトリアージし、続行します

トリアージの承認ゲート(ステップ 5)は、会話の状態であり、保存されません。新しいコンテキストで再開した場合は、処理を始める前にゲートが再度提示されます。各修正は次の作業に移る前にコミットしてプッシュされるため、再度確認を求めても問題はなく、作業が失われることもありません。

つまり、進捗が失われることはありません。各修正は、次の作業に移る前にコミットしてプッシュされます。

状態ディレクトリ

リポジトリのルートにある .pr-review/(プロジェクトによって Git の追跡対象外に設定されています)。

  • plan-<comment-id>.md — 現在対応中のコメントに関する計画(解決後に削除)

禁止事項

  • ユーザーがトリアージを承認する前に、コメントの処理を開始する
  • PR のフィードバックをすべて 1 つの大きなコミットにまとめる
  • 1 つのコミットに、互いに無関係な変更を複数含める
  • 中間コミットを作らず、変更を一度にすべてプッシュする
  • 対応済みのコメントを未解決のまま残す

コメント