🥷

Claude Code GitHub Actionsにインラインでサジェストさせて以前のコメントを隠す

に公開

こんにちは。ダイの大冒険エンジョイ勢のbun913と申します。

みなさんは Claude Code GitHub Actions を利用していますか?

https://docs.anthropic.com/ja/docs/claude-code/github-actions

例えばv1.0を利用する場合、以下のような記述でCIに Claude Code を簡単に組み込めて良いですよね。

name: Code Review
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: "/review"
          claude_args: "--max-turns 5"

ただデフォルトで動作させると、以下のように1つの長いコメントを投稿してしまうため、せっかく良いことを言っていても目が流れがちです。

long_comment

そこで、いくつかの記事や同僚のアイディアを参考にしてシンプルに GitHub GraphQL API と gh コマンドなどを利用して、以下の点について改善をしてみました。(以下全てghコマンドなどで頑張っており、MCPサーバーを使わない構成にしています)

  • 以前のレビュー内容を取得して、重複した指摘を避けるよう指示
  • 以前のコメント(インラインのサジェストではなく)を非表示にするよう指示

minimized_comments

  • 具体的な指摘はインラインでサジェストするよう指示

inline_suggestion

具体的な方法

過去の Claude Code のコメントを取得してJSONとして出力し、重複コメントを避ける

後のステップでは、インラインのサジェスト(reviewThreads)を利用してコメントを残すように指示していますので、開発者がpushするたびに Claude Code によるコメントが残されていきます。

変更を加えていないコードに対して同じ提案や指摘を残されるとテンションが下がるので、以下のように過去の提案をまるっと取得して参照できるようにしてみました。

      - name: Get existing Claude comments
        uses: actions/github-script@v7
        with:
          script: |
            const query = `query($owner: String!, $repo: String!, $number: Int!) {
              repository(owner: $owner, name: $repo) {
                pullRequest(number: $number) {
                  reviewThreads(first: 100) {
                    nodes {
                      id
                      isResolved
                      comments(last: 20) {
                        nodes {
                          author {
                            login
                          }
                          body
                          path
                          line
                          createdAt
                        }
                      }
                    }
                  }
                }
              }
            }`;
            
            const variables = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              number: context.issue.number
            };

            const result = await github.graphql(query, variables);

            // Extract only Claude[bot] comments (from latest)
            const claudeComments = result.repository.pullRequest.reviewThreads.nodes
              .filter(thread => 
                thread.comments.nodes.length > 0 &&
                thread.comments.nodes[0].author.login === 'claude'
              )
              .map(thread => ({
                threadId: thread.id,
                isResolved: thread.isResolved,
                path: thread.comments.nodes[0].path,
                line: thread.comments.nodes[0].line,
                body: thread.comments.nodes[0].body,
                createdAt: thread.comments.nodes[0].createdAt
              }));

            require('fs').writeFileSync('existing_claude_comments.json', 
              JSON.stringify(claudeComments, null, 2));

            console.log(`Found ${claudeComments.length} Claude comments`);

そしてプロンプトに以下のような指示を入れて参照してもらうようにしています。体感ですが、同じような指摘はほとんどされていないような気がします。

You MUST create ONLY inline comments on specific lines. Do NOT create overall PR review summaries.
IMPORTANT: Before creating any comment, check existing_claude_comments.json to ensure you don't duplicate existing Claude comments on the same line.

以前のコメントを非表示にする

