pr-fix
PRの未解決コメントを1回修正してpushする。定期実行は /pr-fix-loop を使う。「/pr-fix」「コメント直して」と言った時に使用。
SKILL.mdskills/pr-fix/SKILL.md
GitHub🐇 PR レビュー修正(単発)
[!IMPORTANT] このスキルは PR の 未解決コメント + CI 失敗 を 1 回だけ修正して push する。 定期実行は
/vibecorp:pr-fix-loopを使う(本スキルはループしない)。 レビュー判定の SoT は Issue ラベル(PR ラベルではない、Issue #575)。intent はgh issue view --json labelsで直接参照する。 4 段フォールバックで Issue 番号を解決できない場合は severity-only fallback(Critical / Major のみ修正、Minor 以下はスキップ)に切替える。
PR の現在の状態を確認し、未解決コメントがあれば修正して push する。1 回の実行で現在の指摘を処理して終了する。
📖 使用方法
/vibecorp:pr-fix # 現在のブランチのPRを自動検出
/vibecorp:pr-fix <PR URL> # PR URLを直接指定
/vibecorp:pr-fix --worktree <path> # worktree 内で実行
🌳 worktree モード
--worktree <path> が指定された場合、全操作を指定パス内で実行する。
- Bash: 全コマンドを
cd <path> && commandで実行する。 - Read/Write/Edit:
<path>/を基準とした絶対パスを使用する。 - サブスキル呼び出し:
--worktree <path>を引き継ぐ。 - 未指定時は従来通り CWD で実行する(後方互換)。
$CLAUDE_PROJECT_DIR: worktree モードでは<path>に置き換える。
✅ 前提条件
- PR が既に作成されていること(未作成なら
/vibecorp:prを先に実行する)。 - 現在のブランチが PR の head ブランチであること。
🔄 ワークフロー
1. PR 情報を取得
PR URL が指定された場合: URL から owner/repo/PR 番号を抽出する。
PR URL が未指定の場合: 現在のブランチから自動検出する。
gh pr view --json number,url,headRefName,baseRefName,state --jq '{number, url, headRefName, baseRefName, state}'
PR が見つからない場合はエラー。
2. マージ済みチェック
gh pr view {pr_number} --json state --jq '.state'
MERGED→ 「PR #{pr_number} はマージ済みです」と報告して 正常終了。CLOSED→ 「PR #{pr_number} はクローズされています」と報告して 正常終了。OPEN→ ステップ 2.5 へ進む。
2.5. CI ステータスの取得
gh pr view {pr_number} --json statusCheckRollup --jq '.statusCheckRollup[] | {name, status, conclusion, detailsUrl}'
CI 状態の分類
| conclusion | 分類 | 行動 |
|---|---|---|
SUCCESS, NEUTRAL, SKIPPED |
CI green | 修正不要 |
FAILURE, CANCELLED, TIMED_OUT, ACTION_REQUIRED |
CI 失敗 | 修正対象 |
null(status が IN_PROGRESS / QUEUED) |
CI 待機(PENDING) | 修正対象外、次イテレーション待ち |
CI 失敗時のログ取得
CI 失敗が 1 件以上ある場合、detailsUrl から run_id を抽出し、失敗ジョブのログを取得する。
# detailsUrl 例: https://github.com/{owner}/{repo}/actions/runs/{run_id}/jobs/{job_id}
# run_id の抽出: detailsUrl の /runs/ と /jobs/ の間のセグメント
gh run view {run_id} --log-failed
末尾 200 行程度に絞り込んで修正コンテキストに渡す。
外部要因 CI 失敗の判定
ログに以下のキーワードが含まれる場合は外部要因と判定し、CEO にエスカレーションして 停止する。
Rate limit, 429, ECONNREFUSED, network is unreachable, could not resolve host, npm install failed, ETIMEDOUT, ENOTFOUND, socket hang up
外部要因と判定した場合:
- 「CI 失敗は外部要因({キーワード})のため、リポジトリ側の修正では解消できません」と報告して 停止する。
CI green 判定
全 check run の conclusion が SUCCESS / NEUTRAL / SKIPPED のいずれかで、かつ PENDING が 0 件の場合に CI green と判定する。
3. CodeRabbit 有効性の確認
awk '/^coderabbit:/{found=1; next} found && /^[^ ]/{exit} found && /enabled:/{print $2}' \
"$CLAUDE_PROJECT_DIR"/.claude/vibecorp.yml
- 結果が
false→ CodeRabbit 無効。ステップ 7(auto-merge 確認)へ直接進む。 - 結果が
trueまたは空(未定義)→ CodeRabbit 有効。ステップ 4 へ進む。
4. rate limit チェック
gh api repos/{owner}/{repo}/issues/{pr_number}/comments \
--paginate \
--jq '[.[] | select(.user.login | test("coderabbit"; "i")) | select(.body | test("[Rr]ate limit"))] | length'
1 以上なら「CodeRabbit が rate limit 中のため停止しています。rate limit 解除後に再実行してください」と報告して 停止する。
5. 未解決スレッドの取得
GraphQL API で未解決の CodeRabbit レビュースレッドを取得する。レビュースレッドが 100 件を超える PR では 1 ページだけ取ると見落とすため、pageInfo.hasNextPage で全ページを巡回する。
threads_all="[]"
cursor=""
while :; do
if [ -n "$cursor" ]; then
after_arg="-f after=$cursor"
else
after_arg=""
fi
page="$(gh api graphql \
-f owner="{owner}" \
-f repo="{repo}" \
-F number={pr_number} \
$after_arg \
-f query='
query($owner: String!, $repo: String!, $number: Int!, $after: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100, after: $after) {
pageInfo { hasNextPage endCursor }
nodes {
isResolved
id
comments(first: 10) {
nodes {
id
databaseId
author { login }
body
path
line
}
}
}
}
}
}
}')"
page_nodes="$(printf '%s' "$page" | jq '.data.repository.pullRequest.reviewThreads.nodes')"
threads_all="$(jq -s 'add' <(printf '%s' "$threads_all") <(printf '%s' "$page_nodes"))"
has_next="$(printf '%s' "$page" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage')"
if [ "$has_next" != "true" ]; then
break
fi
cursor="$(printf '%s' "$page" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor')"
done
# 未解決 + 先頭コメントが CodeRabbit のスレッドのみ抽出
unresolved="$(printf '%s' "$threads_all" \
| jq '[.[] | select(.isResolved == false)
| select(.comments.nodes[0].author.login | test("coderabbit"; "i"))]')"
isResolved == falseかつ先頭コメントが CodeRabbit のスレッドのみ抽出する。- 各スレッドの
id(thread node ID)は却下時の resolve mutation で使用する。 - ページネーションにより 100 件を超えるレビュースレッドも漏れなく取得する。
未解決 0 件 かつ CI 失敗 0 件(PENDING 残存は許容)→ 「対応不要」と報告して正常終了する。 未解決あり または CI 失敗あり → ステップ 6 へ進む。
6. 指摘の修正
6.0 intent ラベルの取得(Issue 側から直接参照)
レビュー判定(intent × severity)の SoT は Issue ラベル。PR には intent ラベルを付与しないため(Issue #575)、Issue 番号を解決して gh issue view --json labels で intent を取得する。
4 段フォールバック:
| 優先 | 取得経路 | コマンド例 |
|---|---|---|
| 1 | closingIssuesReferences(GitHub 自動 close キーワード由来) |
gh pr view {pr_number} --json closingIssuesReferences --jq '.closingIssuesReferences[0].number // empty' |
| 2 | PR 本文 grep(--ref 経由 PR / 手動編集対応) |
pr-issue-link-check.yml 互換正規表現 (close[sd]?|fix(es|ed)?|resolve[sd]?|refs?)[[:space:]]+#([0-9]+) で PR 本文を解析。最初のマッチを採用 |
| 3 | ブランチ名 | dev/<num>_* パターンから <num> 抽出(gh pr view --json headRefName --jq '.headRefName') |
| 4 | 空(severity-only fallback) | warning ログ + severity-only 判定モードに切替 |
# Step 1: closingIssuesReferences(実機検証済み: フラット配列、nodes ラッパー無し)
ISSUE_NUM=$(gh pr view {pr_number} --json closingIssuesReferences --jq '.closingIssuesReferences[0].number // empty')
# Step 2: PR 本文 grep(#N 形式 + GitHub URL 形式の両対応、pr-issue-link-check.yml と互換)
if [ -z "$ISSUE_NUM" ]; then
PR_BODY=$(gh pr view {pr_number} --json body --jq '.body')
ISSUE_NUM=$(printf '%s' "$PR_BODY" \
| grep -oiE '(close[sd]?|fix(es|ed)?|resolve[sd]?|refs?)[[:space:]]+(#[0-9]+|https?://[^[:space:]]+/issues/[0-9]+)' \
| head -1 \
| grep -oE '[0-9]+$')
fi
# Step 3: ブランチ名
if [ -z "$ISSUE_NUM" ]; then
HEAD_REF=$(gh pr view {pr_number} --json headRefName --jq '.headRefName')
ISSUE_NUM=$(printf '%s' "$HEAD_REF" | grep -oE '^dev/([0-9]+)_' | grep -oE '[0-9]+')
fi
# Step 4: intent ラベル取得(ISSUE_NUM の数値検証を実施した上で gh に渡す)
PR_INTENT=""
if [ -n "$ISSUE_NUM" ]; then
if [[ "$ISSUE_NUM" =~ ^[0-9]+$ ]]; then
PR_INTENT=$(gh issue view "$ISSUE_NUM" --json labels --jq '[.labels[].name | select(startswith("intent/"))][0] // empty')
else
echo "[WARN] ISSUE_NUM が数値以外('$ISSUE_NUM')。intent 取得をスキップして severity-only fallback に切替" >&2
fi
fi
severity-only fallback の挙動:
Step 1-3 で Issue 番号を解決できない、または Issue に intent ラベルが付いていない場合、PR_INTENT が空となる。この場合は intent 重視軸判定が不可能となるため、.claude/rules/review-handling.md の判定基準を severity のみで 適用する。
- Critical / Major: 通常通り修正対象(intent 問わず必ず対応)。
- Minor / Trivial / Info(Minor 以下): 全スキップ(intent 重視軸該当判定が不能のため保守的に倒す)。
warning ログ [WARN] Issue 番号 / intent ラベルが解決できませんでした。severity-only fallback で Critical / Major のみ修正対象とします を出力する。
複数 Issue close PR の優先順位: 1 PR で複数 Issue を close する場合、closingIssuesReferences[0] が採用される(GitHub 仕様順)。1 PR 1 Issue 運用が pr-issue-link-check.yml で前提化されているため実運用上稀。
6.1 妥当性検証
.claude/rules/review-handling.md の捌き基準(intent × severity)と .claude/rules/severity/claude-action.md / severity/coderabbit.md の severity 定義に従い、指摘を分類する。判定の入力としては 6.0 で取得した PR_INTENT を使う(PR ラベルではなく Issue ラベル直接参照)。設計方針に関わる大きな変更はユーザーに確認する。
6.2 修正計画
要修正リストに対して、各指摘の具体的な修正計画を策定する。このステップではコードの変更は行わない。
手順:
- 指摘箇所の実コードと周辺コードを読む。
- 既存の類似実装パターンを確認する。
- 修正による影響範囲を特定する。
- 具体的な修正手順を策定する。
各指摘について以下を明記する。
| 項目 | 内容 |
|---|---|
| 修正内容 | 何をどう変更するか |
| 影響範囲 | 変更が影響する他のファイル・テスト |
| 注意点 | 修正時に気をつけるべきこと |
6.3 修正実行
修正計画に従ってコードを修正する。未解決コメントと CI 失敗を同一コンテキストで修正する。
- 計画に記載された範囲のみを変更する。
- 修正後、関連するテスト・lint を実行して通過を確認する。
6.4 却下した指摘に返信・resolve
修正した指摘: 返信不要。次のステップ(6.5)の push 時に CodeRabbit の auto-resolve で自動的に resolved になる。
却下した指摘: 却下理由を返信した後、GraphQL mutation でスレッドを resolve する。
却下理由の返信:
gh api repos/{owner}/{repo}/pulls/{pr_number}/comments \
-X POST \
-f body="{却下理由の markdown}" \
-F in_reply_to={comment_database_id}
{comment_database_id}はステップ 5 で取得した先頭コメントのdatabaseId(REST API 整数 ID)。
返信後、即座にスレッドを resolve する:
gh api graphql -f query='
mutation {
resolveReviewThread(input: { threadId: "{thread_node_id}" }) {
thread { isResolved }
}
}'
{thread_node_id}はステップ 5 で取得した各スレッドのidフィールド(例:PRRT_xxx)。- 返信 → resolve の順序で実行する(resolve 済みスレッドには CodeRabbit は再反応しない)。
6.5 コミット・push
/vibecorp:commit を使用してコミットし(worktree モードでは --worktree <path> を引き継ぐ)、リモートに push する。
git push
7. 結果報告
## /vibecorp:pr-fix 完了
- PR: #{pr_number}
- レビュー修正: {n}件
- レビュー却下: {n}件
- CI 修正: {n}件
- CI 待機: {n}件
マージ済みの場合:
## /vibecorp:pr-fix 完了
- PR: #{pr_number}
- 状態: マージ済み
対応不要の場合:
## /vibecorp:pr-fix 完了
- PR: #{pr_number}
- 未解決コメント: なし
- CI: green(または PENDING {n}件)
⚠️ 制約
--force、--hard、--no-verifyは使用しない。- ユーザーの明示的な指示なしに force push しない。
- 判断に迷う指摘はユーザーに確認する。
- 修正前に必ず関連ファイルを読み込む。
@coderabbitai approveの投稿は禁止 — approve は CodeRabbit が自動発行するか、人間が手動で行う。- PR 本文修正時は auto-close キーワード
Closes #N/Refs #Nを保持する —gh pr edit --bodyで本文を書き換える場合、既存のCloses #N/Refs #N行を消さない。新規追加する場合も#N形式(URL 形式不可)で記載する。詳細は.claude/rules/workflow.md「PR 本文の Issue リンク(auto-close キーワード)」を参照。 - jq では string interpolation
\(...)を使わない — 必ず+で結合する(根拠)。 - コマンドをそのまま実行する —
2>/dev/null、|| echo、; echo等のリダイレクトやフォールバックを付加しない(根拠)。
🔗 関連ルール
- 定期実行ループ:
/vibecorp:pr-fix-loop - PR 作成スキル:
/vibecorp:pr - 捌き基準(intent × severity):
.claude/rules/review-handling.md - レビュー観点(intent 別):
.claude/rules/review-observations.md - severity 公式定義:
.claude/rules/severity/coderabbit.md - severity 実体版:
.claude/rules/severity/claude-action.md - intent ラベル定義:
.claude/rules/intent-labels.md - ワークフロー規約:
.claude/rules/workflow.md - プロンプト作成基準:
.claude/rules/prompt-writing.md - マークダウン規約:
.claude/rules/markdown.md - シェル規約:
.claude/rules/shell.md