Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:32:25 +08:00
commit 5921fa5e52
6 changed files with 367 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"name": "pr-review-action-flow",
"description": "GitHub の PR レビューコメントを取得・精査し、修正と返信まで Claude Code から行うためのプラグインです。",
"version": "0.0.0-2025.11.28",
"author": {
"name": "hatayama",
"email": "zhongweili@tubi.tv"
},
"commands": [
"./commands"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# pr-review-action-flow
GitHub の PR レビューコメントを取得・精査し、修正と返信まで Claude Code から行うためのプラグインです。

View File

@@ -0,0 +1,97 @@
---
description: "PRのレビューコメントを確認し、妥当性を判断して適切に対応します"
allowed_tools: Bash(commands/address-review-comments/*), Bash(git:*), Bash(gh:*), Read, Edit, Write, AskUserQuestion
model: sonnet
---
PRのレビューコメントを確認し、適切な対応を行います。
## コマンド形式
```
/address-review-comments <PR URL> [GitHub username]
```
- **PR URL**: 対応するPRのURL必須
- **GitHub username**: 使用するGitHubアカウント名オプション
- 指定した場合、自動的に `gh auth switch --user <username>` を実行
### 使用例
```bash
# デフォルトアカウントで実行
/address-review-comments https://github.com/your-org/your-repo/pull/13249
# user-a アカウントに切り替えて実行
/address-review-comments https://github.com/your-org/your-repo/pull/13249 user-a
# user-b アカウントに切り替えて実行
/address-review-comments https://github.com/your-org/your-repo/pull/13249 user-b
```
## 基本フロー
0. **GitHubアカウントの切り替え**username指定時のみ
```bash
gh auth switch --user <username>
```
- 指定されたGitHubアカウントに切り替え
1. **未解決レビューコメントの取得**
```bash
commands/address-review-comments/get-review-comments.sh <PR URL>
```
- 未解決のレビューコメント一覧を取得
2. **各コメントの精査と妥当性判断**
- 各コメントの内容を読み、技術的妥当性を判断
- ```suggestion ブロックがあっても、その内容が適切かを必ず確認
- ✅ **妥当な指摘**: 修正を適用ステップ3へ
- ❌ **不当な指摘**: 理由をユーザーに説明ステップ4へ
3. **妥当な指摘への対応**
- Edit/Write ツールを使って該当箇所を修正
- 修正内容をユーザーに提示
- **👤 ユーザー確認を取る**: 「この修正で commit・push してよいか?」
- OK が出たら commit・push を実行
- `reply-review-comment.sh` で返信該当コミットSHAを含める
4. **不当な指摘への対応**
- 不当と判断した理由をユーザーに説明
- ユーザーの承認を得てから、`reply-review-comment.sh` で返信
## 重要な注意事項
- ✅ **AI が自分で判断して Edit ツールで修正を適用する**
- ✅ **```suggestion ブロックの内容も無批判に受け入れない**
- ✅ **必ずユーザー確認を経てから commit・push する**
- ✅ **レビューコメントを無批判に受け入れない**
## 実行前の準備
gh コマンドの認証が設定されていることを確認してください:
```bash
gh auth status || gh auth login
```
**注意**: アカウント切り替えが必要な場合は、コマンドの2つ目の引数にGitHubユーザー名を指定してください。
このコマンドは、リポジトリのルートに `commands/address-review-comments/` が存在することを前提としています。
## レビューコメントへの返信
修正を commit・push した後、レビューコメントに返信:
```bash
commands/address-review-comments/reply-review-comment.sh <COMMENT_ID> "返信本文"
```
返信メッセージには必ず以下を含めること:
- 該当コミットのSHA短縮版
- 何を修正したかの説明
## 開発者向け情報
### スクリプト構成
- **get-review-comments.sh**: 未解決コメント取得
- **reply-review-comment.sh**: レビューコメント返信

View File

@@ -0,0 +1,126 @@
#!/bin/sh
# resolve-review.sh
# 指定した PR の未解決レビューコメントを GraphQL で取得し、
# resolve-review 系 slash command 用に 1 行 1 JSON オブジェクトとして標準出力に返すヘルパースクリプト。
set -eu
if [ "$#" -eq 0 ]; then
printf 'Usage: %s <PR number or PR URL>\n' "$(basename "$0")" >&2
exit 1
fi
readonly INPUT="$1"
OWNER="${OWNER:-}"
REPO="${REPO:-}"
ensure_repo_context() {
if [ -n "$OWNER" ] && [ -n "$REPO" ]; then
return
fi
repo_payload="$(gh repo view --json owner,name --jq '.owner.login + " " + .name' 2>/dev/null || true)"
if [ -z "$repo_payload" ]; then
return
fi
repo_owner=${repo_payload%% *}
repo_name=${repo_payload##* }
if [ -z "$OWNER" ] && [ -n "$repo_owner" ]; then
OWNER="$repo_owner"
fi
if [ -z "$REPO" ] && [ -n "$repo_name" ]; then
REPO="$repo_name"
fi
}
ensure_repo_context
PR=""
case "$INPUT" in
http://*|https://*)
parsed="$(printf '%s' "$INPUT" | sed -E 's#^https?://github\\.com/([^/]+)/([^/]+)/pull/([0-9]+).*$#\1 \2 \3#')"
if [ -n "$parsed" ]; then
IFS=' ' read -r parsed_owner parsed_repo parsed_number <<EOF
$parsed
EOF
if [ -z "$OWNER" ]; then
OWNER="$parsed_owner"
fi
if [ -z "$REPO" ]; then
REPO="$parsed_repo"
fi
if [ -z "$PR" ]; then
PR="$parsed_number"
fi
fi
if [ -z "$PR" ]; then
PR="$(gh pr view "$INPUT" --json number -q .number 2>/dev/null || true)"
fi
;;
*)
PR="$INPUT"
;;
esac
if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
printf 'owner/repo を特定できませんでした。OWNER/REPO を環境変数で指定してください\n' >&2
exit 1
fi
if [ -z "$PR" ]; then
printf 'PR を特定できませんでした。PR番号またはURLを指定してください\n' >&2
exit 1
fi
gh api graphql \
-F owner="$OWNER" -F repo="$REPO" -F pr="$PR" \
-f query='
query($owner:String!, $repo:String!, $pr:Int!){
repository(owner:$owner, name:$repo){
pullRequest(number:$pr){
reviewThreads(first: 100){
nodes{
isResolved
isOutdated
resolvedBy { login }
comments(first: 100){
nodes{
databaseId
author { login }
path
body
line
originalLine
createdAt
}
}
}
}
}
}
}' \
--jq '
.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved | not)
| . as $t
| $t.comments.nodes[]
| {
resolved: $t.isResolved,
outdated: $t.isOutdated,
resolvedBy: ($t.resolvedBy.login // null),
id: .databaseId,
user: .author.login,
path: .path,
line: (.line // .originalLine),
body: .body,
createdAt
}'

View File

@@ -0,0 +1,76 @@
#!/bin/sh
# PR のレビューコメントスレッドに GitHub REST API で返信するヘルパースクリプト。
# resolve-review-run.sh から呼び出され、元コメント ID からスレッドのルートコメントを特定して返信を投稿する。
set -eu
usage() {
printf 'Usage: %s <comment-id> <reply message>\n' "$(basename "$0")" >&2
printf 'Example: %s 123456 \"LGTM です\"\n' "$(basename "$0")" >&2
}
if [ "$#" -lt 2 ]; then
usage
exit 1
fi
readonly COMMENT_ID="$1"
shift
readonly MESSAGE="$*"
if [ -z "$MESSAGE" ]; then
printf '返信本文を指定してください\n' >&2
exit 1
fi
OWNER="${OWNER:-}"
REPO="${REPO:-}"
if [ -z "$OWNER" ]; then
OWNER="$(gh repo view --json owner -q .owner.login 2>/dev/null || true)"
fi
if [ -z "$REPO" ]; then
REPO="$(gh repo view --json name -q .name 2>/dev/null || true)"
fi
if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
printf 'owner/repo を特定できませんでした。OWNER/REPO を環境変数で指定してください\n' >&2
exit 1
fi
comment_info="$(gh api \"repos/$OWNER/$REPO/pulls/comments/$COMMENT_ID\" 2>/dev/null || true)"
if [ -z "$comment_info" ]; then
printf 'コメント %s の取得に失敗しました。gh api のログを確認してください\n' \"$COMMENT_ID\" >&2
exit 1
fi
root_comment_id=\"$(printf '%s' \"$comment_info\" | jq -r '(.in_reply_to_id // .id)')\"
if [ -z \"$root_comment_id\" ] || [ \"$root_comment_id\" = \"null\" ]; then
printf 'コメント %s を特定できませんでした。ID が正しいか確認してください\n' \"$COMMENT_ID\" >&2
exit 1
fi
pull_request_url=\"$(printf '%s' \"$comment_info\" | jq -r '.pull_request_url')\"
pull_number=\"$(printf '%s' \"$pull_request_url\" | sed -E 's#.*/pulls/([0-9]+)$#\1#')\"
if [ -z \"$pull_number\" ] || [ \"$pull_number\" = \"null\" ]; then
printf 'PR番号を特定できませんでした\n' >&2
exit 1
fi
if ! reply_url=\"$(gh api \
--method POST \
\"repos/$OWNER/$REPO/pulls/$pull_number/comments/$root_comment_id/replies\" \
--raw-field body=\"$MESSAGE\" \
--jq '.html_url')\" ; then
printf '返信投稿に失敗しました。gh api のログを確認してください\n' >&2
exit 1
fi
printf '返信を投稿しました: %s\n' \"$reply_url\"

53
plugin.lock.json Normal file
View File

@@ -0,0 +1,53 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:hatayama/PRReviewActionFlow:",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "97804fe2d3766533631bf7367440ba855bc2fecd",
"treeHash": "a9869e2946351b1c99d6c046c2399980daea731052ca5e4b82437eefdba96603",
"generatedAt": "2025-11-28T10:17:23.108705Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "pr-review-action-flow",
"description": "GitHub の PR レビューコメントを取得・精査し、修正と返信まで Claude Code から行うためのプラグインです。",
"version": null
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "c8ad2a044dda48dc84c5d06da355ff4d42a269af392e6d3e3ce32b9279e58012"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "aae455fc2deea72e7e968147e5319fc9756bd62df8a546e9ee3e908c6b49aaf0"
},
{
"path": "commands/address-review-comments.md",
"sha256": "9afaf2297fd05959f9ca6f119549366145a076f90b50702894a1d70af5627040"
},
{
"path": "commands/address-review-comments/reply-review-comment.sh",
"sha256": "d0b46b8fd3e624c203a7768c91c80256e1d8e2f4a0a902200b800ada5d406205"
},
{
"path": "commands/address-review-comments/get-review-comments.sh",
"sha256": "2895dbfb3017203a764ff9565e2558cc35f556ba41d8008f38263f22faf37442"
}
],
"dirSha256": "a9869e2946351b1c99d6c046c2399980daea731052ca5e4b82437eefdba96603"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}