Claude Codeのhooksで開発ワークフローを自動化する
Claude Codeを使っていて、「ファイルを編集したら毎回フォーマッターかけたいな」「コミット前にlintを自動で走らせたいな」って思うことがありました。調べてみたら、Claude Codeにはhooksという仕組みがあって、特定のイベントにシェルコマンドを紐づけて自動実行できるんですよね。今回はこのhooksの設定方法と実践的な使い方をまとめてみました。
目次
Claude Code hooksとは
hooksは、Claude Codeのライフサイクルの特定のタイミングで、ユーザーが定義したシェルコマンドを自動実行する仕組みです。たとえば「ファイルを編集した後にフォーマッターを実行する」「危険なコマンドをブロックする」といったことが、設定ファイルに書くだけで実現できます。
ポイントは決定論的(deterministic)に動くということ。Skillsやプロンプトでの指示はClaude側の判断で実行されたりされなかったりするんですが、hooksは条件に合えば100%確実に実行される。「必ずやってほしいこと」はhooksで設定するのが良さそうです。
設定ファイルの場所と書き方
hooksの設定は以下の2箇所に書けます。
~/.claude/settings.json— ユーザーレベル。すべてのプロジェクトに適用される.claude/settings.json(プロジェクトルート) — プロジェクトレベル。チームで共有できる
基本的な構造はこんな感じです。
{
"hooks": {
"イベント名": [
{
"matcher": "対象のツール名(正規表現)",
"hooks": [
{
"type": "command",
"command": "実行するシェルコマンド"
}
]
}
]
}
}
3層のネスト構造になっていて、「どのイベントで」「どのツールに対して」「何を実行するか」を定義する形になっています。
利用可能なフックイベント
現在使えるフックイベントは以下の通りです。
- PreToolUse — ツール実行前。バリデーションやブロックに使う
- PostToolUse — ツール実行後。フォーマットやログ記録に使う
- UserPromptSubmit — ユーザーがプロンプトを送信した時。入力チェックに使う
- Stop — Claudeが応答を完了した時。通知やまとめ処理に使う
- SessionStart — セッション開始・再開時。コンテキストの注入に使う
- Notification — Claude Codeが通知を送る時
特によく使うのはPreToolUseとPostToolUseですね。この2つでほとんどのワークフロー自動化がカバーできます。
フックが受け取るデータ
各フックはJSON形式でデータを受け取ります。標準入力から読み取れる基本フィールドはこんな感じです。
session_id— セッションIDtranscript_path— トランスクリプトのパスcwd— 現在の作業ディレクトリhook_event_name— フックイベント名tool_name— ツール名(PreToolUse/PostToolUseの場合)tool_input— ツールへの入力(PreToolUseの場合)
これらを使って、フック内でより細かい条件分岐ができます。
PreToolUseの終了コード
PreToolUseフックには特別な終了コードの仕組みがあります。
- 終了コード 0 — ツール実行を許可(そのまま進む)
- 終了コード 2 — ツール実行をブロック(stderrに理由を書くとClaudeにフィードバックされる)
これで、危険な操作を事前にブロックしつつ、Claudeに「なぜブロックしたか」を伝えて代替手段を取らせることができるんですよね。
実例1: コミット前にlintを自動実行
Claude CodeがBashツールでgit commitを実行する前に、自動でESLintを走らせる設定です。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "input=$(cat); cmd=$(echo $input | jq -r '.tool_input.command'); if echo \"$cmd\" | grep -q 'git commit'; then npx eslint . --max-warnings 0 || (echo 'ESLint errors found. Fix before committing.' >&2; exit 2); fi"
}
]
}
]
}
}
このフックはBashツールが使われるたびに発火するんですが、コマンド内容をjqで取り出してgit commitを含む場合だけlintを実行します。lintエラーがあれば終了コード2でブロックして、Claudeにエラー内容をフィードバックしてくれます。
実例2: ファイル編集後にフォーマッターを自動実行
Claude CodeがEditやWriteツールでファイルを変更した後に、Prettierで自動フォーマットする設定です。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "input=$(cat); file=$(echo $input | jq -r '.tool_input.file_path // .tool_input.filePath'); if [ -n \"$file\" ] && [ \"$file\" != \"null\" ]; then npx prettier --write \"$file\" 2>/dev/null; fi"
}
]
}
]
}
}
matcherにEdit|MultiEdit|Writeを指定することで、ファイル編集系のツールすべてに対応しています。フックが受け取るJSONからファイルパスを取得して、そのファイルだけをフォーマットする形ですね。プロジェクト全体をフォーマットするより効率的です。
実例3: ユーザー入力時のカスタムバリデーション
UserPromptSubmitフックを使って、ユーザーの入力に対してルールを適用する例です。
{
"hooks": {
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "input=$(cat); prompt=$(echo $input | jq -r '.prompt'); if echo \"$prompt\" | grep -qi '本番\\|production'; then echo 'WARNING: 本番環境に関する操作が検出されました。慎重に進めてください。'; fi"
}
]
}
]
}
}
この例では、ユーザーの入力に「本番」や「production」が含まれていたら警告メッセージを出すようにしています。終了コード2で完全にブロックすることもできるし、終了コード0で警告だけ出して続行させることもできます。
matcherでの条件指定
matcherフィールドは正規表現で指定します。フックイベントによってマッチ対象が変わります。
""または未指定 — すべてにマッチ"Bash"— Bashツールのみ"Edit|Write"— EditまたはWriteツール"Edit|MultiEdit|Write"— ファイル編集系すべて
PreToolUseとPostToolUseではツール名に対してマッチし、Notificationでは通知タイプに対してマッチします。UserPromptSubmitやStop、SessionStartではmatcherは通常空文字列にしておきます。
Skillsとの使い分け
Claude Codeにはhooksの他にSkillsという仕組みもあります。両者の違いを整理してみました。
- hooks — 決定論的。条件に合えば必ず実行される。自動的なルール適用やガードレールに最適
- Skills — 確率的。Claudeが判断して適用する。タスクの実行手順やベストプラクティスの共有に最適
使い分けの基準はシンプルです。
- 「絶対にやってほしいこと」→ hooks(例: コードフォーマット、lint、セキュリティチェック)
- 「状況に応じてやってほしいこと」→ Skills(例: テストの書き方、デプロイ手順、コーディング規約)
たとえばコードフォーマットは毎回必ず実行したいからhooks、テストの書き方ガイドは参照してほしいけどClaude側の判断に任せたいからSkills、という感じですね。
注意点
無限ループに注意
PostToolUseフックでファイルを変更すると、それがさらにフックを発火させて無限ループになる可能性があります。フック内で実行するコマンドがClaude Codeのツールを介さず直接実行されるなら問題ないんですが、念のためフックの設計時にはループしないか確認しておいた方がいいです。
パフォーマンスへの影響
フックは同期的に実行されるため、重い処理を入れるとClaude Codeの応答が遅くなります。特にPreToolUseフックは毎回のツール実行前に走るので、軽量なチェックに留めておくのが良さそうです。プロジェクト全体のlintなどは対象ファイルを絞るなど工夫した方がいいですね。
デバッグ方法
フックがうまく動かないときは、まずコマンドを手動でターミナルから実行してみるのが確実です。また、stderrへの出力はClaudeにフィードバックされるので、デバッグ用のログ出力にも使えます。
まとめ
Claude Codeのhooksは、開発ワークフローの自動化にかなり使える仕組みでした。「フォーマッターの自動実行」「コミット前のlint」「危険操作のブロック」など、確実に実行したいルールをhooksに設定しておけば、Claudeへの指示忘れを心配する必要がなくなります。
まずはPostToolUseでのフォーマッター自動実行あたりから試してみるのが良さそうです。設定ファイルをプロジェクトの.claude/settings.jsonに置いてチームで共有すれば、メンバー全員のClaude Code環境を統一できるのも大きなメリットだと思います。
今回は以上です!