skills/pr-fix/SKILL.md
PRの未解決コメントを1回修正してpushする。定期実行は /pr-fix-loop を使う。「/pr-fix」「コメント直して」と言った時に使用。
npx skillsauth add hirokimry/vibecorp pr-fixInstall 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.
[!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 <path> が指定された場合、全操作を指定パス内で実行する。
cd <path> && command で実行する。<path>/ を基準とした絶対パスを使用する。--worktree <path> を引き継ぐ。$CLAUDE_PROJECT_DIR: worktree モードでは <path> に置き換える。/vibecorp: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 が見つからない場合はエラー。
gh pr view {pr_number} --json state --jq '.state'
MERGED → 「PR #{pr_number} はマージ済みです」と報告して 正常終了。CLOSED → 「PR #{pr_number} はクローズされています」と報告して 正常終了。OPEN → ステップ 2.5 へ進む。gh pr view {pr_number} --json statusCheckRollup --jq '.statusCheckRollup[] | {name, status, conclusion, detailsUrl}'
| conclusion | 分類 | 行動 |
|---|---|---|
| SUCCESS, NEUTRAL, SKIPPED | CI green | 修正不要 |
| FAILURE, CANCELLED, TIMED_OUT, ACTION_REQUIRED | CI 失敗 | 修正対象 |
| null(status が IN_PROGRESS / QUEUED) | CI 待機(PENDING) | 修正対象外、次イテレーション待ち |
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 行程度に絞り込んで修正コンテキストに渡す。
ログに以下のキーワードが含まれる場合は外部要因と判定し、CEO にエスカレーションして 停止する。
Rate limit, 429, ECONNREFUSED, network is unreachable, could not resolve host, npm install failed, ETIMEDOUT, ENOTFOUND, socket hang up
外部要因と判定した場合:
全 check run の conclusion が SUCCESS / NEUTRAL / SKIPPED のいずれかで、かつ PENDING が 0 件の場合に CI green と判定する。
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 へ進む。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 解除後に再実行してください」と報告して 停止する。
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 で使用する。未解決 0 件 かつ CI 失敗 0 件(PENDING 残存は許容)→ 「対応不要」と報告して正常終了する。 未解決あり または CI 失敗あり → ステップ 6 へ進む。
レビュー判定(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 のみで 適用する。
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 で前提化されているため実運用上稀。
.claude/rules/review-handling.md の捌き基準(intent × severity)と .claude/rules/severity/claude-action.md / severity/coderabbit.md の severity 定義に従い、指摘を分類する。判定の入力としては 6.0 で取得した PR_INTENT を使う(PR ラベルではなく Issue ラベル直接参照)。設計方針に関わる大きな変更はユーザーに確認する。
要修正リストに対して、各指摘の具体的な修正計画を策定する。このステップではコードの変更は行わない。
手順:
各指摘について以下を明記する。
| 項目 | 内容 | |---|---| | 修正内容 | 何をどう変更するか | | 影響範囲 | 変更が影響する他のファイル・テスト | | 注意点 | 修正時に気をつけるべきこと |
修正計画に従ってコードを修正する。未解決コメントと CI 失敗を同一コンテキストで修正する。
修正した指摘: 返信不要。次のステップ(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)。/vibecorp:commit を使用してコミットし(worktree モードでは --worktree <path> を引き継ぐ)、リモートに push する。
git push
## /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 は使用しない。@coderabbitai approve の投稿は禁止 — approve は CodeRabbit が自動発行するか、人間が手動で行う。Closes #N / Refs #N を保持する — gh pr edit --body で本文を書き換える場合、既存の Closes #N / Refs #N 行を消さない。新規追加する場合も #N 形式(URL 形式不可)で記載する。詳細は .claude/rules/workflow.md「PR 本文の Issue リンク(auto-close キーワード)」を参照。\(...) を使わない — 必ず + で結合する(根拠)。2>/dev/null、|| echo、; echo 等のリダイレクトやフォールバックを付加しない(根拠)。/vibecorp:pr-fix-loop/vibecorp:pr.claude/rules/review-handling.md.claude/rules/review-observations.md.claude/rules/severity/coderabbit.md.claude/rules/severity/claude-action.md.claude/rules/intent-labels.md.claude/rules/workflow.md.claude/rules/prompt-writing.md.claude/rules/markdown.md.claude/rules/shell.mddata-ai
skills/**/SKILL.md 内に embed された 5 行以上のエージェント呼出プロンプトテンプレ・長文ブロックを .claude/rules/notification-prompt-extraction.md 基準で skills/<skill>/prompts/<name>.md に切り出す migration skill。「/prompts-extract-all」「プロンプト切り出し」「プロンプト extract」「SKILL.md プロンプト migration」と言った時に使用。検出は awk でフェンスコードブロックを抽出して行数カウント、要否判定は LLM が閾値・用途軸・命名規約と照合。diff 提案 → CEO 承認 → 書換の 2 段階で挙動を壊さず適用する。自動マージ禁止、自律ループ対象外。
documentation
.github/workflows/**/*.{yml,yaml} の --body 通知文と hooks/**/*.sh の長文 echo/printf/heredoc を .claude/rules/notification-prompt-extraction.md 基準で個別 .md ファイルに切り出す migration skill。「/notifications-extract-all」「通知文切り出し」「通知文 extract」「workflow 通知 migration」と言った時に使用。検出は grep で機械絞り込み、要否判定は LLM が閾値・命名規約と照合。diff 提案 → CEO 承認 → 書換の 2 段階で挙動を壊さず適用する。自動マージ禁止、自律ループ対象外。
development
`**/*.sh` / `**/*.js` / `**/*.ts` / `**/*.py` / `**/*.rb` / `**/*.go` / 設定ファイル等のコード内コメントを一括棚卸しし、 `.claude/rules/code-comments.md` と機械的に照合する。 diff 提案 → CEO 承認 → 書換の 2 段階で自動マージを禁じる。 生成コード・`node_modules`・`vendor`・`dist`・`build` 等は除外する。 「/vibecorp:comments-rewrite-all」「コメント全書き直し」「コード内コメント棚卸し」 と言った時に使用。
development
skills/**/SKILL.md・agents/*.md・.claude/rules/*.md を .claude/rules/prompt-writing.md 基準で一括書き直し提案するスキル。「/prompts-rewrite-all」「プロンプト書き直し」「スキル一括書き直し」「エージェント書き直し」と言った時に使用。claude-code-guide サブエージェントで Claude Code 公式仕様(docs.claude.com)を確認し、prompt-writing.md の指針 MUST / 禁止パターンと照合する。diff 提案 → CEO 承認 → 書き換えの 2 段階で挙動を壊さず適用する。自動マージ禁止。