Claude Code hooksについて解説してみる

Claude Code hooksについて解説してみる

Clock Icon2025.07.08

お疲れさまです。とーちです。

皆さんclaude code hooks使ってますか?なかなかおもしろい機能ですが、理解するのが難しい部分もあったので、この記事ではhooksについて解説していきたいと思います。

そもそもhooksとは

まずhooksについて理解していきましょう。公式ページは以下になります。

https://docs.anthropic.com/en/docs/claude-code/hooks

ざっくりいうとClaude Codeの特定のアクションの前後にシェルコマンドを挟み込めるという機能になっています。

これによって例えば以下のようなことが実現できます(上記、公式ページより)

  • 通知: Claude Code が入力待ち状態のときなどに通知を受け取る方法をカスタマイズします。
  • 自動フォーマット: ファイル編集のたびに prettier などのフォーマッターを自動で実行させます。
  • ログ記録: コンプライアンスまたはデバッグのために、実行されたすべてのコマンドを追跡して記録します。

hooksの設定項目

それでは、まずhooksの設定について理解していきましょう。

hooksには大まかに以下の設定項目があります

  • EventName: どのイベントに対してhooksを起動するか。例えばTool(Claude Codeが使用するRead、EditやMCPサーバ等のこと)の使用前(PreToolUse)、使用後(PostToolUse)等のイベントがあります。
  • matcher: PreToolUseまたはPostToolUseのEventにのみ使用可能なパラメータです。これらのEventでは様々なToolが使用されるため、どのTool使用時にhooksを起動するかを指定するのに使用します。
  • hooks
    • command: 条件に合致したときにhooksとして実行するBashコマンドを記載します。

設定項目を見るとhooksがどのような機能か理解しやすいですね。これらの設定項目を/hooks スラッシュコマンド、または、settings.json ファイルで設定するとhooksが動作するようになります。

hooksの動作確認

もう一つ重要なポイントとしてcommandに指定したコマンドがどのように動作するのかという点があります。

これは実際に動かしてみるとわかりやすいです。

まずsettings.json に以下のような設定を書きます。

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Task|TodoWrite|Write|MultiEdit|Edit",
      "hooks": [{
        "type": "command",
        "command": "jq '.' >> ~/.claude/post_tool_use_hook_debug.log"
      }]
    }]
  }
}

上記の設定では、Claude CodeがTaskやWrite等のToolを使用した後jqコマンドが実行され、コマンドの結果が~/.claude/post_tool_use_hook_debug.log に出力されます。jqコマンドは標準入力として入力されたJSON文字列を解析(パース)し、整形して出力します。jq '.' の 「.」 は、「入力されたJSONをそのまま出力する」という意味のフィルタになります。

イメージしやすくするためにシンプルなjqコマンドの実行例を載せておきます。

bash-5.2$ echo '{"test":"value"}' | jq '.'
{
  "test": "value"
}

この状態でclaude codeを起動し以下のように指示してみます。

> 適当なファイルを作成してみて

⏺ Write(sample_file.txt)
  ⎿  Wrote 5 lines to sample_file.txt
     サンプルファイル
     作成日時: 2025-07-06
     目的: フックイベントのテスト用ファイル
     このファイルは動作確認のために作成されました。

⏺ ファイルを作成しました。

ファイル作成のために「Write」Toolが使用されました。hooksの条件が満たされたので、jq コマンドが実行され、~/.claude/post_tool_use_hook_debug.log は以下のような内容が記載されました。

{
  "session_id": "********-****-****-****-************",
  "transcript_path":"<~/.claudeのフルパス>/projects/-Users-<User名>--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "<作成したファイルのフルパス>/sample_file.txt",
    "content": "サンプルファイル\\n作成日時: 2025-07-06\\n目的: フックイベントのテスト用ファイル\\n\\nこのファイルは動作確認のために作成されました。"
  },
  "tool_response": {
    "type": "create",
    "filePath": "<作成したファイルのフルパス>/sample_file.txt",
    "content": "サンプルファイル\\n作成日時: 2025-07-06\\n目的: フックイベントのテスト用ファイル\\n\\nこのファイルは動作確認のために作成されました。",
    "structuredPatch": []
  }
}

