Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal 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
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# pr-review-action-flow
|
||||
|
||||
GitHub の PR レビューコメントを取得・精査し、修正と返信まで Claude Code から行うためのプラグインです。
|
||||
97
commands/address-review-comments.md
Normal file
97
commands/address-review-comments.md
Normal 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**: レビューコメント返信
|
||||
126
commands/address-review-comments/get-review-comments.sh
Normal file
126
commands/address-review-comments/get-review-comments.sh
Normal 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
|
||||
}'
|
||||
|
||||
|
||||
76
commands/address-review-comments/reply-review-comment.sh
Normal file
76
commands/address-review-comments/reply-review-comment.sh
Normal 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
53
plugin.lock.json
Normal 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": []
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user