数か月前、自作アプリのコミットを積み重ねていた時に「Claude Code に毎回コードを見てもらうのは便利だけれど、保存するたびに走らせるとコストも待ち時間も無視できないな」と感じていました。/review を手で叩き続けるのも、つい忘れてしまいます。落ち着いて考えると、コミット直前という「ちょうど見直したいタイミング」に、変更したファイルだけ渡せばいいのだと気がつきました。
そこで husky と lint-staged を組み合わせて、ステージング済みファイルだけを Claude Code に渡すパイプラインを作ってみたところ、想像していた以上に静かに馴染んでくれました。本記事はその構成を、個人開発者の視点で実装手順としてまとめたものです。
なぜ「ステージング済みファイルだけ」に絞るのか
Claude Code はリポジトリ全体を読みに行ってくれる賢いエージェントですが、pre-commit のたびに全ファイルを巡回されると、開発者の小気味よいリズムが崩れてしまいます。私が試して気づいたのは、「コミットしようとしているもの」だけを見せた方が、Claude の指摘が驚くほど鋭くなるということでした。
git diff --cached で取れるステージング済みの変更だけを渡すと、Claude は「なぜこの変更が今必要なのか」「他の変更と整合しているか」だけに集中してくれます。リポジトリ全体を読み込ませた時の「一般論的なアドバイス」が消えて、その瞬間に必要な指摘だけが返ってきます。
加えて、lint-staged が変更ファイルパスを引数で渡してくれる仕組みは、Claude Code の --print モード(非対話一括実行)と非常に相性がいいのです。後段で詳しく書きますが、変更ファイルの数が増えても、シェルパイプで直列に処理できます。
前提条件と構成の全体像
導入する前に、ローカルで以下が動く状態を確認してください。
- Node.js 20 以上(husky v9 の前提)
claudeコマンドが PATH に通っている(claude --versionで確認)- 対象リポジトリが
package.jsonを持つ Node プロジェクト
全体像はシンプルです。
- husky で
.husky/pre-commitを生成する - lint-staged を導入し、変更ファイルにかける処理を
package.jsonに書く - lint-staged の処理として「Claude Code に変更ファイルだけ渡してレビューさせる小さなシェルスクリプト」を呼ぶ
- レビュー結果が問題ありの時のみ非ゼロで終了し、コミットを止める
ステップ1: husky と lint-staged のセットアップ
まず開発依存に husky と lint-staged を入れます。
# プロジェクトルートで実行
npm install --save-dev husky lint-staged
# husky v9 の初期化(.husky ディレクトリ作成)
npx husky initnpx husky init を実行すると、.husky/pre-commit が以下のようなファイルとして自動生成されます。
# .husky/pre-commit の初期内容
npm testこれを npx lint-staged に書き換えます。
# .husky/pre-commit
npx lint-staged次に package.json に lint-staged の設定を追記します。
{
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"scripts/claude-staged-review.sh"
]
}
}ポイントは、対象拡張子を絞ることです。私のプロジェクトでは TypeScript / TSX / JS / JSX に限定しています。Markdown や設定ファイルまで毎回レビューさせると、Claude にとっても判断材料が散りすぎてしまいました。
ステップ2: レビュー用シェルスクリプトを書く
scripts/claude-staged-review.sh を新規作成します。
#!/usr/bin/env bash
# scripts/claude-staged-review.sh
# lint-staged が変更ファイルパスを引数で渡してくれる
set -euo pipefail
# 引数: 変更されたファイルパスの配列
FILES=("$@")
if [ ${#FILES[@]} -eq 0 ]; then
echo "ℹ️ レビュー対象ファイルなし。スキップします。"
exit 0
fi
# Claude に渡すプロンプト本体
PROMPT=$(cat <<'PROMPT_EOF'
以下のファイルは git commit 直前にステージングされた変更です。
コードレビュアーとして、以下の観点だけを確認してください:
1. 明らかなバグ・null 安全性・型の不整合
2. テストが必要な変更でテストが追加されていない箇所
3. 個人情報・APIキー・トークンの誤コミット
4. 命名や責務分離の重大な問題
問題がなければ最初の行に "OK" とだけ書いてください。
問題があれば、各指摘を箇条書きで簡潔に書いてください。
リファクタ提案や改善のアイデアは不要です。
PROMPT_EOF
)
# 変更ファイルの diff を取得して Claude に渡す
DIFF=$(git diff --cached -- "${FILES[@]}")
# Claude Code を非対話モードで実行(--print で結果をそのまま標準出力に)
RESULT=$(printf '%s\n\n```diff\n%s\n```\n' "$PROMPT" "$DIFF" | claude --print --model claude-haiku-4-5)
echo "$RESULT"
# 1行目が "OK" でなければ非ゼロ終了 → コミット中止
if [ "$(echo "$RESULT" | head -n1 | tr -d '[:space:]')" = "OK" ]; then
exit 0
else
echo ""
echo "❌ Claude レビューで指摘があります。修正してから再コミットしてください。"
echo "💡 一時的にスキップしたい場合: git commit --no-verify"
exit 1
fi実行権限を忘れずに付けます。
chmod +x scripts/claude-staged-review.shこのスクリプトでいくつか意図を込めた箇所があります。
claude --print --model claude-haiku-4-5 を使っているのは、pre-commit という「速くなければ意味がない」場面に Haiku 4.5 がちょうど合うからです。Sonnet や Opus を呼ぶと指摘の精度は上がりますが、待ち時間で開発のリズムが崩れます。私の体感では、Haiku 4.5 は「明らかなバグや誤コミットの検出」に十分応えてくれます。
プロンプトで「リファクタ提案は不要」と明示しているのも実体験から来た工夫です。Claude は基本的に親切なので、放っておくと「変数名はこちらの方が」「この関数は分割した方が」と善意のアドバイスを大量にくれます。pre-commit ではそれが邪魔になるので、最初に範囲を絞っています。
exit 1 でコミットを止める判定を「1行目が OK 以外」にしているのも、出力ブレに強くするための工夫です。Claude は時に「OK です。問題ありません。」と書いてしまいますが、それでも先頭が "OK" なら通します。
ステップ3: 動作確認とログの読み方
実際に動かしてみましょう。
# 適当なファイルを変更
echo "console.log('test')" >> src/example.ts
git add src/example.ts
git commit -m "test pre-commit"問題がなければ、以下のような出力で git commit が成功します。
✔ Backed up original state in git stash
✔ Running tasks for staged files...
ℹ️ レビュー対象ファイルなし。スキップします。
✔ Applying modifications from tasks...
[main abc1234] test pre-commit
意図的に問題のあるコードを混ぜてみます。
// src/example.ts
const apiKey = "sk-ant-api03-XXXXXXXXXXXXX"; // 実トークンを誤コミット
console.log(apiKey);すると Claude が止めてくれます。
- src/example.ts に Anthropic API キーらしき文字列がハードコードされています。
即座に削除し、環境変数から読み込むように変更してください。
- 漏洩した可能性があるため、コンソールでキーをローテーションしてください。
❌ Claude レビューで指摘があります。修正してから再コミットしてください。
💡 一時的にスキップしたい場合: git commit --no-verify
このように、git の通常フローを壊さずに「ヒヤリとした瞬間」を捕まえてくれるのが、この構成のいちばん嬉しいところです。
実際に運用して気づいたこと
私の場合、最初は「すべてのファイルを Sonnet でレビュー」する大袈裟な構成にしていました。3回ほど使った時点で、コミットのたびに 20〜30 秒待つことになり、結局 --no-verify で常時スキップする習慣がついてしまいました。
そこから「変更ファイルだけ・Haiku 4.5・観点を4つに限定」と削っていったところ、平均で 4〜6 秒ほどに収まりました。これくらいなら、コミットメッセージを考えている間に終わります。
「指摘の数は減らす、走る速度は上げる」という方向に倒したのが、結果として最も使われる構成だったのです。Claude の能力を全開で使うことではなく、開発のリズムに溶け込ませることが、ここでの目的でした。
設定の調整ポイント
拡張子・パスごとに観点を変える
スクリプト内で ${FILES[@]} を見て、観点を出し分けることもできます。たとえばテストファイルだけは「テストの網羅性」を聞き、本番コードは「セキュリティ」を聞く、というふうに。
# 簡易例: テストファイルを判別
if [[ "${FILES[0]}" =~ \.test\. ]]; then
PROMPT="$PROMPT_TEST"
else
PROMPT="$PROMPT_PROD"
fiただし、私はあまり凝った分岐は推奨しません。pre-commit で動くスクリプトは、シンプルであるほど壊れにくいからです。
コストの目安
Haiku 4.5 で 5〜10 ファイル程度のコミットなら、1 回あたりの API コストは数セント以下に収まります。1 日に 30 回コミットしても日次のコストは小さく、私の小規模個人開発の範囲では月額 $1〜2 程度でした。
もしさらに節約したい場合は、*.test.* を lint-staged の対象から外す、docs/ 配下を除外する、などのフィルタを足すと効果的です。
Sonnet を使う条件付きエスカレーション
「重要な変更だけ Sonnet にエスカレートしたい」場合は、変更行数で判定する方法があります。
LINES=$(echo "$DIFF" | wc -l)
if [ "$LINES" -gt 200 ]; then
MODEL="claude-sonnet-4-6"
else
MODEL="claude-haiku-4-5"
fi
RESULT=$(printf '...' | claude --print --model "$MODEL")200 行を超える変更(リファクタ・新機能追加など)は Sonnet に判断させ、それ以下は Haiku で済ませる、という割り振りです。
関連する仕組みとの違い
このアプローチを選ぶ前に、いくつか他の選択肢も検討しました。それぞれの違いを整理しておきます。
Claude Code 自体の Hooks 機能(PreToolUse / PostToolUse など)は、Claude Code セッション内のツール呼び出しに反応する仕組みです。これは Claude Code Hooks 自動化マスターガイド で詳しく解説していますが、git の pre-commit とは別の層の話です。git の commit イベントには反応しないため、本記事の用途には使えません。
GitHub Actions で PR レビューを走らせる方法もあります。これは確実に動きますが、コミットしてから 1〜2 分待つことになるので、ローカルで「即座に気づく」ことを目的にする本記事の趣旨とは異なります。両者は補完関係で、ローカル pre-commit で軽くチェック → PR で本格レビュー、という二段構えがおすすめです。
claude --print の使い方そのものに興味がある場合は、claude --bare / --print フラグでヘッドレス自動化を組む完全ガイド も併せて読むと理解が深まります。
トラブルシューティング
claude コマンドが見つからない
husky の pre-commit はログインシェルではなく非対話シェルで動くため、PATH に Claude Code が入っていないことがあります。
# .husky/pre-commit の冒頭で PATH を補強
export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"
npx lint-stagedこれで多くのケースは解決します。
lint-staged が「対象ファイルなし」で止まる
ステージングされたファイルが、lint-staged の glob パターンに一致しないだけです。たとえば *.md のみの変更なら、*.{ts,tsx,js,jsx} には一致せず、Claude のレビューはスキップされます。これは仕様通りで、問題ありません。
コミットを急ぎたい時は --no-verify
緊急のホットフィックスでレビューを飛ばしたい時は、git の標準オプションが使えます。
git commit --no-verify -m "hotfix: rollback"ただし、--no-verify を多用するようになったら、レビューの観点が厳しすぎるサインです。プロンプトを見直して、もっと範囲を絞ることをおすすめします。
次の一歩
まずは *.{ts,tsx,js,jsx} の小さなプロジェクトで試してみてください。スクリプトをコピーして 1 ファイルだけ変更してコミットすると、最初の動作が確認できます。
実際に組み込んでみると、Claude Code を「常時走らせる重たいレビュアー」ではなく、「コミット直前にだけそっと立ち会ってくれる相棒」として置けることに気づきます。私自身、この構成を入れてから API キーの誤コミットを 2 度ほど未然に防いでもらえました。
最後までお付き合いいただき、ありがとうございました。同じように「pre-commit で AI レビューを軽く回したい」と考えていた方の役に立てば嬉しいです。