つまり、hooksのcommandに書かれたコマンドが実行される際に標準入力として、発生したイベントに応じたJSONが入力されているということになります。この基本的な動きが分かるとイベントに応じた情報を使用して、色々なことができそうだなと想像をふくらませることができますね。

シェルセッションの検証

ついでにシェルセッションがどういう扱いになっているのかも確認しておきましょう。以下のような設定をsettings.jsonにしてみます。

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "export HOOK_TEST_VAR=\"PreToolUse Hook Session\" && echo \"PreToolUse: 環境変数設定 HOOK_TEST_VAR=$HOOK_TEST_VAR\" >> ~/.claude/session_test.log && pstree -p $$ >> ~/.claude/session_test.log 2>&1 && echo \"=== End PreToolUse ===\" >> ~/.claude/session_test.log"
      }]
    }]
  }
}

「Bash」Tool使用前に、環境変数をエクスポートするコマンドを実行させ、その値とpstreeの結果をファイルに記録させます。これでClaude CodeがBashでコマンド実行する前に環境変数が設定されるはずです。この状態で、以下のような指示を出します。つまり、hooksにより環境変数がセットされた後に、Claude Codeに「Bash」Toolを使わせ、セットされた環境変数を参照させようというわけです。

> これを実行してみて

  echo \"Bashツール: 環境変数設定 HOOK_TEST_VAR=$HOOK_TEST_VAR\" >> ~/.claude/session_test.log && pstree -p $$ >>
  ~/.claude/session_test.log 2>&1

⏺ Bash(echo "Bashツール: 環境変数設定 HOOK_TEST_VAR=$HOOK_TEST_VAR" >> ~/.claude/session_test.log && pstree -p $$ >>
       ~/.claude/session_test.log 2>&1)
  ⎿  (No content)

すると以下のような結果になりました。

PreToolUse: 環境変数設定 HOOK_TEST_VAR=PreToolUse Hook Session
-+= 00001 root /sbin/launchd
 \-+= 68073 [USERNAME] /Applications/iTerm.app/Contents/MacOS/iTerm2

<中略>

           \-+= 74493 [USERNAME] claude  
             \-+- 74933 [USERNAME] /bin/sh -c export HOOK_TEST_VAR="PreToolUse Hook Session" && echo "PreToolUse: 環境変数設定 HOOK_TEST_VAR=$HOOK_TEST_VAR" >> ~/.claude/session_test.log && pstree -p $$ >> ~/.claude/session_test.log 2>&1 && echo "=== End PreToolUse ===" >> ~/.claude/session_test.log
               \-+- 74934 [USERNAME] pstree -p 74933
                 \--- 74935 root ps -axwwo user,pid,ppid,pgid,command
=== End PreToolUse ===
Bashツール: 環境変数設定 HOOK_TEST_VAR=
-+= 00001 root /sbin/launchd

<中略>

           \-+= 74493 [USERNAME] claude  
             \-+= 74946 [USERNAME] /bin/zsh -c -l source /var/folders/25/tg9t4tq912b5_kn08rhzdk6w0000gp/T/claude-shell-snapshot-470d && eval 'echo "Bashツール: 環境変数設定 HOOK_TEST_VAR=$HOOK_TEST_VAR" >> ~/.claude/session_test.log && pstree -p $$ >> ~/.claude/session_test.log 2>&1' \< /dev/null && pwd -P >| /var/folders/25/tg9t4tq912b5_kn08rhzdk6w0000gp/T/claude-370c-cwd
               \-+- 74962 [USERNAME] pstree -p 74946
                 \--- 74963 root ps -axwwo user,pid,ppid,pgid,command

ここから分かることは、hooksで起動されるcommandのシェルセッションはその後に実行されるBashツールのシェルセッションとは異なるということです。そのためhooksで環境変数をセットしていたとしてもそれがその後に実行されるBashツールで引き継がれることはありません。環境変数が引き継げればAWS認証などを通すのに便利かなと思ったのですが、別の方法で検討する必要がありそうですね。