こちらも GitHub GraphQL API を利用して、以前のコメントを非表示にします。ご覧の通り、こちらのステップまでは機械的に実施できるため、普通に GitHub Actions で gh コマンドを呼んでいることに他なりません。

      - name: Minimize previous mini-bun comments
        uses: actions/github-script@v7
        with:
          script: |
            // Get all PR comments using GraphQL to get proper IDs
            const query = `query($owner: String!, $repo: String!, $number: Int!, $after: String) {
              repository(owner: $owner, name: $repo) {
                pullRequest(number: $number) {
                  comments(first: 100, after: $after) {
                    nodes {
                      id
                      author {
                        login
                      }
                      isMinimized
                      body
                    }
                    pageInfo {
                      endCursor
                      hasNextPage
                    }
                  }
                }
              }
            }`;
            
            let after = null;
            let hasNextPage = true;
            const claudeComments = [];

            while (hasNextPage) {
              const result = await github.graphql(query, {
                owner: context.repo.owner,
                repo: context.repo.repo,
                number: context.issue.number,
                after: after
              });
              
              const comments = result.repository.pullRequest.comments.nodes;
              const claudeOverallComments = comments.filter(c => 
                c.author.login === 'claude' && !c.isMinimized && 
                c.body.toLowerCase().includes('mini-bun')
              );
              claudeComments.push(...claudeOverallComments);
              
              after = result.repository.pullRequest.comments.pageInfo.endCursor;
              hasNextPage = result.repository.pullRequest.comments.pageInfo.hasNextPage;
            }
            // Minimize mini-bun comments only
            for (const comment of claudeComments) {
              await github.graphql(`
                mutation($input: MinimizeCommentInput!) {
                  minimizeComment(input: $input) {
                    clientMutationId
                  }
                }
              `, {
                input: {
                  subjectId: comment.id,
                  classifier: 'OUTDATED'
                }
              });
              console.log(`Minimized mini-bun comment: ${comment.id}`);
            }

こちらに関してもシンプルに以下のことを実行しているだけです。

  • 過去のコメントを取得し
    • Claude Code が残したコメント
    • かつ最小化されていないコメントをフィルタリング
    • レビュー時に mini-bun と名乗らせるようにしているため、その文字列がBodyに含まれていることも検証しています(この辺りは省略したり他の条件に変えても良い気がします)
  • それらのコメントを最小化する

以下のリンク先から取得できるGraphQLのスキーマに以下のミューテーションがあったので利用しました。

https://docs.github.com/ja/graphql/overview/public-schema

  """
  Minimizes a comment on an Issue, Commit, Pull Request, or Gist
  """
  minimizeComment(
    """
    Parameters for MinimizeComment
    """
    input: MinimizeCommentInput!
  ): MinimizeCommentPayload

インラインでサジェストしてもらう

Claude Code GitHub Actions を実行するプロンプトに以下の指示を加えることで、インラインでのサジェストを受けることができます。

You MUST create ONLY inline comments on specific lines. Do NOT create overall PR review summaries.
IMPORTANT: Before creating any comment, check existing_claude_comments.json to ensure you don't duplicate existing Claude comments on the same line.

For each NEW issue found (not already commented on by Claude), create a review comment file and submit it:

cat > review_comments.json << 'EOF'
{
  "event": "COMMENT",
  "comments": [
    {
      "path": "path/to/file.go",
      "line": 10,
      "body": "Consider this improvement:\n```suggestion\nfunc improvedFunction() {\n    return \"better implementation\"\n}\n```"
    }
  ]
}
EOF

gh api --method POST \
  --header "Accept: application/vnd.github+json" \
  --header "X-GitHub-Api-Version: 2022-11-28" \
  repos/${{ github.repository }}/pulls/${{ github.event.number }}/reviews \
  --input review_comments.json

Create separate API calls for each file or group of related comments. Focus on actionable suggestions with code examples.

こちらのやり方に関しては親切なつよつよエンジニアがSlackで教えてくれました。

他の組織ではMCPサーバーが提供するツールの利用を許可して実現しているようです。

https://note.com/reality_eng/n/n873a4cab65ee

まとめ

  • Claude Code GitHub Actions のレビューを見やすくするためにいくつか工夫しました
    • 長い一つのコメントではなく、インラインで提案できるようにghコマンドを利用させました
    • 以前の長いコメントが残ると目が滑るので、最小化するようにしました
    • また、過去のコメントを取得して同じようなコメントの重複を避けることを目指しました

以上、最後までお読みいただきありがとうございました。

GitHubで編集を提案
Money Forward Developers

Discussion