概要
コミット時に自動で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
仕組み
-
git caiコマンドが.git/AI_REVIEWフラグファイルを作成 -
git commitが実行され、pre-commitフックが起動 - ローカルの
.git/hooks/pre-commitがあれば先に実行(プロジェクト固有のチェック) - フラグファイルが存在する場合、
ai-review.shを実行 -
review-prompt.txtとgit diff --cachedを結合してClaude Codeに送信 - レビュー結果を色分けして表示し、y/nで最終確認
- 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 など