メインコンテンツへスキップ
見出し画像

Claude Codeのフック(hooks)の使い方|settings.jsonの書き方と実用例5つ・Windowsでつまずく所まで、実機で確認【2026年10月】

    「CLAUDE.md に『.env は触らないで』と書いたのに、書き換えられた」。「毎回『編集したらフォーマットして』と頼むのが面倒」。Claude Code を使い込むと、こんな場面に出会います。

    CLAUDE.md の指示は、あくまで「お願い」です。守られることが多くても、AI が判断で破る可能性はゼロではありません。そこで使うのが、フック(hooks)です。

    この記事では、フックとは何か、どう設定するか、実用例5つを、実際に動かして確かめた結果とあわせて説明します。

    私は Claude Code と Codex を毎日使って、教材づくり、note・Gumroad への出品、画像づくりを自動化しています。私の作業場でも、フックを使っています(セッションの開始と、依頼を送るたびに、作業の一覧を AI に添える処理です。あとで書きます)。

    この記事のフックは、読み物のために書いたものではなく、実際に動かして確かめたものです。Windows 11 の Claude Code で、ファイルの編集と危険なコマンドを止めるフックを入れ、AI に実際に頼んで、止められるところまで確認しました。そのときに Windows でつまずいた所も、そのまま書きます。全文無料です。

    この記事で分かること

    • フックとは何か。CLAUDE.md の指示と何が違うのか

    • 主なイベントと、いつ動くか

    • settings.json の書き方と、置き場所

    • 終了コード 0 と 2、JSON で返す方法

    • 実用例5つ(通知・自動整形・保護ファイルのブロック・危険コマンドのブロック・圧縮後の再注入)

    • 実機で確かめた結果と、Windows でつまずいた文字コードの問題

    • 動かないときの調べ方、安全の注意

    先に結論

    • フックは、Claude Code の動きの決まったタイミングで、あなたが書いた処理を必ず実行する仕組みです。公式のドキュメントの言葉では、「AI が実行するかを選ぶのではなく、必ず起きる」ものです

    • 設定は settings.json に書きます。「どのイベントで・どの道具に対して・何を実行するか」の3つを決めます

    • 処理が終了コード 2 で終わると、その操作を止められます。標準エラー出力に書いた文は、AI へのフィードバックになります

    • 「お願い」と「仕組み」の違いは大きい。絶対に守らせたいことは、CLAUDE.md ではなく、フックに書くのが基本です

    • 私の環境では、権限の確認を省く設定にしていましたが、それでもフックの「止める」は効きました

    • Windows では、Python で書くと楽です。ただし、日本語を出力するときは文字コードに注意が要ります

    フックとは

    フックは、「Claude Code の動きの途中で、決まったタイミングに、自動で動く処理」です。公式のドキュメントでは、こう説明されています。

    Hooks are user-defined shell commands. Claude Code runs them at specific points in its lifecycle, which gives you deterministic control: certain actions always happen rather than relying on the LLM to choose to run them.

    つまり、「LLM(AI)に実行を選ばせるのではなく、必ず実行させる」ための仕組みです。

    「お願い」と「仕組み」

    CLAUDE.md に書くのは、AI へのお願いです。AI はそれを読んで、判断して動きます。多くは守られますが、長い作業の途中で抜けたり、状況によっては別の判断をしたりします。

    フックは、AI の判断の外で動きます。AI が「.env を書き換えよう」と決めても、その直前にフックが走って、止められます。AI の気分に関係なく、必ず起きます。

    使い分けは、こうです。

    • 多少ぶれてもよい、考え方や文体:CLAUDE.md

    • 絶対に守らせたい、守れなかったら困ること:フック

    主なイベント

    フックは、「いつ動くか」を、イベントで決めます。公式のドキュメントには多くのイベントが載っていますが、最初に覚えるのは、次の6つです。

    • SessionStart:セッションが始まったとき(再開したときも)

    • UserPromptSubmit:あなたが依頼を送ったとき。AI が読む前に動く

    • PreToolUse:AI が道具(ファイルの編集・コマンドの実行など)を使う直前。ここで止められる

    • PostToolUse:道具を使ったあと(成功したとき)

    • Notification:Claude Code が通知を出すとき(確認待ちなど)

    • Stop:AI が返答を終えたとき

    ほかにも、PreCompact(会話の圧縮の前)、SubagentStop(サブエージェントの終了)、SessionEnd(終了)など、30を超えるイベントが用意されています。最初は、上の6つで足ります。

    settings.json の書き方

    フックは、設定ファイル(settings.json)に書きます。基本の形は、次のとおりです。

    {
      "hooks": {
        "イベント名": [
          {
            "matcher": "対象の道具の名前",
            "hooks": [
              {
                "type": "command",
                "command": "実行するコマンド"
              }
            ]
          }
        ]
      }
    }

    matcher:どの道具のときに動くか

    matcher は、「どの場合に動くか」の絞り込みです。PreToolUse や PostToolUse では、道具の名前を書きます。

    • "Bash":コマンドを実行するとき

    • "Edit|Write":ファイルを編集・作成するとき(| は「または」)

    • 空("")か省略:そのイベントのすべての場合

    絞らないと、すべての道具の使用のたびに動きます。できるだけ狭く絞ります。

    置き場所

    公式のドキュメントでは、場所によって、効く範囲が変わります。

    • ~/.claude/settings.json:自分の全プロジェクト。自分のパソコンだけ

    • .claude/settings.json:そのプロジェクト。Git に入れれば、チームで共有できる

    • .claude/settings.local.json:そのプロジェクトだけ。共有しない

    設定したあと、Claude Code の中で /hooks と打つと、設定されているフックの一覧が出ます。書いたあとは、まず /hooks で、登録されているかを確かめるのが、確実です。

    フックの入力と出力

    フックの処理(コマンド)は、標準入力から JSON を受け取り、終了コードで答えます。

    入力

    イベントが起きると、Claude Code が、状況の JSON をコマンドの標準入力に渡します。たとえば、PreToolUse(Bash)で npm test を実行しようとしているとき、こんな JSON が渡されます。

    {
      "session_id": "abc123",
      "cwd": "/Users/sarah/myproject",
      "hook_event_name": "PreToolUse",
      "tool_name": "Bash",
      "tool_input": {
        "command": "npm test"
      }
    }

    ファイルの編集なら、tool_input の中に file_path が入ります。UserPromptSubmit なら、送った文(prompt)が入ります。

    出力:終了コード

    終了コードで、次の動きが決まります。

    • 終了コード 0:異論なし。PreToolUse の場合でも、これは「許可」ではなく、通常の許可の流れに進むだけです

    • 終了コード 2:その操作を止める。標準エラー出力に書いた理由が、AI へのフィードバックとして渡る

    • それ以外:多くのイベントでは、エラーとして扱われるが、動きは止まらない

    出力:JSON で返す

    終了コード 0 のまま、JSON を標準出力に出すと、もっと細かく制御できます。たとえば PreToolUse で、操作を断る場合です。

    {
      "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "Use rg instead of grep for better performance"
      }
    }

    permissionDecision は、allow(確認を省く)・deny(取り消して、理由を AI に返す)・ask(普通に確認する)の3つです。

    2つの注意が、公式のドキュメントにあります。

    • 終了コード 2 で止める方法と、JSON で返す方法は、1つのフックの中でどちらか一方にする(混ぜない)

    • permissionDecision は、hookSpecificOutput の中に入れる。外側に置いても、JSON としては読まれますが、効きません(エラーも出ません)

    画像

    実用例5つ

    例1:確認待ちを通知する(Notification)

    AI が許可を待って止まっているとき、デスクトップに通知を出す。長い作業を任せて、別のことをしているときに便利です。公式のドキュメントの例を、使いやすい形で書きます。

    macOS:

    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
              }
            ]
          }
        ]
      }
    }

    Windows(PowerShell):

    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
              }
            ]
          }
        ]
      }
    }

    公式のドキュメントによると、Windows のこの例は、画面の隅の通知ではなく、ダイアログを開きます。ターミナルの後ろに隠れることがあるそうです。動かないときは、まずその命令を PowerShell で直接実行して確かめてください。

    例2:編集したら、自動で整形する(PostToolUse)

    AI がファイルを編集したあと、自動でフォーマッタ(Prettier など)を走らせます。「編集したら整形して」と毎回頼まなくてよくなります。公式のドキュメントの例です。

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

    jq は、JSON から値を取り出すコマンドです。Windows には最初から入っていないので、入れるか、次の例のように Python で書き換えてください。

    例3:保護ファイルの編集を止める(PreToolUse・終了コード 2)

    .env(鍵が入っている)、package-lock.json、.git/ の中身を、AI に触らせない例です。次の Python のスクリプトを、.claude/hooks/protect_files.py に置きます。

    #!/usr/bin/env python3
    """PreToolUse: 触ってほしくないファイルへの Edit / Write を止める"""
    import json
    import sys
    
    sys.stderr.reconfigure(encoding="utf-8")  # Windows の既定の文字コードで日本語が化けないように
    
    PROTECTED = [".env", "package-lock.json", ".git/"]
    
    data = json.load(sys.stdin.buffer)  # 入力は UTF-8 の JSON
    path = (data.get("tool_input", {}).get("file_path") or "").replace("\\", "/")
    
    for pattern in PROTECTED:
        if pattern in path:
            # 標準エラー出力に書いた文が、そのまま Claude へのフィードバックになる
            print(f"Blocked: {path} は保護されているファイルです({pattern})。別の方法を考えてください", file=sys.stderr)
            sys.exit(2)  # 終了コード 2 = この操作を止める
    
    sys.exit(0)  # 0 = 異論なし(通常の許可の流れに進む)

    .replace("\\", "/") は、Windows のパスの区切り(\)を、/ にそろえるためです。公式の例(シェルスクリプト)にも、同じ理由の一行があります。

    例4:危険なコマンドを止める(PreToolUse・JSON)

    rm -rf、git push --force、git reset --hard のような、取り返しのつかないコマンドを止める例です。こちらは、JSON で返す方法で書きます。.claude/hooks/block_dangerous.py です。

    #!/usr/bin/env python3
    """PreToolUse(Bash): 取り返しのつかないコマンドを止める"""
    import json
    import re
    import sys
    
    RULES = [
        (r"\brm\s+-\w*r\w*f|\brm\s+-\w*f\w*r", "rm -rf は使わないでください。消すものを1つずつ指定してください"),
        (r"\bgit\s+push\b.*(--force\b|\s-f\b)", "force push は止めています"),
        (r"\bgit\s+reset\s+--hard\b", "git reset --hard は止めています。先に git stash で退避してください"),
    ]
    
    data = json.load(sys.stdin.buffer)  # 入力は UTF-8 の JSON
    command = (data.get("tool_input") or {}).get("command", "")
    
    for pattern, reason in RULES:
        if re.search(pattern, command, re.IGNORECASE):
            # 終了コード 0 のまま JSON を出す方法。理由は Claude に返る
            # ensure_ascii の既定(True)なら日本語は \uXXXX になり、Windows でも化けない
            print(json.dumps({
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": reason,
                }
            }))
            sys.exit(0)
    
    sys.exit(0)

    ルールは完璧ではありません。たとえば rm -r -f(オプションを分けて書く)は、この正規表現ではすり抜けます。最初は少ないルールで始めて、実際に困ったものを足していくのが現実的です。

    登録する

    2つのスクリプトを、.claude/settings.json に登録します。

    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/protect_files.py\""
              }
            ]
          },
          {
            "matcher": "Bash",
            "hooks": [
              {
                "type": "command",
                "command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/block_dangerous.py\""
              }
            ]
          }
        ]
      }
    }

    $CLAUDE_PROJECT_DIR は、プロジェクトのフォルダを指す変数です。Windows 11 でも、この書き方で動きました。

    実機で確かめた結果

    この設定のフォルダで、Claude Code に、わざと止められることを頼みました。

    1つ目は、「rm -rf build を実行して」と頼みました。AI の答えは、こうでした。

    拒否されました。理由:「rm -rf は使わないでください。消すものを1つずつ指定してください」

    フックが書いた理由が、日本語のまま AI に届いています。

    2つ目は、「.env というファイルに KEY=1 と書き込んで」と頼みました。AI は、「.env ファイルは保護されているため書き込みができません」と答えました。実際に、.env は作られませんでした。

    なお、私の環境は、権限の確認を省く設定(bypassPermissions)にしてありました。それでも、フックの「止める」は効きました。公式のドキュメントにも、「PreToolUse のフックは、どの権限モードよりも先に動き、deny は権限確認を省く設定でも効く」と書かれています。「確認を省く設定」にしているときこそ、フックが安全網になります。

    例5:会話の圧縮後に、大事な決まりごとを入れ直す(SessionStart)

    会話が長くなると、Claude Code は、過去のやりとりを要約して圧縮します。そのとき、細かい決まりごとが抜けることがあります。SessionStart の compact という matcher を使うと、圧縮のあとに、決まりごとを入れ直せます。公式のドキュメントの例です。

    {
      "hooks": {
        "SessionStart": [
          {
            "matcher": "compact",
            "hooks": [
              {
                "type": "command",
                "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
              }
            ]
          }
        ]
      }
    }

    標準出力に書いた文が、そのまま AI の記憶に足されます(SessionStart と UserPromptSubmit の場合)。

    私の使い方:作業の一覧を、毎回 AI に添える

    私の作業場でも、フックを2つ使っています。SessionStart と UserPromptSubmit の両方で、同じ処理(自作のコマンド)を走らせています。

    やっていることは、こうです。私は、「依頼」「タスク」「質問」などを、画面(ボード)に書いて管理しています。セッションが始まったとき、そして私が何か送るたびに、フックが走ります。そして、ボードに新しく足した項目や、変えた項目があれば、それを AI のメッセージに添えます。

    これで、会話の途中で私がボードに項目を足しても、次のメッセージで AI に伝わります。AI に「ボードを見て」と言う必要がありません。

    CLAUDE.md に「ボードを見て」と書く方法もあります。でも、それは「お願い」です。フックにすると、AI が見るのを忘れても、必ず届きます。ここが、フックのいちばんの強みです。

    Windows でつまずいた所:文字コード

    この記事のスクリプトを作るとき、最初の版では、Windows で次の問題にぶつかりました。

    問題:日本語の出力が、化けた

    最初は、理由の日本語を、そのまま print() で出力していました(JSON のほうは ensure_ascii=False でした)。模擬の入力を流して出力を見ると、日本語が文字化けしていました。Python の標準出力は、Windows では、既定でその PC の文字コード(日本語環境では Shift_JIS 系)になるためです。

    Claude Code は、フックの出力を UTF-8 として読むはずなので、この状態では食い違うおそれがあります。私は、この版を実際の Claude Code にかける前に直したので、「化けたまま届く」ところまでは確かめていません。直したあとの版は、次のとおり、日本語の理由が正しく AI に届きました。

    直し方

    • 標準エラー出力(終了コード 2 の方法):sys.stderr.reconfigure(encoding="utf-8") を、先頭に1行入れる

    • JSON で返す方法:json.dumps() の既定(ensure_ascii=True)のままにする。日本語は \uXXXX に変換されて、どの文字コードでも壊れません。ensure_ascii=False にしないでください

    • 標準入力の読み込み:json.load(sys.stdin) ではなく、json.load(sys.stdin.buffer) にする。念のための対策です。バイト列のまま読めば、日本語のファイル名やパスが入っていても、UTF-8 として読めます

    上のスクリプトは、直したあとのものです。

    jq が入っていない

    公式の例は、jq を使っています。Windows には、最初から入っていません。Python で書けば、追加のものを入れずに、同じことができます。

    動かすのに、シェルの違いはある?

    私の環境では、python "$CLAUDE_PROJECT_DIR/.claude/hooks/xxx.py" という書き方で動きました。ただし、環境によって、フックのコマンドを実行するシェルが違うことがあります。動かないときは、まず、コマンド単体をターミナルで実行してください。

    動かないときの調べ方

    1. /hooks で登録されているか見る

    一覧に出ていなければ、settings.json の場所や書式が間違っています。JSON のカンマや括弧のミスも多いです。

    2. matcher を見直す

    Edit|Write のように、道具の名前が正しいか。大文字・小文字も区別されます。

    3. コマンドを単体で動かす

    標準入力に JSON を流して、終了コードを確かめます。

    echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf build"}}' | python .claude/hooks/block_dangerous.py
    echo $?

    4. デバッグログを見る

    claude --debug で起動すると、フックの終了コードや出力のログが見られます。公式のドキュメントでも、これが勧められています。

    5. JSON の項目の場所を見直す

    permissionDecision や additionalContext を、hookSpecificOutput の外側に置いても、エラーは出ません。ただ、効きません。「JSON を返しているのに、何も起きない」ときは、まずここを疑います。

    安全のために

    フックは、あなたの権限で、あなたのパソコンのコマンドを実行します。便利な分、扱いには注意が要ります。

    • 知らないリポジトリの .claude/settings.json に、フックが書かれていないか確かめる:他人のプロジェクトを開くとき、そこの設定に書かれたフックは、コマンドとして実行されます。中身を見てから使ってください

    • フックの中で、鍵や個人情報をログに残さない:標準入力の JSON には、ファイルの中身や入力した文が入ることがあります

    • 止めるフックは、最初は少なく:厳しすぎると、AI が必要な作業までできなくなります。実際に困ったものを、1つずつ足します

    • フックが遅いと、その分、全体が遅くなる:重い処理は、timeout を決めるか、別の方法にします

    よくある疑問

    Q. フックと、権限の設定(許可・拒否のルール)は、何が違いますか?

    権限の設定は、「この道具を使ってよいか」を、名前や形式で決めるものです。フックは、もっと細かく、「中身を見て」判断したり、自動で別の処理を走らせたりできます。両方を組み合わせて使います。

    Q. フックを書くのが難しそうです

    Claude Code 自身に、「.env を編集しようとしたら止めるフックを作って」と頼めば、書いてくれます(公式のドキュメントにも、AI に書かせる方法が載っています)。そのうえで、この記事のように、実際に止まるかを試してください。

    Q. 複数のフックが、同じイベントで動くと?

    同じイベントに合うフックは、並列で動きます。PreToolUse で結果が食い違った場合は、いちばん厳しいもの(deny → ask → allow の順)が採用されます。1つが deny を返しても、他のフックの処理も最後まで動きます。止めたいときに、他のフックの副作用に頼らないでください。

    注意点

    • この記事の内容は、2026年10月15日の公式ドキュメントと、私の Windows 11 の環境で確かめたものです。イベントや項目は、版が上がると変わることがあります。迷ったら /hooks と、公式のドキュメントを見てください

    • 危険コマンドのルールは、完璧ではありません。あくまで「うっかり」を防ぐ網です。悪意ある操作を防ぐ仕組みとして、これだけに頼らないでください

    • フックの設定ミスで、必要な作業まで止まることがあります。止まったら、/hooks で確認し、必要ならいったん外してください

    まとめ

    • フックは、Claude Code の動きの決まったタイミングで、必ず動く処理。CLAUDE.md の「お願い」と違い、AI の判断の外で動く

    • 最初に覚えるイベントは、SessionStart・UserPromptSubmit・PreToolUse・PostToolUse・Notification・Stop の6つ

    • 設定は settings.json。イベント → matcher → コマンドの順。書いたら /hooks で登録を確かめる

    • 処理は、標準入力で JSON を受け取り、終了コード 2(止める)か、JSON(permissionDecision)で答える。この2つは混ぜない

    • 実用例は、通知・自動整形・保護ファイルのブロック(例3)・危険コマンドのブロック(例4)・圧縮後の再注入。例3と例4を実機で確かめた

    • 権限の確認を省く設定でも、PreToolUse の deny は効く

    • Windows では、Python で書く。標準入力は sys.stdin.buffer、標準エラーは reconfigure(encoding="utf-8")、JSON は ensure_ascii の既定のまま

    • 知らないリポジトリのフックは、実行される前に中身を見る

    次の一歩は、.env を編集しようとしたら止まるフックを1つ入れて、AI に頼んで、本当に止まるかを試すことです。10分で、できます。


    次に読む

    このアカウントをフォローすると、Claude Code や Codex の新しい手順の記事が届きます。ぜひフォローしてください。

    この先で分かること(今後の有料記事)

    • そのままコピーして使えるフックの実物(保護・通知・整形・記録・上限のチェックなど)

    • 自動実行(claude -p)の中で、フックを安全網にする設計

    • 実際に起きた失敗と、直し方の記録

    まさのAI実務ラボ|Codex・Claude Code では、有料記事が読み放題になります。テンプレートや指示文をまとめて使いたい方はご覧ください。

    あなたへのおすすめ