フック:決定論的な自動化
フックは、ライフサイクルの定められたポイントで Claude Code が自動的に実行するシェルコマンドです。権限がアクションを許可するかどうかを決めるのに対し、フックはその周りであなたが決定論的なロジックを実行できるようにします——フォーマット、検証、ロギング、ゲート。これが「忘れずにやってください」ではなく、振る舞いを保証する方法です。
- 指示や権限ではなくフックに手を伸ばすべきとき
- フックの配線方法:イベント、マッチャー、そして stdin の JSON ペイロード
- フックがアクションをブロックする 2 つの方法——終了コード 2 と stdout の JSON
- 高速で安全なフックを、もたつく無口なフックから分ける良い習慣とよくある間違い
いつフックに手を伸ばすか
振る舞いを単に要求するのではなく保証したいときにフックに手を伸ばします。よくある仕事はそれぞれライフサイクルイベントに対応します:
- ファイル編集のたびに自動フォーマット/リント(
PostToolUse)。 - ルールに違反するアクションを実行前にブロック(
PreToolUse)。 - セッション終了やタスク完了時に通知またはログ(
Stop)。 - セッション開始時にコンテキストを注入。
どう動くか
settings.json でフックを登録し、イベント(そしてしばしばツールマッチャー)にマッチさせます。イベントが発火すると、Claude はあなたのコマンドを実行し、stdin に JSON ペイロード(ツール名、その入力、セッション)を渡します。あなたのコマンドの終了コードと出力が、次に何が起きるかを決めます。
- 気にするライフサイクルイベントの下に settings.json でフックを登録します——たとえば PostToolUse。
- ツールマッチャーを追加し、関連するツールだけでフックが発火するようにします。例:ファイル編集には matcher "Edit|Write"。
- イベントが発火すると、Claude はあなたのコマンドを実行し、stdin に JSON ペイロード——ツール名、その入力、セッション——をパイプします。
- あなたのコマンドの終了コードと出力が結果を決めます:アクションを進めさせる、ロジックを実行する、またはブロックする。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
]
}
]
}
}
上のフックは編集されたファイルのパスを stdin の JSON(.tool_input.file_path)から読み取り、フォーマットします。環境変数がパスを保持していると仮定してはいけません——stdin から読んでください。 ${CLAUDE_PROJECT_DIR} のような便利なパスのプレースホルダーはスクリプトの場所特定のために利用できます。
フックはどうブロックするか
イベントに応じて 2 通りあります:
- 終了コード 2 — フックがアクションを失敗させ、stderr に書いたものが Claude が見るメッセージになります。シンプルで、コマンドフックで機能します。
- stdout の JSON(終了 0) — 構造化された決定を返します。
PreToolUseではpermissionDecisionがdeny、PostToolUse/Stopなどでは{"decision": "block", "reason": "…"}です。
以下のスクリプトは Bash ツールに対する PreToolUse フックです。上から下に読んでください:stdin からコマンドを取り出し、破壊的に見えるなら理由を stderr に書いて終了コード 2 でブロックします。
#!/usr/bin/env bash
# PreToolUse hook on the Bash tool: refuse to delete things.
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm\ * || "$command" == *"rm -rf"* ]]; then
echo "Blocked: destructive 'rm' is not allowed by policy." >&2
exit 2
fi
exit 0
メンタルモデル
PreToolUse フックはアクションの前に実行されブロックできます。PostToolUse フックはアクションが成功した後に実行され、結果に反応します。
良い習慣
- フックを高速かつ冪等に保つ — 頻繁に実行されます。
- 本当の問題には声高に失敗するが、表面的な問題ではブロックしないこと。
- フックの出力を Claude へのフィードバックとして扱う — 明確なメッセージが自己修正を助けます。
- フックはあなたのシェルの権限で実行されます——自分で書いていないフックはレビューしてください(サードパーティコードのレビュー)。
よくある間違い
- ファイルパスを環境変数から読む。 パスは stdin の JSON(
.tool_input.file_path)にあり、$CLAUDE_FILE_PATHにはありません。stdin をjqに通してください。 - 無口なブロック。
PreToolUseフックが stderr に何も書かず終了コード 2 で終わると、Claude はブロックされるのに理由が分からず適応できません。常に明確な理由を書いてください。 - 遅いフック。
PostToolUseフックはすべてのマッチする編集の後に実行されます。3 秒かかるリンターはセッション全体をもたつかせます——フックを高速に保ち、理想的には変更されたものだけに作用させましょう。 - 広すぎるマッチャー。
matcher: ".*"はすべてのツールで発火します。正確な名前、Edit|Writeリスト、またはハンドラーごとのifフィールド(例:"if": "Bash(git push *)")で絞り込んでください。 - 自分で書いていないフックを信頼する。 フックはあなたの権限で任意のシェルを実行します。プラグインやテンプレートのフックはまず読んでください——サードパーティコードのレビューを参照。
- フックはあなたの権限で任意のシェルを実行します——プラグインやテンプレートのフックを、まず読まずに配線しないでください。
コピー&ペーストできるスターターはフックと settings.json のレシピにあります。
編集したファイルを自動フォーマット(Edit|Write の PostToolUse)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
]
}
]
}
}確認しよう
0/3- フックは振る舞いを要求ではなく保証する——権限が許可/拒否するだけのアクションの周りで決定論的なロジックを実行する。
- settings.json でイベントとマッチャーに対してフックを登録する。Claude が stdin に JSON ペイロードをパイプし、あなたの終了コードと出力を読む。
- ファイルパスは stdin(.tool_input.file_path)から読む——環境変数からではない。
- 終了コード 2(stderr がメッセージになる)または stdout の構造化 JSON(終了 0)でブロックする。常に明確な理由を含める。
- フックを高速、冪等、狭くマッチさせて保つ——そして自分で書いていないフックはレビューする。あなたのシェルの権限で実行されるため。
次へ
- settings.json · 権限
- スキル — 専門知識 vs 自動化
- 自律実行のハードニング