Claude CodeやClaude Code Actionを用いて、AIに自律的にPull Requestのレビューを行なってもらうとき、いくつかの課題があった。その解決のためにPull Requestのレビュー操作に特化したgithub-pr-review-operationというClaude Skillsを作ったので紹介します。
課題
次の3つの課題があった。
- インラインコメントを付ける時に、コメントする行を間違える
- Claude Code ActionにはインラインコメントをつけるMCPが同梱されているが、ある行に対する指摘内容を別の行にコメントを付けることが多かった
- そのPull Requestに過去ついたコメントを考慮してと指定しても、うまくコメント一覧を取得できない
- 通常のコメントは取得できてもインラインコメントは取得せずに進めてしまうなど
- そのPull Requestについたコメントに対してうまくReplyを付けることができない
- 特定コメントに対して、別のインラインコメントで返信していた
これらの課題は全てghの操作をうまく行うだけで解決できるのだが、LLMの確率論的な特性上、うまくいくこともあればいかないこともあった。
Claude Skillsを使って操作をClaude Codeに教える
先ほどいったとおり、課題を解決するにはClaude Codeに対してghの操作を教えてあげれば良さそうだ。その目的にはClaude Skillsが使えそうなので、Pull Requestのレビュー操作に特化したClaude Skillsを自作した。
Skillsを作るときはClaudeの公式でSkill Generatorというデフォルトで組み込まれているスキルを活用すると良い。今回はClaude Desktopで以下プロンプトを入れてベース部分を作った後、使ってみながら色々な調整を加えていった。
GitHubのPullRequestのレビューのため、それに必要な操作をスキル化したい。必要な操作は - Pull Requestのtitleやdescriptionを取得する - Pull Requestのファイル変更を取得する - Pull Requestのコメントを全て取得する - Pull Requestにコメントする - Pull Requestのline指定でinline commentする - Pull Requestの特定のコメントへ返信する
最終的にすごくシンプルにSKILL.mdだけの構成にした。これを使うだけで、上記課題はほぼ100%解決するようになった。便利。
SKILL.md:
--- name: github-pr-review-operation description: GitHub Pull Requestのレビュー操作を行うスキル。PR情報取得、差分確認、コメント取得・投稿、インラインコメント、コメント返信をghコマンドで実行する。PRレビュー、コードレビュー、PR操作が必要な時に使用。 --- # GitHub PR Review Operation GitHub CLI (`gh`) を使ったPRレビュー操作。 ## 前提条件 - `gh` インストール済み - `gh auth login` で認証済み ## PR URLのパース PR URL `https://github.com/OWNER/REPO/pull/NUMBER` から以下を抽出して使用: - `OWNER`: リポジトリオーナー - `REPO`: リポジトリ名 - `NUMBER`: PR番号 ## 操作一覧 ### 1. PR情報取得 ```bash gh pr view NUMBER --repo OWNER/REPO --json title,body,author,state,baseRefName,headRefName,url ``` ### 2. 差分取得(行番号付き) ```bash gh pr diff NUMBER --repo OWNER/REPO | awk ' /^@@/ { match($0, /-([0-9]+)/, old) match($0, /\+([0-9]+)/, new) old_line = old[1] new_line = new[1] print $0 next } /^-/ { printf "L%-4d | %s\n", old_line++, $0; next } /^\+/ { printf " R%-4d| %s\n", new_line++, $0; next } /^ / { printf "L%-4d R%-4d| %s\n", old_line++, new_line++, $0; next } { print } ' ``` 出力例: ``` @@ -46,15 +46,25 @@ jobs: L46 R46 | prompt: | L49 | - (削除行) R49 | + (追加行) L50 R50 | # レビューガイドライン ``` - `L数字`: LEFT(base)側の行番号 → インラインコメントで`side=LEFT`に使用 - `R数字`: RIGHT(head)側の行番号 → インラインコメントで`side=RIGHT`に使用 ### 3. コメント取得 Issue Comments(PR全体へのコメント): ```bash gh api repos/OWNER/REPO/issues/NUMBER/comments --jq '.[] | {id, user: .user.login, created_at, body}' ``` Review Comments(コード行へのコメント): ```bash gh api repos/OWNER/REPO/pulls/NUMBER/comments --jq '.[] | {id, user: .user.login, path, line, created_at, body, in_reply_to_id}' ``` ### 4. PRにコメント ```bash gh pr comment NUMBER --repo OWNER/REPO --body "コメント内容" ``` ### 5. インラインコメント(コード行指定) まずhead commit SHAを取得: ```bash gh api repos/OWNER/REPO/pulls/NUMBER --jq '.head.sha' ``` 単一行コメント: ```bash gh api repos/OWNER/REPO/pulls/NUMBER/comments \ --method POST \ -f body="コメント内容" \ -f commit_id="COMMIT_SHA" \ -f path="src/example.py" \ -F line=15 \ -f side=RIGHT ``` 複数行コメント(10〜15行目): ```bash gh api repos/OWNER/REPO/pulls/NUMBER/comments \ --method POST \ -f body="コメント内容" \ -f commit_id="COMMIT_SHA" \ -f path="src/example.py" \ -F line=15 \ -f side=RIGHT \ -F start_line=10 \ -f start_side=RIGHT ``` **注意点:** - `-F` (大文字): 数値パラメータ(`line`, `start_line`)に使用。`-f`だと文字列になりエラーになる - `side`: `RIGHT`(追加行)または `LEFT`(削除行) ### 6. コメントへ返信 ```bash gh api repos/OWNER/REPO/pulls/NUMBER/comments/COMMENT_ID/replies \ --method POST \ -f body="返信内容" ``` `COMMENT_ID`はコメント取得で得た`id`を使用。
工夫ポイント: インラインコメントを正しい行に付けられるように
インラインコメントを正しい行へ付けられるようにするのがなかなか難しく、Skillsの自動生成後に一番調整を行なった。
なぜインラインコメントが正しい行に付けられないか説明する。gh pr diffコマンドで差分を取得すると、git diffの形式で差分が取得できる。たとえば https://github.com/shibayu36/slack-explorer-mcp/pull/20 から取得して抜粋すると次のようになる。
diff --git a/main.go b/main.go index 3029a7a..9383209 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "context" "log/slog" + "net/http" "os" "github.com/mark3labs/mcp-go/mcp" @@ -163,17 +164,55 @@ func main() { handler.SearchUsersByName, ) - if err := server.ServeStdio(s, server.WithStdioContextFunc(func(ctx context.Context) context.Context { - ctx = WithSlackTokenFromEnv(ctx) + transport := os.Getenv("TRANSPORT") + if transport == "" { + transport = "stdio" + } + + switch transport { + case "stdio": + if err := server.ServeStdio(s, server.WithStdioContextFunc(func(ctx context.Context) context.Context { + ctx = WithSlackTokenFromEnv(ctx) - // Add session ID from ClientSession - if session := server.ClientSessionFromContext(ctx); session != nil { - ctx = WithSessionID(ctx, SessionID(session.SessionID()))
さらにインラインコメントを付けるときは行番号 + side(LEFT=削除行かRIGHT=追加行)を指定する必要がある。
gh api repos/OWNER/REPO/pulls/NUMBER/comments \ --method POST \ -f body="コメント内容" \ -f commit_id="COMMIT_SHA" \ -f path="src/example.py" \ -F line=15 \ -f side=RIGHT
この2つのAPIインターフェースのため、diffを取得した後にインラインコメントを付けるにはgit diffの@@ -163,17 +164,55 @@ func main() { のような行から行番号を抽出し、さらに行頭の-や+を考慮して、sideのLEFT=削除行かRIGHT=追加行のどちらかを決定し、その二つの組み合わせでコメントする必要がある。この抽出操作がLLMにとって難しく(Opus-4.5を使っても同様だった)、インラインコメントを別の行に付けてしまうという課題が生じていた。
ただ、よく考えるとGitHubのWeb上のFiles changedのページの情報を見れば、以下のようにインラインコメントを付けるための情報はすぐにわかる。

じゃあCLIから同じような情報を作ればいいじゃんということで作ったコマンドが、SKILL.mdの2. 差分取得(行番号付き)の部分。
gh pr diff NUMBER --repo OWNER/REPO | awk '
/^@@/ {
match($0, /-([0-9]+)/, old)
match($0, /\+([0-9]+)/, new)
old_line = old[1]
new_line = new[1]
print $0
next
}
/^-/ { printf "L%-4d | %s\n", old_line++, $0; next }
/^\+/ { printf " R%-4d| %s\n", new_line++, $0; next }
/^ / { printf "L%-4d R%-4d| %s\n", old_line++, new_line++, $0; next }
{ print }
'
これを使って https://github.com/shibayu36/slack-explorer-mcp/pull/20 のdiffを取得すると、Web上のFiles changedのページと同じような情報がテキストで取得できる。
@@ -163,17 +164,55 @@ func main() {
L163 R164 | handler.SearchUsersByName,
L164 R165 | )
L165 R166 |
L166 | - if err := server.ServeStdio(s, server.WithStdioContextFunc(func(ctx context.Context) context.Context {
L167 | - ctx = WithSlackTokenFromEnv(ctx)
R167 | + transport := os.Getenv("TRANSPORT")
R168 | + if transport == "" {
R169 | + transport = "stdio"
R170 | + }
R171 | +
R172 | + switch transport {
R173 | + case "stdio":
R174 | + if err := server.ServeStdio(s, server.WithStdioContextFunc(func(ctx context.Context) context.Context {
R175 | + ctx = WithSlackTokenFromEnv(ctx)
L168 R176 |
L169 | - // Add session ID from ClientSession
L170 | - if session := server.ClientSessionFromContext(ctx); session != nil {
L171 | - ctx = WithSessionID(ctx, SessionID(session.SessionID()))
あとは左に出ているL163 R164 | のような行から、インラインコメントを付ける情報を簡単にゲットできるため、Claude Codeは正しい位置にコメントが付けられるようになった。
まとめ
今回はClaude CodeやClaude Code Actionを用いて、AIに自律的にPull Requestのレビューを行なってもらうときに便利に使えるClaude Skillsの紹介をした。.claude/skills/github-pr-review-operation/配下に上記したSKILL.mdを配置するだけで使えるのでご利用ください。

