| name | resolve-pr-threads |
| description | PR の過去レビュースレッドのうち、指摘どおりに修正済みのものだけを resolve する。再レビュー後に古いスレッドを畳むのに使う。`THREAD_RESOLVE_SCOPE` 引数 (all / own / none) で resolve 範囲を制御する。判定に迷う場合は resolve しない。 |
resolve-pr-threads skill
過去のレビュースレッドのうち修正済みのものを resolve する skill。
誤判定で未対応の指摘を畳むと見落としに直結するため、判定に迷う場合は常に「resolve しない」を選ぶこと。
入力
OWNER / REPO / PR_NUMBER: 対象 PR
CHANNEL (任意): GitHub アクセスチャネル。gh または mcp。caller (run-pr-review Step 1) が解決済みならその値を使う。未指定なら run-pr-review Step 1-2 と同じ手順で自力解決する (gh probe → MCP ツールの有無 → どちらも不可ならエラー停止。解決手順は run-pr-review Step 1-2 を正典とする)。gh と MCP は対等な正規チャネル。
THREAD_RESOLVE_SCOPE: resolve 範囲
all (デフォルト): すべての未 resolve スレッドを対象に、author 種別 (本レビュアー Bot / 他 Bot レビュアー / 人間レビュアー) を問わず resolve 候補にする。
own: 本アクション自身 (claude-code-action が用いる Bot) が author のスレッドのみ resolve 候補にする。判定は自身の過去コメントの author.login と一致するか否かで行う。判定が困難な場合は resolve しない。
none: 過去スレッドの resolve は一切行わない。本 skill 全体を skip する。
SELF_LOGIN (任意): THREAD_RESOLVE_SCOPE=own 時に「自身」を判定するための author.login。caller が判明していれば渡す。未指定の場合は次の優先順で推定する:
- 直近の review / comment から
*[bot] author で実行中アクションと一致するもの。
- それで決まらなければ、PR 内で
*[bot] 投稿件数最多の author。複数 Bot 同居時は別 Bot (github-copilot[bot] 等) との取り違えに注意。
- 一意に決まらなければ scope=own をまるごと skip し、Step 5 で「SELF_LOGIN 推定不能」と報告する。
共通の resolve 判定ルール
(スレッドの resolved / outdated フラグは、CHANNEL=gh の GraphQL では isResolved / isOutdated、CHANNEL=mcp の get_review_comments では is_resolved / is_outdated として返る。以下は両者を指す。)
- 既に resolved 済み (
isResolved / is_resolved が true) のスレッドは触らない。
- resolve するのは 指摘内容どおりの修正が現在の差分・ファイル内容から確認できる場合のみ。
- 以下のいずれかに該当する場合は resolve しない:
- 指摘箇所が削除されただけで、別の場所に同じ問題が残っている可能性がある。
- outdated (
isOutdated / is_outdated が true) だが、修正されたか単に行がずれただけかが判別できない。
- 指摘が複数論点を含み、一部しか対応されていない。
- そもそも修正されたかどうか判断に迷う。
手順
Step 0. THREAD_RESOLVE_SCOPE のチェック
THREAD_RESOLVE_SCOPE=none の場合は何もせず終了する。
Step 1. レビュースレッド一覧を取得する
各スレッドの id (thread node ID PRRT_...。Step 4 の resolve で必須) / isResolved / isOutdated / 各コメントの author.login / path / line / body を取得し、未 resolve スレッドすべてを判定対象とする。取得経路は CHANNEL で分岐する。
CHANNEL=mcp
mcp__github__pull_request_read を method=get_review_comments (owner / repo / pullNumber) で呼ぶ。スレッド単位で id (thread node ID) / is_resolved / is_outdated / 各コメントの author / path / line / body / html_url が返る。pageInfo.hasNextPage が true の間 after=<endCursor> を付けて全スレッドを取得しきるまで繰り返す (perPage は最大 100)。
Step 3-2 の reply 用に、各スレッド先頭コメントの数値 comment ID をここで控える: mcp__github__add_reply_to_pull_request_comment は数値 ID を要求する。get_review_comments が数値 ID を独立フィールド (例: id / databaseId) で返すならそれを優先し、無い場合は各コメントの html_url 末尾アンカー #discussion_r<数値> の <数値> 部分を抽出して保持する (アンカー抽出は tool schema の現状に対するフォールバックであり、数値 ID フィールドが提供されればそちらを使う)。どのスレッドでも数値 ID を確定できない場合は、そのスレッドの reply / resolve を skip し Step 5 で「コメント投稿失敗で見送り」として計上する (黙って全 skip しない)。
CHANNEL=gh
GraphQL で取得する。reviewThreads(first: 100) は GitHub GraphQL API の 1 ページあたりの上限値。100 件を超える可能性がある場合はページネーションする。
gh api graphql \
-F owner=<OWNER> \
-F name=<REPO> \
-F number=<PR_NUMBER> \
-f query='
query($owner: String!, $name: String!, $number: Int!, $after: String) {
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
reviewThreads(first: 100, after: $after) {
pageInfo { hasNextPage endCursor }
nodes {
id
isResolved
isOutdated
comments(first: 100) {
nodes {
author { login }
path
line
originalLine
body
}
}
}
}
}
}
}'
初回呼び出し時は $after を省略 (または null) する。pageInfo.hasNextPage が true の場合は -F after=<endCursor> を付けて同じクエリを再実行し、全スレッドを取得しきるまで繰り返す。
Step 2. resolve 対象を決める
各スレッドについて以下を判定する。
isResolved: true ならスキップ。
THREAD_RESOLVE_SCOPE=own の場合、スレッド先頭コメントの author.login が SELF_LOGIN と一致しないならスキップ。
- 上記「共通の resolve 判定ルール」に従って、指摘どおりに修正されたものだけを resolve 候補にする。コメントの
path / line 周辺の現在のファイル内容と差分を必ず確認する。
Step 3. resolve 根拠コメントを投稿する
resolve する前に、なぜ resolve するのか根拠を一言コメントで残す。後から「なぜこのスレッドが畳まれたのか」を追えるようにするため。
コメント本文には 必ず AI 自動投稿マーカーを先頭に付与する (テンプレートと根拠は 3-2 参照)。
3-1. 対応 commit を特定する
可能なら指摘箇所を修正した commit を特定し、その URL を根拠として添える。優先順は次の通り:
- caller から特定の commit SHA が明示されている場合はそれを採用する (caller の意図を尊重)。
- それ以外は
git blame <path> -L <line>,<line> で該当行を最後に変更した commit を特定するのが最も確実。
- (2) で取れない場合は
git log --oneline -- <path> で対象ファイルの commit 履歴から推定する。
- PR 全体の commit は
gh pr view <PR_NUMBER> --json commits (CHANNEL=gh) / mcp__github__pull_request_read method=get_commits (CHANNEL=mcp) でも取得できる (補助情報)。
commit URL は https://github.com/<OWNER>/<REPO>/commit/<COMMIT_SHA> 形式。
特定できない場合 (リファクタで該当行が消えただけ等) は commit URL を省略し、根拠を文章で簡潔に書く。
3-2. スレッドへ返信コメントを投稿する
スレッドに返信する。経路は CHANNEL で分岐する (CHANNEL=gh は addPullRequestReviewThreadReply mutation、CHANNEL=mcp は mcp__github__add_reply_to_pull_request_comment。本文の規約は共通)。
本文の先頭には AI 自動投稿マーカーを必ず付与する。認証主体が人間 PAT でも投稿内容は AI 生成であることを明示するため。エージェント名 (Claude Code / Codex / Cursor 等) はマーカーに含めない (本 skill は複数の AI エージェントから呼ばれうる前提)。
マーカーと根拠コメントのテンプレート (<根拠コメント本文> は短く 1 文程度に抑える):
> **[AI 自動投稿]** このコメントは AI エージェントによる自動投稿です。内容の判断は AI が行っています。
<根拠コメント本文>
例:
> **[AI 自動投稿]** このコメントは AI エージェントによる自動投稿です。内容の判断は AI が行っています。
[abc1234](https://github.com/<OWNER>/<REPO>/commit/abc1234) で対応済みのため resolve します。
body には テンプレート全体 (マーカー + <根拠コメント本文>) を結合した文字列を渡す。
CHANNEL=mcp: mcp__github__add_reply_to_pull_request_comment を呼ぶ: owner / repo / pullNumber、commentId = Step 1 で控えたスレッド先頭コメントの数値 ID (html_url の #discussion_r<数値> から抽出した値)、body = テンプレート全体 (複数行文字列をツール引数に直接渡す。ファイル経由は不要)。
CHANNEL=gh: addPullRequestReviewThreadReply mutation を呼ぶ。複数行文字列を扱いやすくするため、Write ツールで body をファイル (例: /tmp/resolve-body.txt) に書き出してから -F body=@/tmp/resolve-body.txt で読み込ませる方法を推奨する。
gh api graphql \
-F threadId=<THREAD_ID> \
-F body=@/tmp/resolve-body.txt \
-f query='
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
comment { id }
}
}'
Step 4. スレッドを resolve する
Step 3 のコメント投稿が成功したスレッドのみ resolve する。Step 3 の返信投稿が失敗した場合は 当該スレッドのみ skip し、resolve も実行せず次のスレッドへ進む (全体停止はしない)。skip した件数は Step 5 で別カウントとして報告する。
<THREAD_ID> は Step 1 で得たスレッドの id (thread node ID PRRT_...)。
CHANNEL=mcp: mcp__github__pull_request_review_write を method=resolve_thread、owner / repo / pullNumber / threadId=<THREAD_ID> で呼ぶ (post-pr-review の投稿と同じ review-write ツールに寄せる。標準の GitHub MCP サーバーでは resolve は独立ツールではなく本ツールの resolve_thread メソッドで行う。pullNumber は schema 上必須なので PR_NUMBER を渡す)。
CHANNEL=gh: resolveReviewThread mutation を実行する。
gh api graphql \
-F threadId=<THREAD_ID> \
-f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { id isResolved }
}
}'
resolve が失敗した場合は 再試行せず次のスレッドへ進む (再試行で根拠コメントが二重投稿になるのを避けるため)。Step 3 のコメントは投稿済みなので、当該スレッドは「根拠コメントだけ残り isResolved=false のオーファン状態」になる。これは Step 5 で別カウントとして caller に報告し、後続で人間が手動 resolve できるようにする。
Step 5. caller への報告
以下の件数をそれぞれ分けて caller に返す (caller がレビュー本文の総括に「既存指摘のうち N 件は対応済みのためスレッドを resolve しました」と1〜2文で記載できるように)。4 件種別の合計は「未 resolved スレッド総数」と一致するように振り分ける (既 resolved は対象外で別枠):
- resolve したスレッド件数 (Step 4 まで成功したもの)
- コメント投稿失敗で resolve を見送った件数 (Step 3-2 の返信投稿が失敗したもの。CHANNEL=gh なら
addPullRequestReviewThreadReply、CHANNEL=mcp なら mcp__github__add_reply_to_pull_request_comment の失敗)
- resolve 実行失敗で見送った件数 (Step 4 の resolve が失敗したもの。CHANNEL=gh なら
resolveReviewThread、CHANNEL=mcp なら mcp__github__pull_request_review_write (method=resolve_thread) の失敗。当該スレッド ID と path:line を明示して人間が手動 resolve しやすくする)
- 判定保留で resolve しなかった件数 (Step 2 の判定で「resolve しない」とした未対応 / 判別不能 / scope=own で対象外、のもの)
別枠で参考表示する項目 (4 件種別の合計には含めない):
- 既に
isResolved=true で対象外だったスレッド件数 (触っていない)
THREAD_RESOLVE_SCOPE=own で SELF_LOGIN 推定が困難で skill 自体を skip した場合は、その旨を明示する。