hooksのスコープ

hooksの設定はsettings.json に記述されるため、settings.json のスコープをそのまま適用できます。スコープとはどの範囲で指定した設定を有効とするかです。以下の範囲があります。

  • ~/.claude/settings.json (ユーザー設定): ここにhooksを記述すると、そのPC端末上の全てのプロジェクトでhooksが有効になります。
  • .claude/settings.json (プロジェクト設定): 特定のプロジェクト内でのみhooksが有効になります。
  • .claude/settings.local.json (ローカルプロジェクト設定): Git管理外とすることで特定のプロジェクト内の個人環境でのみ有効となる設定。Claude Codeはこのファイル作成時に .gitignore に含めるようにするとのこと。

このほかに Enterprise managed policy settings でも設定が可能なようですが、私は使ったことがないので説明は割愛します。

イベントの詳細検証

大まかな概要について理解したところで、設定の分かりづらい部分について私なりに解説します。

EventNameの種類

Eventの種別としては以下のものがあります。

  • PreToolUse: Tool使用前に実行
  • PostToolUse: Tool使用後に実行
  • Notification: 通知発生時に実行
  • Stop: Claude Codeの応答完了時に実行
  • SubagentStop: サブエージェント完了時に実行

PreToolUseとPostToolUseについては分かりやすいのですが、その他のイベントがいまいちイメージが湧きません。そこで調べてみました。

下準備として、settings.json に以下のような設定をしておきます。

{
  "hooks": {
    "Notification": [{
      "hooks": [{
        "type": "command",
        "command": "jq '.' >> ~/.claude/notification_test.log"
      }]
    }],
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "jq '.' >> ~/.claude/stop_test.log"
      }]
    }],
    "SubagentStop": [{
      "hooks": [{
        "type": "command",
        "command": "jq '.' >> ~/.claude/subagent_stop_test.log"
      }]
    }]
  }
}

Notificationイベントの検証

まずnotificationのイベントを試してみます。

これはClaude Codeによるユーザーへの通知を指しています。ここでいう通知とは、例えば以下のようにClaude Codeからユーザーに実行許可を求められることはよくあると思いますが、このことを指しているようです。

> 適当なファイルを作成して

⏺ Write(../../../tmp/testfile)

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Create file                                                                                                           │
│ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ ../../../tmp/testfile                                                                                             │ │
│ │                                                                                                                   │ │
│ │ これはテストファイルです。                                                                                        │ │
│ │ 作成日時: 2025-07-06                                                                                              │ │
│ │ フックイベントのテスト用に作成されました。                                                                        │ │
│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ Do you want to create testfile?                                                                                       │
│ ❯ 1. Yes                                                                                                              │
│   2. Yes, add /tmp and don't ask again this session (shift+tab)                                                       │
│   3. No, and tell Claude what to do differently (esc)                                                                 │
│                                                                                                                       │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

上記の場合、以下のような内容が~/.claude/notification_test.logに出力されます。

{
  "session_id": "********-****-****-****-************",
  "transcript_path": "<~/.claudeのフルパス>/projects/-Users-<User名>--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Write"
}

SubagentStopイベントの検証

最後にSubagentStopです。公式ドキュメントによると、「Claude Code サブエージェント (タスク ツール呼び出し) が応答を完了したときに実行」されるとのことです。

確認するために以下のような指示を出してみます。

> SubagentStop を確認したいがこれはどういうタイミングで発動するものか。
  公式ドキュメントでは以下のような記載がある
  Claude Code サブエージェント (タスク ツール呼び出し) が応答を完了したときに実行されます。

⏺ 公式ドキュメントによると、SubagentStopイベントは「Claude Code
  サブエージェント(タスクツール呼び出し)が応答を完了したときに実行される」とのことです。

  つまり、Taskツールを使用してサブエージェントを起動し、そのサブエージェントが作業を完了して応答を返したタイミングで
  SubagentStopイベントが発生するはずです。

  実際に確認してみましょう:

