0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git commit時にClaude Codeにコードレビューをしてもらう

0
Last updated at Posted at 2025-10-09

概要

コミット時に自動でClaude Codeによるコードレビューを挟む仕組みを作成しました。ただし標準の git commit コマンドにはオプションを追加できないこと、またAIレビューなしでコミットしたい場面もあることを考慮し、git cai という専用のサブコマンドを作成しました。

スクリプトもClaude Codeにほぼ作ってもらいました。Claude Code最高

前提条件

  • Gitがインストール済み
  • Claude Code がインストール済み
  • zshを使用

検証環境

  • macOS Tahoe 26.0.1
  • git 2.50.1
  • Claude Code 2.0.11
  • zsh 5.9

セットアップ

1. ディレクトリ作成

まず、git hooksとカスタムコマンド用のディレクトリを作成します。

mkdir -p ~/.git-hooks ~/bin
git config --global core.hooksPath ~/.git-hooks

core.hooksPathをグローバルに設定していますが、後述のpre-commitファイル内でローカルのhookも呼び出しています。

2. レビュープロンプトの作成

~/.git-hooks/review-prompt.txt を作成します。このファイルでレビューの基準を定義しています。

以下のコード変更を徹底的かつ効率的にレビューしてください。レビュー時間を最適化するため、重大な問題を優先してください。

## レビューチェック項目(優先度順)

### 1. 重大な問題(コミット前に必ず修正)
- 構文エラー、コンパイルエラー、型エラー
- ロジックエラー:off-by-one、境界条件、誤った早期リターン
- Null/undefined/Noneポインタ参照のリスク
- 配列・リストのインデックス範囲外アクセス
- ゼロ除算、整数オーバーフロー/アンダーフロー
- 無限ループの可能性
- SQLインジェクションの脆弱性
- XSS(クロスサイトスクリプティング)の脆弱性
- コマンドインジェクション、パストラバーサルのリスク
- ハードコードされた秘密情報、認証情報、APIキー
- 認証・認可チェックの欠落
- 重大なリソースリーク(ファイル、接続、ロックが閉じられていない)
- メモリリーク、use-after-free、double-free の問題
- 並行処理における競合状態、デッドロック

### 2. 重要な問題(修正すべき)
- 未定義変数、未使用変数・インポート
- エラーハンドリングの欠落または不十分
- 入力値の検証・サニタイゼーション不足
- 不適切な例外処理(広範囲すぎるcatch、エラーの無視)
- 後方互換性の破壊:
  - 関数・メソッドのシグネチャ変更
  - 戻り値の型・構造の変更
  - 公開APIの破壊的変更
  - デフォルト値の変更
  - 削除された機能
- 不適切な状態管理
- クリティカルパスでのnull/エラーチェックの欠落
- 並行処理の問題(スレッドセーフでない操作)

### 3. コード品質(改善すべき)
- パフォーマンスの問題:
  - アルゴリズムの計算量が悪い(より良い方法があるのにO(n²)以上)
  - ループ内での冗長な計算
  - 不要なオブジェクトコピーやメモリ確保
  - ユースケースに対して非効率なデータ構造
  - N+1クエリ問題
  - ループ内での非効率な文字列連結
- 命名規則の違反
- マジックナンバー(定数化されていない数値)
- 重複コードの存在
- 関数・メソッドの責務過多(単一責任原則違反)
- デッドコード(到達不可能なコードパス)
- 複雑なロジックのドキュメント不足
- 変更されたコードのテストカバレッジ不足
- エッジケースの処理漏れ

### 4. ベストプラクティス(あればよい改善)
- 軽微なコードスタイルの不一致
- より良い抽象化の機会
- 保守性向上のためのリファクタリング提案
- より説明的な変数名・関数名
- デバッグのための追加ログ

## 重要な指示

