PR のレビューコメントと CI 失敗を Monitor ツールでイベント駆動監視し、ユーザー確認なしで自動で修正・コミット・プッシュ・返信を行う。pr-fix と pr-ci を統合し自律実行する。最大 30 分 (活動検出時は最大 60 分)。Use when PR の監視、自動修正、ウォッチを求められた際に使用する。
PR のレビューコメントと CI 失敗を Monitor ツールでバックグラウンド監視し、イベント検出次第自動で修正・コミット・プッシュ・返信を実行する。
japanese-text-style スキルに従う| パラメータ | 値 |
|---|
| Monitor 方式 | バックグラウンドスクリプト (persistent: true) |
| ポーリング間隔 | 60 秒 |
| アイドル上限 | 30 分 (レビュー/CI 失敗/コンフリクト等の活動が一度も検出されなかった場合に終了) |
| 絶対上限 | 60 分 (活動有無に関わらず強制終了) |
| 即時終了条件 | PR クローズ/マージ済み |
Monitor スクリプトが stdout に出力するイベント。各行が 1 イベント。
| イベント | 意味 | 対応 |
|---|---|---|
NEW_REVIEWS|thread_id1,thread_id2 | 新しい未解決レビュースレッドを検出 | レビュー修正を実行 (3a) |
CI_FAIL|run_id1,run_id2 | CI 失敗を検出 (全 run 完了後、run ID のみ) | CI 修正を実行 (3b) |
PR_MERGED | PR がマージされた | 監視終了 → 完了報告 (4) |
PR_CLOSED | PR がクローズされた | 監視終了 → 完了報告 (4) |
PR_CONFLICT | コンフリクトが発生した | コンフリクト解消を実行 (3d) |
TIMEOUT_IDLE|Xmin | アイドルタイムアウト (30 分) | 監視終了 → 完了報告 (4) |
TIMEOUT_ABS|Xmin | 絶対タイムアウト (60 分) または API エラー 3 サイクル連続 | 監視終了 → 完了報告 (4) |
イベント対応全体で以下の状態を管理する:
PR_NUMBER: PR 番号OWNER, REPO: リポジトリ情報MY_LOGIN: 自分の GitHub ユーザー名 (gh api user --jq '.login' で取得、自分のコメントを除外するため)MONITOR_ID: Monitor のタスク ID (TaskStop で終了するため)UNFIXABLE_RUNS: 修正不可能と判断した CI run ID のリスト (以降のイベントで同じ失敗の再処理をスキップする)REVIEW_COMMITS: レビュー修正コミット数CI_COMMITS: CI 修正コミット数REPLIED_COMMENTS: 返信済みコメント数RESOLVED_THREADS: resolve 済みスレッド数PR_UPDATES: PR タイトル・description の更新回数CONFLICT_RESOLVES: コンフリクト解消回数RE_REQUESTED_REVIEWERS: レビュー再リクエスト済みユーザーのリスト必須: 作業開始前に TaskList で残存タスクを確認し、存在する場合は全て TaskUpdate({ status: "deleted" }) で削除する。その後、TaskCreate ツールで以下のタスクを登録する:
TaskCreate({ subject: "PR の特定", description: "引数または現在のブランチから PR を特定", activeForm: "PR を特定中" })
TaskCreate({ subject: "Monitor セットアップ", description: "バックグラウンド監視スクリプトを起動", activeForm: "Monitor をセットアップ中" })
TaskCreate({ subject: "イベント対応", description: "Monitor からのイベントに応じて修正・返信を実行", activeForm: "PR を監視中" })
TaskCreate({ subject: "監視終了・完了報告", description: "監視結果を集計して報告", activeForm: "完了報告を作成中" })
各ステップの開始時に TaskUpdate で in_progress に、完了時に completed に更新する。
引数で PR 番号が指定されていない場合、現在のブランチから PR を特定する:
gh pr view --json number,title,headRefName,state --jq '{number, title, headRefName, state}'
MERGED または CLOSED の場合は監視を開始せず終了する状態変数を初期化する。初期化時に MY_LOGIN=$(gh api user --jq '.login') で自分の GitHub ユーザー名を取得する。
以下の要件でバックグラウンド監視スクリプトを作成し、Monitor ツールで起動する。
スクリプトの要件:
TIMEOUT_ABS を出力して exit)スクリプトテンプレート:
<OWNER>, <REPO>, <PR_NUMBER>, <MY_LOGIN> はステップ 1 で取得した値で置き換える。
#!/bin/bash
set -uo pipefail
OWNER="<OWNER>"; REPO="<REPO>"; PR_NUMBER=<PR_NUMBER>; MY_LOGIN="<MY_LOGIN>"
START=$(date +%s)
IDLE_LIMIT=1800; ABS_LIMIT=3600
PREV_THREADS=""; PREV_FAILS=""; PREV_SHA=""
HAD_ACT=false; API_ERRORS=0; PREV_CONFLICT=false
while true; do
NOW=$(date +%s); ELAPSED=$(( (NOW - START) / 60 ))
# タイムアウト判定
[ $((NOW - START)) -ge $ABS_LIMIT ] && echo "TIMEOUT_ABS|${ELAPSED}min" && exit 0
[ "$HAD_ACT" = false ] && [ $((NOW - START)) -ge $IDLE_LIMIT ] && echo "TIMEOUT_IDLE|${ELAPSED}min" && exit 0
API_FAIL=false
# PR 状態チェック
PRI=$(gh pr view "$PR_NUMBER" -R "$OWNER/$REPO" --json state,mergeable,headRefOid 2>/dev/null) || API_FAIL=true
if [ "$API_FAIL" = false ]; then
ST=$(echo "$PRI" | jq -r '.state')
MG=$(echo "$PRI" | jq -r '.mergeable')
SHA=$(echo "$PRI" | jq -r '.headRefOid')
[ "$ST" = "MERGED" ] && echo "PR_MERGED" && exit 0
[ "$ST" = "CLOSED" ] && echo "PR_CLOSED" && exit 0
if [ "$MG" = "CONFLICTING" ]; then
[ "$PREV_CONFLICT" = false ] && echo "PR_CONFLICT" && HAD_ACT=true
PREV_CONFLICT=true
else
PREV_CONFLICT=false
fi
# 新コミット検出時: CI 失敗トラッキングをリセット
if [ "$SHA" != "$PREV_SHA" ]; then
PREV_SHA="$SHA"
PREV_FAILS=""
fi
# 未解決レビュースレッド取得
TJ=$(gh api graphql -f query='
query {
repository(owner: "'"$OWNER"'", name: "'"$REPO"'") {
pullRequest(number: '"$PR_NUMBER"') {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 100) {
totalCount
nodes { author { login } }
}
}
}
}
}
}' 2>/dev/null) || API_FAIL=true
if [ "$API_FAIL" = false ]; then
# フィルタ: 未解決 かつ 自分以外のコメント (スレッド ID + コメント数で変化を検出)
CT=$(echo "$TJ" | jq -r --arg m "$MY_LOGIN" \
'[.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved == false)
| select(.comments.nodes[0].author.login != $m)
| .id + ":" + (.comments.totalCount | tostring)] | sort | join(",")')
# 新規または変化のあるスレッドを抽出 (PREV_THREADS に含まれないエントリ)
# エントリは "thread_id:comment_count" 形式。コメント追加時も変化を検出する
if [ -n "$CT" ]; then
NEW_T=""
IFS=',' read -ra CUR_ARR <<< "$CT"
for entry in "${CUR_ARR[@]}"; do
case ",$PREV_THREADS," in
*",$entry,"*) ;; # 既知 (ID もコメント数も同一)
*) tid="${entry%%:*}"; NEW_T="${NEW_T:+$NEW_T,}$tid" ;;
esac
done
[ -n "$NEW_T" ] && echo "NEW_REVIEWS|$NEW_T" && HAD_ACT=true
fi
PREV_THREADS="$CT"
fi
# CI ステータスチェック
if [ -n "$SHA" ]; then
CI_API_OK=true
RJ=$(gh run list --commit "$SHA" -R "$OWNER/$REPO" --json databaseId,status,conclusion,name -L 50 2>/dev/null) || CI_API_OK=false
if [ "$CI_API_OK" = true ]; then
# in_progress / queued があれば CI 確定待ち → スキップ
IP=$(echo "$RJ" | jq '[.[] | select(.status == "in_progress" or .status == "queued")] | length')
if [ "$IP" -eq 0 ] && [ "$(echo "$RJ" | jq 'length')" -gt 0 ]; then
CF=$(echo "$RJ" | jq -r '[.[] | select(.conclusion == "failure") | (.databaseId | tostring)] | sort | join(",")')
# 新しい失敗のみ検出 (PREV_FAILS に含まれない run ID のみ抽出)
if [ -n "$CF" ]; then
NEW_F=""
IFS=',' read -ra CUR_FAIL_ARR <<< "$CF"
IFS=',' read -ra PRV_FAIL_ARR <<< "$PREV_FAILS"
for fid in "${CUR_FAIL_ARR[@]}"; do
IS_KNOWN=false
for pfid in "${PRV_FAIL_ARR[@]}"; do
[ "$fid" = "$pfid" ] && IS_KNOWN=true && break
done
[ "$IS_KNOWN" = false ] && NEW_F="${NEW_F:+$NEW_F,}$fid"
done
[ -n "$NEW_F" ] && echo "CI_FAIL|$NEW_F" && HAD_ACT=true
fi
PREV_FAILS="$CF"
fi
else
API_FAIL=true
fi
fi
fi
# サイクル単位の API エラー判定
if [ "$API_FAIL" = true ]; then
API_ERRORS=$((API_ERRORS + 1))
[ $API_ERRORS -ge 3 ] && echo "TIMEOUT_ABS|${ELAPSED}min" && exit 0
else
API_ERRORS=0
fi
sleep 60
done
Monitor 起動:
Monitor({
description: "PR #<PR_NUMBER> 監視",
persistent: true,
command: "bash /tmp/pr-monitor-<PR_NUMBER>.sh"
})
起動前にスクリプトを /tmp/pr-monitor-<PR_NUMBER>.sh に書き出す。Monitor が返すタスク ID を MONITOR_ID として保持する。
起動後、ユーザーに監視開始を報告する:
PR #<number> (<title>) の監視を開始しました。
60 秒間隔でレビューコメントと CI 失敗を監視します。
検出次第自動で修正・コミット・プッシュ・返信を行います。
Monitor からの通知を受信したら、以下のルールに従って対応する。
優先順位: 同一通知内に NEW_REVIEWS と CI_FAIL が含まれる場合、レビュー修正を先に処理する。
通知に含まれるスレッド ID を処理対象とする。重複排除は Monitor スクリプトの PREV_THREADS で行われるため、イベントハンドラ側での追加フィルタは不要。
詳細取得:
処理対象のスレッドについて、完全なコメント情報を取得する:
# <owner>, <repo>, <number> は実際の値に置き換える
gh api graphql -F query='
query {
repository(owner: "<owner>", name: "<repo>") {
pullRequest(number: <number>) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 10) {
nodes {
databaseId
body
path
line
author { login }
}
}
}
}
}
}
}'
取得したスレッドのうち、処理対象の ID に一致するもののみ使用する。
妥当性判断の基準:
| 指摘の種類 | 判断 | 対応 |
|---|---|---|
| コードの正確性に関する指摘 | 修正が必要 | コードを修正 |
| セキュリティに関する指摘 | 修正が必要 | コードを修正 |
| パフォーマンスに関する指摘 | 修正が必要 | コードを修正 |
スタイルや好みの問題 (nits:) | 内容次第 | コードを修正または、理由を返信して resolve |
| 誤解に基づく指摘 | 対応不要 | 説明を返信して resolve |
| 既に別のコミットで対応済みの指摘 | 対応不要 | 対応済みの旨を返信して resolve |
ファクトチェック (必須):
レビューの指摘を鵜呑みにせず、技術的な主張や根拠が正しいか検証する。特に以下のケースでは必ずファクトチェックを行う:
ファクトチェックのソース優先順位:
| 優先度 | ソース | 用途 |
|---|---|---|
| 1 | LSP | コードベース内の定義・参照・型情報の確認 |
| 2 | deepwiki MCP | OSS リポジトリの Wiki・ドキュメント |
| 3 | Gemini MCP | Google 検索による最新情報の取得 |
| 4 | context7 MCP | ライブラリの公式ドキュメントとコード例 |
| 5 | WebFetch | 公式サイト・GitHub・特定 URL の確認 |
| 6 | WebSearch | 最新情報・ブログ・リリースノートの検索 |
例外 (上記の優先順位より優先):
mcp__terraform__*) が最優先mcp__google-developer-knowledge__*) が最優先subagent_type: "claude-code-guide") が最優先ファクトチェックの結果、指摘が誤りだった場合はその根拠をソース付きで返信コメントに記載する。
処理フロー:
各未解決コメントの妥当性を上記基準で判断し、ファクトチェックで検証する
修正が必要なコメントに対してコードを修正する
修正したファイルをステージングする: git add <修正ファイル>
commit-proposer subagent でコミットメッセージを生成する:
Task({
subagent_type: "git:commit-proposer",
description: "コミットメッセージ候補の生成",
prompt: "ステージング済みの変更に対してコミットメッセージ候補を提案してください。コンテキスト: レビュー指摘に基づく修正です。subject には「レビュー指摘に基づく修正」のような汎用的な表現ではなく、実際に何を変更したかを具体的に記述してください。"
})
subagent がエラーを返した場合は、変更差分から Conventional Commits 形式のメッセージを自前で生成する。その際も subject には実際の変更内容を具体的に記述し、「レビュー指摘に基づく修正」のような汎用表現は使わない。
推奨メッセージ (候補 1) でコミットする
# <type>, <scope>, <subject>, <body> は commit-proposer の出力で置き換える
git commit -m "$(cat <<'EOF'
<type>(<scope>): <subject>
<body>
EOF
)"
git push でリモートに反映する
各コメントに返信・リアクション・resolve を実行する:
# 元のコメントに +1 リアクション (databaseId 使用)
gh api repos/{owner}/{repo}/pulls/comments/<databaseId>/reactions -f content="+1"
# スレッドに返信 (GraphQL mutation、thread id 使用)
# <thread_id>, <body> は実際の値に置き換える
gh api graphql -F query='
mutation {
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: "<thread_id>", body: "<body>"}) {
comment { id body }
}
}'
# スレッドを resolve
# <thread_id> は実際の値に置き換える
gh api graphql -F query='
mutation {
resolveReviewThread(input: {threadId: "<thread_id>"}) {
thread { isResolved }
}
}'
処理順序: リアクション追加 → 返信投稿 → resolve。エラーが発生しても続行し、失敗を記録する。
返信テンプレート:
| 対応タイプ | 返信例 |
|---|---|
| 修正完了 | 修正しました。ご指摘ありがとうございます。 |
| 対応しない | [理由] のため、現状のままとさせてください。 |
| 対応済み | [コミット hash] で対応済みです。 |
ソース参照ルール:
理由を添えて返信する場合 (対応しない、内容次第で対応不要と判断した場合など)、信頼できるソースの情報を参照できるときはコメントにも記載する。
例:
Go の仕様上、nil map への読み取りはゼロ値を返すためパニックしません。