⏺ Task(SubagentStop test)
  ⎿  Done (2 tool uses · 24.3k tokens · 15.9s)

⏺ Bash(ls -la ~/.claude/subagent_stop_test.log 2>/dev/null && echo "SubagentStopログファイルが存在します" || echo
      "SubagentStopログファイルは存在しません")
  ⎿  -rw-r--r--@ 1 [USERNAME]  staff  267  7  7 06:38 /Users/[USERNAME]/.claude/subagent_stop_test.log
     SubagentStopログファイルが存在します

⏺ Bash(cat ~/.claude/subagent_stop_test.log)
  ⎿ {
      "session_id": "********-****-****-****-************",
      "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-
    … +4 lines (ctrl+r to expand)

Taskツールが実行されたので、 ~/.claude/subagent_stop_test.log にも内容が出力されました。

{
  "session_id": "********-****-****-****-************",
  "transcript_path": "<~/.claudeのフルパス>/projects/-Users-<User名>--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false
}

そもそもTaskツールとはどのようなツールなのでしょうか?Claude Codeに調べてもらったところ公式ドキュメント上には明確な記載がないとのことでしたが、以下のように解説してくれました。( ※下記内容にはハルシネーションと思われる回答が含まれています

⏺ 公式ドキュメントにはTaskツールの直接的な説明は見つかりませんでした。ただし、私の理解では:

  Taskツールは、複雑なタスクを実行するためにサブエージェントを起動するツールです。サブエージェントは:

  1. 独立したClaude Codeインスタンスとして動作
  2. 与えられたプロンプトに基づいて自律的に行動
  3. 複数のツールを使用して作業を完了
  4. 最終的な結果を親エージェントに返す

  実際の動作例:
  - 親エージェント(私)がTaskツールを呼び出す
  - サブエージェントが起動し、指定されたタスクを実行
  - サブエージェントが複数のツール(Read、Bash、Grepなど)を使用
  - タスク完了後、結果を返してSubagentStopイベントが発生

  これは、複雑な作業を委任したり、並列処理を行ったりする際に便利です。

上記を確認すべく、task実行中にsleepコマンドを実行させpstreeで確認したところ以下のような結果でした。PID=82682のclaudeプロセスはTask依頼元のClaude Codeのプロセスになります。

<省略>
		   \-+= 82682 [USERNAME] claude  
             |-+= 87948 [USERNAME] /bin/zsh -c -l source /var/folders/25/tg9t4tq912b5_kn08rhzdk6w0000gp/T/claude-shell-snapshot-b100 && eval 'echo "=== タスク開始: $(date) ===" > /tmp/long_task_log.txt && echo "現在のPID:
             | \--- 88147 [USERNAME] (sleep)
<省略>

どうも、 独立したClaude Codeインスタンスとして動作 という部分や 与えられたプロンプトに基づいて自律的に行動 という記載についてはハルシネーションっぽい気がしますね。

実際には上記の通り、Claude CodeからClaude Codeを呼び出すような動きはしておらず、あくまでもサブプロセスとして、コマンドを実行しているだけのように見えます。

何はともあれ、Taskツールを実行し、完了した際にSubagentStop イベントが発生することは確認できました。

Stopイベントの検証

最後にStopですが、これはClaude Codeの応答が完了したときです。

例えば、Claude Codeに test と入力します。するとtestと応答を返しやり取りが完了しました。

> test

⏺ test

すると ~/.claude/stop_test.log には以下のような内容が出力されています

{
  "session_id": "********-****-****-****-************",
  "transcript_path": "<~/.claudeのフルパス>/projects/-Users-<User名>--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}

stop_hook_activeというパラメータが気になりますね。公式ドキュメントには以下の記載があります。(日本語訳したものを記載)

stop_hook_active が true の場合、Claude Code は既にストップフックの結果として実行中です。この値(stop_hook_activeの値)を確認するか、トランスクリプトを処理して、Claude Code が無限に実行されないようにしてください。

ここだけ読むと、どういうタイミングで"stop_hook_active": true になるのかさっぱり分かりませんでしたが、色々と検証をすることでtrueになる状況が分かってきました。

まず前提として、hooksコマンドの終了コードについて知る必要があります。

hooksで実行されるコマンドは単なるbashコマンドなので必ず終了コードを持っています。この終了コードはClaude Codeに伝達され、終了コードによってClaude Codeの動作は変わります。

  • 終了コード 0: 成功。処理は継続される(例えばPreToolUseならTool実行に移るということ)。
  • 終了コード 2: Claude Codeの処理を停止する(例えばPreToolUseなら実行されるはずだったTool使用を実行しない)。また、標準エラー出力の内容がClaude Codeにフィードバックされ、自動で処理される
  • その他の終了コード: エラー扱いだが処理は継続。stderrの内容がユーザーに表示される。

stop_hook_activeを知るうえで重要なのが、終了コード2のときの挙動です。このとき標準エラー出力のエラーメッセージの内容を元にClaude Codeがエラーを解消しようと自動で解消するための処理を行おうとします。

処理を行った後は再度応答を停止します。すると再度Stopイベントのhooksコマンドが実行されます。この挙動は「Claude Codeが処理して応答完了」→ 「Stop hooksコマンド実行(終了コード:2)」→「Claude Codeが処理して応答完了」 → 「Stop hooksコマンド実行(終了コード:2)」・・・といった無限ループを引き起こす可能性があります。

例えば以下のような記載をsettings.jsonにしてみましょう

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "jq '.' >>  ~/.claude/stop_test.log && exit 2"
      }]
    }]
  }
}