**重要:**
1. レビュー結果のセクションのみを出力してください - 説明、思考プロセス、前置き文は不要
2. 【変更内容】セクションから直接開始してください
3. すべてのセクションを日本語で出力してください
4. プレーンテキスト形式を使用してください(**, *, ` などのマークダウン記法は使わない)
5. 「収集した情報は」や「レビューを出力します」などのフレーズを含めないでください

## 必須出力形式(日本語)

【変更内容】
3-5行で説明:
- 何を変更したか
- なぜ変更したか
- 影響範囲

【指摘事項】
以下の形式で分類:
- [重大] 必ず修正が必要(Critical issues)
- [警告] 修正を推奨(Important issues)
- [提案] 改善提案(Quality/Best practices)

形式:
filename:line_number - 説明

[重大]または[警告]の場合は、説明の直後に修正コードを追加:

  ---Suggested Changes---
  修正後のコード
  -----------------------

[提案]レベルにはコード提案不要。
問題がない場合: 「問題は見つかりませんでした」

【レビュー結果】
最後に必ず以下のいずれか1行:
- Review-Result: OK([重大][警告]が0個の場合)
- Review-Result: NG([重大][警告]が1個以上ある場合)

---

## 変更差分

3. pre-commitフックの作成

~/.git-hooks/pre-commit を作成します。このスクリプトは.git/AI_REVIEWフラグファイルの有無でAIレビューの実行を制御しています。また、ローカルの.git/hooks/pre-commitがあれば先に実行します。

#!/bin/bash
# Pre-commit hook with AI code review integration

set -euo pipefail

# Colors
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly BOLD='\033[1m'
readonly NC='\033[0m'

# Configuration
readonly HOOKS_DIR="$HOME/.git-hooks"
readonly AI_REVIEW_SCRIPT="$HOOKS_DIR/ai-review.sh"
readonly LOCAL_HOOK=".git/hooks/pre-commit"

if [ -x "$LOCAL_HOOK" ]; then
    echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${BLUE}${BOLD}  Running Local Pre-commit Hook${NC}"
    echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    if ! "$LOCAL_HOOK"; then
        echo ""
        echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "${RED}✗ Local pre-commit hook failed${NC}"
        echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        exit 1
    fi
    echo ""
    echo -e "${GREEN}✓ Local pre-commit hook completed successfully${NC}"
    echo ""
fi

# Check for AI_REVIEW flag
RUN_AI_REVIEW=0
if [ -f .git/AI_REVIEW ]; then
    RUN_AI_REVIEW=1
    rm -f .git/AI_REVIEW
fi

STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

if [ -z "$STAGED_FILES" ]; then
    exit 0
fi

if [ "$RUN_AI_REVIEW" -eq 1 ]; then
    if [ ! -x "$AI_REVIEW_SCRIPT" ]; then
        echo -e "${RED}✗ AI review script not found:${NC} $AI_REVIEW_SCRIPT"
        echo -e "  Please ensure the script exists and is executable."
        exit 1
    fi

    if "$AI_REVIEW_SCRIPT"; then
        echo ""
        echo -e "${BOLD}Proceed with commit? (y/n)${NC}"
        exec < /dev/tty
        read -r response

        if [ "$response" != "y" ] && [ "$response" != "Y" ]; then
            echo -e "${YELLOW}⚠ Commit aborted by user${NC}"
            exit 1
        fi
    else
        echo ""
        echo -e "${BOLD}AI review found issues. Proceed with commit anyway? (y/n)${NC}"
        exec < /dev/tty
        read -r response

        if [ "$response" != "y" ] && [ "$response" != "Y" ]; then
            echo -e "${YELLOW}⚠ Commit aborted by user${NC}"
            exit 1
        fi
    fi
fi

exit 0

4. AIレビュースクリプトの作成

~/.git-hooks/ai-review.sh を作成します。このスクリプトがClaude Codeを呼び出してコードレビューを実行しています。

#!/bin/bash
# AI Code Review Engine - Minimal Output

set -euo pipefail

# Colors
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly MAGENTA='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly BOLD='\033[1m'
readonly NC='\033[0m'

# Paths
readonly HOOKS_DIR="$HOME/.git-hooks"
readonly MODEL="claude-sonnet-4-5-20250929"

# Spinner function
show_spinner() {
    local pid=$1
    local message=$2
    local spinstr='|/-\'
    local temp

    printf "\n"
    while kill -0 "$pid" 2>/dev/null; do
        temp=${spinstr#?}
        printf "\r${YELLOW}[%c]${NC} ${BOLD}%s${NC}" "$spinstr" "$message"
        spinstr=$temp${spinstr%"$temp"}
        sleep 0.15
    done
    printf "\r%*s\r" $((${#message} + 13)) ""
}

# Colorize review output
colorize_review() {
    while IFS= read -r line; do
        # Section titles (【】)
        if [[ "$line" =~ ^【.*$ ]]; then
            echo -e "${CYAN}${BOLD}$line${NC}"
        # Issue severity markers
        elif [[ "$line" =~ ^-\ \[重大\]\ (.*)$ ]]; then
            echo -e "- ${RED}${BOLD}[重大]${NC} ${BASH_REMATCH[1]}"
        elif [[ "$line" =~ ^\[重大\]\ (.*)$ ]]; then
            echo -e "${RED}${BOLD}[重大]${NC} ${BASH_REMATCH[1]}"
        elif [[ "$line" =~ ^-\ \[警告\]\ (.*)$ ]]; then
            echo -e "- ${YELLOW}${BOLD}[警告]${NC} ${BASH_REMATCH[1]}"
        elif [[ "$line" =~ ^\[警告\]\ (.*)$ ]]; then
            echo -e "${YELLOW}${BOLD}[警告]${NC} ${BASH_REMATCH[1]}"
        elif [[ "$line" =~ ^-\ \[提案\]\ (.*)$ ]]; then
            echo -e "- ${BLUE}${BOLD}[提案]${NC} ${BASH_REMATCH[1]}"
        elif [[ "$line" =~ ^\[提案\]\ (.*)$ ]]; then
            echo -e "${BLUE}${BOLD}[提案]${NC} ${BASH_REMATCH[1]}"
        # Review-Result line
        elif [[ "$line" =~ ^Review-Result:\ OK$ ]]; then
            echo -e "${GREEN}${BOLD}$line${NC}"
        elif [[ "$line" =~ ^Review-Result:\ NG$ ]]; then
            echo -e "${RED}${BOLD}$line${NC}"
        elif [[ "$line" =~ ^Review-Result: ]]; then
            echo -e "${BOLD}$line${NC}"
        else
            echo "$line"
        fi
    done
}

# Run review
run_review() {
    local diff_content="$1"
    local temp_input=$(mktemp)
    local temp_output=$(mktemp)
    local prompt_file="$HOOKS_DIR/review-prompt.txt"

    if [ ! -f "$prompt_file" ]; then
        echo -e "${RED}✗ Review prompt not found:${NC} $prompt_file"
        rm -f "$temp_input" "$temp_output"
        return 1
    fi

    cat "$prompt_file" > "$temp_input"
    echo "" >> "$temp_input"
    echo "$diff_content" >> "$temp_input"

    local error_output
    if error_output=$(claude --model "$MODEL" < "$temp_input" 2>&1 > "$temp_output"); then
        cat "$temp_output" | colorize_review
        local exit_code=0
    else
        echo -e "${RED}✗ Review failed${NC}"
        echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "${BOLD}Claude CLI Error:${NC}"
        echo "$error_output"
        echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        local exit_code=1
    fi

    rm -f "$temp_input" "$temp_output"
    return $exit_code
}

# Main
main() {
    if ! command -v claude >/dev/null 2>&1; then
        echo -e "${RED}✗ Claude CLI not found${NC}"
        echo -e "  Please install the Claude CLI to enable AI code review."
        exit 1
    fi

    local staged_files=$(git diff --cached --name-only --diff-filter=ACM)
    if [ -z "$staged_files" ]; then
        echo -e "${YELLOW}⚠ No files staged for commit${NC}"
        exit 0
    fi

    echo ""
    echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${BLUE}${BOLD}  AI Code Review${NC}"
    echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    echo -e "${BOLD}Changes to be reviewed:${NC}"
    echo ""
    git diff --cached --stat --color=always
    echo ""

    local diff_content=$(git diff --cached)
    if [ -z "$diff_content" ]; then
        echo -e "${YELLOW}⚠ No changes detected${NC}"
        exit 0
    fi

    # Run review with spinner
    local temp_result=$(mktemp)
    (run_review "$diff_content" > "$temp_result"; echo $? > "$temp_result.exit") &
    local review_pid=$!

    show_spinner $review_pid "Analyzing changes..."

    wait $review_pid
    local review_exit_code=$(cat "$temp_result.exit")

    # Display result
    cat "$temp_result"
    echo ""

    # Check review result
    if grep -qi "Review-Result: NG" "$temp_result"; then
        rm -f "$temp_result" "$temp_result.exit"
        echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "${RED}✗ Issues found during AI review${NC}"
        echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo ""
        return 1
    else
        rm -f "$temp_result" "$temp_result.exit"
        echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "${GREEN}✓ Review completed successfully${NC}"
        echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo ""
        return 0
    fi
}

if main "$@"; then
    exit 0
else
    exit 1
fi

5. git cai コマンドの作成

~/bin/git-cai を作成します。このスクリプトがフラグファイルを作成してgit commitを呼び出しています。

#!/bin/bash
touch .git/AI_REVIEW
git commit "$@"

6. 実行権限の付与

作成したスクリプトに実行権限を付与します。

chmod +x ~/.git-hooks/pre-commit
chmod +x ~/.git-hooks/ai-review.sh
chmod +x ~/bin/git-cai

7. PATHの設定

~/bin/git-caiを実行可能にするため、~/.zshrc(bashの場合は~/.bashrc)にPATHを追加します。

export PATH="$HOME/bin:$PATH"

設定を反映:

source ~/.zshrc

使い方

git cai -m "commit message"

実行例

例1: 問題なしの場合

$ git cai -m "add input validation"

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  AI Code Review
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Changes to be reviewed:

 user_service.py | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

[/] Analyzing changes...

【変更内容】
ユーザー登録機能に入力値の検証を追加しました。
メールアドレスの形式チェック、パスワードの長さ制限、
特殊文字のエスケープ処理を実装しています。
影響範囲: register_user()メソッド

【指摘事項】
問題は見つかりませんでした。

【レビュー結果】
Review-Result: OK

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Review completed successfully
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Proceed with commit? (y/n)
y

[main a1b2c3d] add input validation
 1 file changed, 15 insertions(+), 3 deletions(-)

例2: 問題が検出された場合

$ git cai -m "fix array processing"

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  AI Code Review
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Changes to be reviewed:

 data_processor.py | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

[-] Analyzing changes...

【変更内容】
配列処理のロジックを変更し、データの集計方法を改善しました。
ループ内でデータを集計し、結果を返却します。
影響範囲: process_data()関数

【指摘事項】
- [重大] data_processor.py:42 - 配列インデックスの境界チェックが必要。for i in range(len(items) + 1) はインデックス範囲外アクセスの可能性があります。

  ---Suggested Changes---
  for i in range(len(items)):
      process_item(items[i])
  -----------------------

- [警告] data_processor.py:58 - ゼロ除算の可能性。total / count でcountが0の場合に例外が発生します。

  ---Suggested Changes---
  if count > 0:
      average = total / count
  else:
      average = 0
  -----------------------

【レビュー結果】
Review-Result: NG

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✗ Issues found during AI review
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

AI review found issues. Proceed with commit anyway? (y/n)
n
⚠ Commit aborted by user

仕組み

  1. git caiコマンドが.git/AI_REVIEWフラグファイルを作成
  2. git commitが実行され、pre-commitフックが起動
  3. ローカルの.git/hooks/pre-commitがあれば先に実行(プロジェクト固有のチェック)
  4. フラグファイルが存在する場合、ai-review.shを実行
  5. review-prompt.txtgit diff --cachedを結合してClaude Codeに送信
  6. レビュー結果を色分けして表示し、y/nで最終確認
  7. yでコミット実行、nで中止

カスタマイズ

レビュー項目の変更

~/.git-hooks/review-prompt.txtを編集することで、レビュー基準をカスタマイズできます。

使用モデルの変更

ai-review.shのMODEL変数を編集することで、異なるClaudeモデルを使用できます。MAXプラン使えるリッチな人はOpusにすると良いと思います(羨ましい...)

readonly MODEL="claude-sonnet-4-5-20250929"

ファイル構成

~/.git-hooks/
  ├── pre-commit          # メインフック(フラグチェック、ローカルフック実行)
  ├── ai-review.sh        # AIレビュー実行スクリプト
  └── review-prompt.txt   # レビュー基準定義

~/bin/
  └── git-cai            # カスタムgitサブコマンド

.git/hooks/             # プロジェクトローカルフック(任意)
  └── pre-commit        # 例: make build など

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?