タグ: Hooks

  • Hooks ー Claudeの動きの「前後」に、自分の指示を差し込む

    Hooks ー Claudeの動きの「前後」に、自分の指示を差し込む

    ずっと「お願い」していた

    毎回 CLAUDE.md に「ファイルを編集したら必ず prettier をかけて」と書いていた。Claude は気が向くとやってくれるし、気が向かないと忘れる。私と犬のしつけが同じ進捗を辿っているのを見て、これは依頼ではなく自動化の話だと気づいた。

    そこで出会ったのが Hooks(フック)だった。「Claude が動いた瞬間に、私の用意したスクリプトを横から差し込める」という、要するに 「お願い」を「実行」に格上げする仕組みである。


    結論マップ(ビフォー/アフター)

    場面Hooks を知る前Hooks を入れたあと
    編集後の整形毎回「prettier かけて」と頼むファイル編集 → 自動で prettier が走る
    危ないコマンドrm -rf を打つ前に祈る設定で問答無用ブロックされる
    終わったら通知画面を見続けて待つ終了の瞬間にデスクトップ通知が出る

    3行で済んだ。Claude にいちいちお願いするより、Claude の周りに自動ドアを設置するほうが結局は早い、というだけの話だった。


    Hooks って何

    公式ドキュメントによれば、Hooks は Claude Code のライフサイクルの特定タイミングで自動的に走る、ユーザー定義のシェルコマンド・HTTP エンドポイント・LLM プロンプト のこと。日本語で言えば「Claude の動作の前後に、自分の処理を割り込ませる栓抜き」だ。

    たとえばツール(Bash や Edit)を使う直前に走らせれば、危険なコマンドを止められる。直後に走らせれば、編集したファイルを自動で整形できる。会話が始まるとき、終わるとき、ユーザーがプロンプトを送ったとき、それぞれにフックポイントがある。

    💡 似た言葉だが、いわゆる「Webhook(外部から呼ばれる仕組み)」ではない。Hooks は自分のマシンの中で動くしかけ。つまり LINE スタンプを送るのではなく、自分の家の玄関に自動センサーライトを付ける、くらいのスケール感。


    どこに書くのか

    設定ファイルは置き場所で 適用範囲(スコープ) が変わる。これは公式ドキュメントの記載どおり:

    置き場所適用範囲共有
    ~/.claude/settings.json自分の全プロジェクト自分のマシン内のみ
    .claude/settings.jsonそのプロジェクトのみgit にコミット可
    .claude/settings.local.jsonそのプロジェクトのみgitignored

    このほか、プラグインに同梱したり、スキルやサブエージェントの frontmatter(YAML のメタ情報部分)に書く方法もある。「自分専用なら ~/.claude/、チームで共有するなら .claude/settings.json」と覚えると整理しやすい。


    どんなイベントにフックできるのか

    公式リファレンスに載っているイベントはかなり多いが、初心者がまず触りそうなのは以下:

    • SessionStart / SessionEnd — セッションの開始・終了
    • UserPromptSubmit — 私が Enter を押した直後、Claude が読む前
    • PreToolUse / PostToolUse — Claude がツール(Bash, Edit, Write, etc.)を呼ぶ直前・直後
    • PostToolUseFailure — ツールが失敗したあと
    • Stop — Claude が応答を終えた瞬間
    • Notification — Claude が通知を出すとき(権限確認など)
    • PreCompact / PostCompact — 会話圧縮の前後
    • FileChanged / CwdChanged — 監視対象ファイルが変わった/作業ディレクトリが変わった

    このほかにも SubagentStartInstructionsLoadedWorktreeCreate など細かいものが並んでいて、要は 「Claude が何かする」ほぼ全タイミングに切れ目が用意されている。映画の隙にどこでも CM が入れられる、と思うと近い。


    実例1:ファイル編集後に自動 prettier

    公式ガイドにそのまま載っている、いちばん腑に落ちる例。.claude/settings.json に書く:

    {
    "hooks": {
    "PostToolUse": [
    {
    "matcher": "Edit|Write",
    "hooks": [
    {
    "type": "command",
    "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
    }
    ]
    }
    ]
    }
    }

    matcher で「Edit か Write のとき」と絞り込み、Claude が編集したファイルパスを jq(JSON を取り出すコマンド)で抜き出して prettier に渡す。「お願い」の文言を CLAUDE.md に何行書くより、これ1つ置くほうが効く。これは私の犬の食事よりも確実に動く。

    💡 公式が jq を前提にしているので、Mac なら brew install jq、Ubuntu なら apt-get install jq を先に入れておく。私はここで30分溶かした(jq が入っていないと、フックは静かに失敗する)。


    実例2:危ないコマンドを問答無用で止める

    PreToolUse に「Bash で rm 系のコマンドが来たら止める」スクリプトを差し込む。.claude/hooks/protect-files.sh を作って中身は公式の例から拝借(exit 2 でブロックする):

    #!/bin/bash
    INPUT=$(cat)
    COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
    if echo "$COMMAND" | grep -qE 'rm -rf'; then
    echo "Blocked: rm -rf は禁止です" >&2
    exit 2
    fi
    exit 0

    実行権限を付けて(chmod +x .claude/hooks/protect-files.sh)、.claude/settings.json に登録:

    {
    "hooks": {
    "PreToolUse": [
    {
    "matcher": "Bash",
    "hooks": [
    {
    "type": "command",
    "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
    }
    ]
    }
    ]
    }
    }

    ポイントは exit 2。終了コード 2 は「ブロッキングエラー」と決まっていて、これを返すとツール呼び出し自体が止まり、stderr の文字列が Claude にエラーとして渡る(公式ドキュメント「Exit code output」セクション)。Claude には「あ、それはダメだったのね」と伝わるので、別の手を考えてくれる。父親の「ダメ」と母親の「いいよ」が衝突したとき、強いほうが通るアレ、と説明したい。


    実例3:Claude が止まった瞬間に通知

    長いタスクを投げて待っている間、私は別の作業をしている。気がついたら Claude は10分前に終わっていた、というのを何度かやらかしたので、Stop イベント(Claude が応答を終えた瞬間)にデスクトップ通知を出す:

    {
    "hooks": {
    "Stop": [
    {
    "matcher": "",
    "hooks": [
    {
    "type": "command",
    "command": "osascript -e 'display notification \"Claude が応答を終えました\" with title \"Claude Code\"'"
    }
    ]
    }
    ]
    }
    }

    これは Mac の例(osascript を使う)。Linux なら notify-send、Windows なら powershell.exe 経由でメッセージボックスを出す例が公式ガイドに載っている。要は 「終わり」の瞬間を取れるということ。私のスマホがピンと鳴るたび、Claude のタスクが終わっていて、Pavlov の犬のようになる未来が見えた。


    ここで止まりそうなポイント

    • 設定の置き場所を間違える~/.claude/settings.json(自分の全体)と .claude/settings.json(プロジェクト用)は別物。間違えるとフックが動かない or 全プロジェクトで暴走する。私は「家の鍵と会社の鍵を同じキーホルダーに付けたまま、うっかり会社の鍵で家を開けようとした」レベルで混乱した
    • exit 2 以外はブロックしない:終了コード 1 は「ノンブロッキングエラー」。Unix の慣習で 1 を返したくなるが、フックを「拒否」として使いたいなら必ず 2 にする。これは公式ドキュメントの「Exit code output」セクションに明記されている
    • /hooks で確認できる:今どんなフックが効いているのか分からなくなったら、Claude Code 内で /hooks と打つと、設定済みのフックが一覧で読める。私はこれで「設定したつもりで動いていなかった」ことに気づいた

    セキュリティの話(ここは飛ばさず読んでほしい)

    公式ドキュメントの「Security considerations」に明記されているとおり、Hooks は私のユーザー権限で任意のシェルコマンドを実行する。つまり、信頼できないフック設定をうっかり共有リポジトリから取り込むと、ファイルを消されたり、認証情報を盗まれたりする可能性がある。

    ⚠️ 要するに、人がくれた .claude/settings.json の中身は、見知らぬ人がくれた USB を挿す前と同じ気持ちで一度開いて読む。コマンドが読める範囲かどうか確認してから取り込むこと。便利な道具ほど、初対面の相手から黙って受け取ってはいけない。

    公式が挙げているベストプラクティスは「入力を信用せず検証する」「シェル変数は必ずクォートする("$VAR")」「絶対パスを使う」「.env.git/ には触らない」など。家の鍵を二重ロックするのと同じ感覚で、フックも二重に守る。


    全部止めたいとき

    設定を1個ずつ消す前に、disableAllHooks: true を settings.json に書けば、その階層のフックを丸ごと黙らせられる(公式リファレンス「Disable or remove hooks」セクション)。トラブルシュート時にまずこれで切り分けると早い。ブレーカーを落としてから配線を見る、あの感じ。


    まとめ/次に試したいこと

    Hooks は 「Claude が動く瞬間の前後に、自分の処理を差し込める仕組み」。お願い文を増やすより、設定1つで自動化したほうが早い場面が確実にある。

    • PostToolUse + Edit|Write … 編集後の自動フォーマット
    • PreToolUse + Bash … 危険コマンドのブロック
    • Stop … 終了通知
    • SessionStart + compact … 圧縮後にプロジェクトの大事なルールを再注入

    私が次に試したいのは、コミット前に自動でテストを走らせる PostToolUse と、ログを残す SessionEnd の組み合わせ。気がつけばリポジトリの周りに自動ドアと監視カメラと宅配ロッカーが揃っていて、私は寝ていても進捗が出る家になっているはず(は、言いすぎた)。

    なお、本記事の設定例・コマンド・イベント名は2026-04-27 時点の公式ドキュメントに基づく。Claude Code の更新頻度は高いので、実際に組む前に必ず一次情報を確認してほしい。


    関連リンク(一次情報)