この状態でClaude Codeにstopイベントを発生させます。応答を完了させればstopイベントは発生するので単純な返答で済むようなプロンプトを送ります。

> こんにちわ

⏺ こんにちは!何かお手伝いできることはありますか?

> Stop hook feedback:
  - [jq '.' >>  ~/.claude/stop_test.log && exit 2]: No stderr output

⏺ フックの出力を確認しました。stop_test.logファイルが作成されているか確認してみましょう。

⏺ Read(stop_test.log)
  ⎿  Read 7 lines (ctrl+r to expand)

⏺ フックが正常に動作しています。Stopイベントがログに記録され、stop_hook_active:
  falseとなっているので、フックシステムは期待通り機能しているようです。

  何か特定の問題や質問がありますか?

> Stop hook feedback:
  - [jq '.' >>  ~/.claude/stop_test.log && exit 2]: No stderr output

⏺ フックが再度実行されました。ログファイルを確認してみましょう。

⏺ Read(stop_test.log)
  ⎿  Read 13 lines (ctrl+r to expand)

⏺ 2回目のStopイベントが記録されました。今度はstop_hook_active:
  trueになっています。フックは正常に動作し、セッション終了時のデータを適切にログファイルに記録しています。

> Stop hook feedback:
  - [jq '.' >>  ~/.claude/stop_test.log && exit 2]: No stderr output

⏺ フックが3回目実行されました。ログを確認します。

⏺ Read(stop_test.log)
  ⎿  Read 19 lines (ctrl+r to expand)

⏺ 3つのStopイベントが記録されています。stop_hook_activeの値がfalse → true →
  trueと変化しています。フックシステムは正常に動作し、各セッション終了イベントを適切にキャプチャしています。

> Stop hook feedback:
  - [jq '.' >>  ~/.claude/stop_test.log && exit 2]: No stderr output

⏺ Read(stop_test.log)
  ⎿  Read 25 lines (ctrl+r to expand)

<以下省略するがこのやりとりが延々と続く>

Stopイベントに対して、jqコマンドの出力がファイル出力されていますが、標準エラー出力には特にメッセージが出ていないので、Claude Codeは「何か特定の問題や質問がありますか?」と応答を返すことで応答を完了させています。するとそれに対してStopイベントが再度発動し、無限ループとなっているのがわかりますね。

ちなみに、~/.claude/stop_test.log の内容は以下のようになりました。2回目以降は"stop_hook_active": true となっていますね。hooksのcommand定義のところで、stop_hook_activeの値による判断を入れておけば永久ループを防ぐことができるというのが分かるかと思います。

{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}
{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
{
  "session_id": "********-****-****-****-************",
  "transcript_path": "/Users/[USERNAME]/.claude/projects/-Users-[USERNAME]--claude/********-****-****-****-************.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

JSON OutputでのTool制御

hooksでコマンドを実行する際に標準出力に特定のJSONデータを出力すると、そのJSONデータに応じた処理をClaude Codeに実行させることができます。

例えばPreToolUseイベントでは、以下のJSONデータを出力することでTool呼び出しを実行するか否かを制御できます。

{
  "decision": "approve" | "block" | undefined,
  "reason": "Explanation for decision"
}

decisionではツール呼び出しを許可(approve)するか拒否(block)するか、または既存の認可フローに移譲(undefined)できます。

またreasonには許可や拒否した理由を記述します。ここで書いた文章はClaude Codeも認識するので、禁止または許可された後、Claude Codeが何をするかをある程度制御できます。後ほど実際の例を見せます。

実際の例

実際に試してみましょう。settings.jsonには以下のように記載します。

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/scripts/block_write_tool.sh"
      }]
    }]
  }
}

続いて~/.claude/scripts/block_write_tool.sh は以下のようなスクリプトにします

#!/usr/bin/env bash
set -euo pipefail

# Writeツールを絶対に禁止するPreToolUseフック
# matcherで"Write"が指定されているため、このスクリプトはWriteツールでのみ実行される

# Writeツールの使用を完全にブロック
cat << 'EOF'
{
  "decision": "block",
  "reason": "Writeツールの使用は禁止されています。ファイルの作成や変更は許可されていません。"
}
EOF

catでJSONを出力するだけです。とてもシンプルですね。

この状態で、Claude Codeでファイルを書かせてみます。

> 適当なファイルを作成してください

⏺ 適当なファイルを作成します。

⏺ Write(sample.txt)
  ⎿  Error: Write operation blocked by hook:
     - Writeツールの使用は禁止されています。ファイルの作成や変更は許可されていません。

⏺ フック設定により、Writeツールでのファイル作成が禁止されています。設定を確認してください。

ではreasonを少し変えてみましょう。

cat << 'EOF'
{
  "decision": "block",
  "reason": "一時的なシステム制限です。別の方法でファイル作成を試してください。"
}
EOF

この状態で再度同じ命令をだすとどうなるでしょうか?

> 適当なファイルを作成してください

⏺ test.txtというファイルを作成します。

⏺ Write(test.txt)
  ⎿  Error: Write operation blocked by hook:
     - 一時的なシステム制限です。別の方法でファイル作成を試してください。

Claude Opus 4 limit reached, now using Sonnet 4

⏺ hooksによってファイル作成がブロックされました。Bashコマンドを使用してファイルを作成します。

⏺ Bash(cat > /Users/kikuchi.toshinori/.claude/test.txt << 'EOF'
      これはテストファイルです。…)

このようにechoコマンドでファイルを作成しようとしました。上記のようにreason次第で拒否された後の動作の方向性を決めることができるようになっています。

まとめ

Claude Code hooksは、Claude Codeの動作をカスタマイズできる強力な機能です。この記事で解説した内容をまとめると以下のようになります。

hooksでできること

  • 通知のカスタマイズ: Claude Codeの入力待ち状態などでの通知設定
  • 自動フォーマット: ファイル編集時の自動フォーマッター実行
  • ログ記録: 実行されたコマンドの追跡と記録
  • Tool制御: JSON出力によるTool実行の許可/拒否制御

注意点

  • hooksのシェルセッションはBashツールとは独立している
  • 終了コード2を使用する際は無限ループに注意
  • stop_hook_activeフラグを活用して適切な制御を行う

hooksを活用することで、Claude Codeをより自分の開発環境に適合させることができます。セキュリティ制御やワークフロー自動化など、様々な場面で活用できそうですね。

以上、とーちでした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.