マギのクロゼミ
研究室 #Claude#Hooks 公開 2026.04.28

Claude Hooks の書き方入門。settings.json から始める3例

Hooks は「特定のイベントで自動的にシェルコマンドを走らせる」しくみ——前回そう紹介しました。今回は実際に書く側に回ります。3 分で動く最小例から始めて、入力 JSON の使い方、環境変数、失敗時の直し方まで、`settings.json` をいちから組み立てていきます。

はじめに

前回の「Claude Code の Hooks とは」で、Hooks の正体——特定のイベントで harness が機械的にシェルコマンドを走らせるしくみ——をお伝えしました。今回は実際の 書き方 に進みます。

本記事のゴールは、

  • ~/.claude/settings.json がどこにあるか分かる
  • 最小の Hook が手元で動かせる
  • 3つの実例(編集後の git diff/危険コマンドのブロック/応答終了の通知)が書ける
  • うまく動かないときの直し方が分かる

の4点です。最初の Hook が動くまで の最短手順を、いちから歩きます。

settings.json の場所

Hooks の設定は settings.json という JSON ファイルに書きます。場所は3つあります。

スコープ場所適用範囲
User(あなた個人)~/.claude/settings.json全プロジェクト
Project(案件共通)<案件フォルダ>/.claude/settings.jsonその案件、Git で共有
Local(このマシン専用)<案件フォルダ>/.claude/settings.local.jsonその案件、git に上げない

最初に練習するなら User の ~/.claude/settings.json がおすすめです。1か所書き換えれば、すべてのプロジェクトで効くので、動作確認が楽です。

ファイルが存在しなければ、新規作成して構いません。空の状態から、

{}

の1行があれば最低限 OK です。

最小例:編集後に git diff --stat を表示する

最初の Hook として、「ファイルを編集するたびに git diff --stat を表示する」 という素朴なものを書きます。

~/.claude/settings.json に次を書き込みます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "git diff --stat HEAD"
          }
        ]
      }
    ]
  }
}

これだけで、Claude Code を 再起動 すると Hook が有効になります。Claude が EditWrite でファイルを書き換えるたびに、git diff --stat HEAD が走り、変更ファイルの一覧が会話に出てきます。

この JSON の構造を読む

短いですが、Hooks の構造はこの形がベースになります。

hooks                       ← Hooks 全体を入れる箱
└── PostToolUse             ← イベント種別
    └── 配列                ← 同じイベントに複数のグループを書ける
        └── matcher         ← どのツールに反応するか(正規表現可)
        └── hooks           ← 実行するコマンドの配列
            └── type        ← 種別("command" が基本)
            └── command     ← 実行するシェルコマンド

入れ子が深く見えますが、中身は「イベント → 対象ツール → 実行コマンド」の3段 だけです。慣れると一瞬で書けるようになります。

matcher の書き方

matcher には どのツール名のときに発火するか を正規表現で書けます。

matcher意味
"Edit"Edit ツールのときだけ
`“EditWrite”`
"Bash"Bash ツールのとき
""(空)すべてのツールで発火
".*"同上(すべて)

最初は 対象を絞っておく のが安全です。"" で全ツールに反応させると、想定外のところで Hook が走り、動作が読みづらくなります。

よく使うツール名

Claude Code でよく出てくるツール名は、

  • Edit / Write:ファイルの編集/新規作成
  • Read:ファイルの読み取り
  • Bash:シェルコマンドの実行
  • Grep / Glob:検索系

このあたりです。「ファイルが書き換わったときに何かしたい」なら Edit|Write、「コマンドが走る前に確認したい」なら Bash を使う、というのが定石になります。

Hook には「入力 JSON」が来る

Hook のコマンドが起動されるとき、標準入力(stdin)に JSON が流れ込んできます。中身には「何のイベントか」「どのツールが何の引数で呼ばれたか」が入っています。

たとえば PostToolUse(Edit) で発火した Hook の標準入力には、

{
  "session_id": "abc123",
  "hook_event_name": "PostToolUse",
  "tool_name": "Edit",
  "tool_input": {
    "file_path": "/Users/me/projects/site/src/index.ts",
    "old_string": "...",
    "new_string": "..."
  },
  "tool_response": { ... }
}

のような JSON が渡されます。Hook のコマンド側でこの JSON を読めば、「どのファイルが書き換わったか」「中身はどうなったか」が手に入ります

JSON を使う最小例

たとえば「書き換わったファイルが .ts だったら、そのファイルだけ型チェックする」という Hook はこう書けます(jq というJSON処理ツールを使う前提)。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path'); if [[ \"$FILE\" == *.ts ]]; then npx tsc --noEmit \"$FILE\"; fi"
          }
        ]
      }
    ]
  }
}

中身を分解すると、

部分意味
FILE=$(jq -r '.tool_input.file_path')標準入力の JSON から file_path を抜き出して FILE に入れる
if [[ "$FILE" == *.ts ]]; then ... fiFILE.ts で終わっていれば中の処理を走らせる
npx tsc --noEmit "$FILE"そのファイルだけ型チェック

シェルスクリプトが書ければ、Hook はかなり自由にカスタマイズできますjq は事前に brew install jq(Mac の場合)で入れておく必要があります。

環境変数も使える

Hook のコマンドの中では、いくつかの 便利な環境変数 が使えます。

環境変数意味
$CLAUDE_PROJECT_DIRいま Claude Code が動いているプロジェクトのフォルダ
$HOMEあなたのホームフォルダ(OS 標準)

たとえば「プロジェクトのルートに置いてあるスクリプトを呼ぶ」なら、

{
  "command": "$CLAUDE_PROJECT_DIR/scripts/post-edit.sh"
}

と書けます。プロジェクトごとに動きを変えたいときに重宝します。

実例 1:危険コマンドをブロック

PreToolUse(Bash) で、Claude が実行しようとしているコマンドをチェックして、rm -rf が含まれていたら止める という Hook を書いてみます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command'); if echo \"$CMD\" | grep -qE 'rm\\s+-rf'; then echo 'Blocked: rm -rf is not allowed'; exit 2; fi"
          }
        ]
      }
    ]
  }
}

ポイントは末尾の exit 2 です。PreToolUse の Hook が exit 2 で終わると、harness は 「このツール実行をブロック」 と解釈します。Claude には「Hook によってブロックされた」と伝わり、別の方法を考えてくれます。

終了コード意味
0正常終了。普通に実行を続ける
2ブロック。PreToolUse で使うとツール実行を止める
その他エラー扱い。stderr の内容が会話に出る

取り返しのつかないコマンドだけは止める」という保険は、特に Bypass Permissions モードで Claude を走らせるときの命綱になります。

実例 2:応答終了で通知を出す(macOS)

長く走らせていて別作業をしているとき、Claude の応答が終わったら macOS の通知センターに通知 を出す Hook です。

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

Stop イベントは Claude が応答を終えたタイミングで発火します。matcher を省略しているのは、Stop には対象ツールがないためです。

長いプロンプトを投げて別作業に移るときに、これを入れておくと 「終わったかどうかをわざわざ画面で確認しない」 で済むようになります。

失敗パターンと対処

Hooks を書き始めると、よく踏むパターンがあります。先回りで対処を書いておきます。

失敗 1. クォートが甘くて壊れる

JSON の中にシェルコマンドを書くので、クォートのエスケープ がやっかいです。

× "command": "echo "hello""           ← JSON が壊れる
○ "command": "echo \"hello\""         ← OK
○ "command": "echo 'hello'"           ← シングルクォートなら OK

複雑なコマンドは、シェルスクリプトファイルに切り出して $CLAUDE_PROJECT_DIR/scripts/foo.sh を呼ぶ ようにすると、クォート問題から解放されます。

失敗 2. ツール名のスペル違い

matcher"Edit""edit" と小文字で書くと反応しません。ツール名は大文字小文字を厳密に区別 します。/help で確認できる正式名で書いてください。

失敗 3. 設定ファイルの JSON 構文ミス

カンマの過不足、波括弧の閉じ忘れなど。JSON は厳しいので、書いたら必ず構文チェック することをおすすめします。

cat ~/.claude/settings.json | jq .

jq . を通して何も出なければ構文エラー、整形された JSON が返ってくれば OK です。

失敗 4. Claude Code の再起動を忘れる

settings.jsonClaude Code の起動時に読まれます。ファイルを書き換えただけでは、いま動いているセッションには反映されません。書き換えたら必ず Claude Code を起動し直す が鉄則です。

失敗 5. 標準出力に余計なものが出る

Hook のコマンドが標準出力に余計な情報を出すと、それが会話に表示されて鬱陶しいです。ログを残したいなら console.error 相当(stderr)か、ログファイルへリダイレクト するのが定石。

{
  "command": "your-command >> ~/.claude/hook.log 2>&1"
}

デバッグの3点セット

それでも動かないとき、頼れる確認手段は3つです。

1. コマンドを単独で動かしてみる

settings.json に書いたコマンドを、ターミナルで 直接実行してみる

echo '{"tool_input":{"file_path":"test.ts"}}' | <your-command>

JSON を echo で渡しながら動かすと、Hook の中で動いていることを再現できます。

2. ログファイルに記録する

{
  "command": "echo \"$(date) hook fired\" >> ~/.claude/hook.log"
}

シンプルにログを取って、ファイルが増えるか確認します。増えなければそもそも発火していない、増えていればコマンドのどこかで失敗している、と切り分けできます。

3. /permissions でブロックされていないか確認

権限設定で Hook の動作を絞っているケースもあります。/permissions で現状を確認してみてください。

触り始めるときの順序(再掲)

前回の概念編にも書きましたが、Hooks は 慎重に始める のが結果的に早道です。

  1. PostToolUse で「結果を表示するだけ」の Hookgit diff --stat など)
  2. 慣れたら Stop で通知
  3. その次に PreToolUse でブロック系
  4. 最後に UserPromptSubmit でログ取得や定型挿入

副作用のある Hook は 後回し に。「想定外の場面で発火して困った」が一番ありえる失敗なので、最初の数日は読むだけ系でクセを掴むのが安全です。

まとめ

Hooks 基本の書き方の要点を整理します。

  1. 書き場所は ~/.claude/settings.json(または案件配下)
  2. 構造は「イベント → matcher → command」の3段
  3. 最小例:PostToolUse(Edit|Write)git diff --stat を出す
  4. matcher はツール名の正規表現:絞らないと想定外で発火する
  5. 標準入力に JSON が来るjq で抜き出して使える
  6. $CLAUDE_PROJECT_DIR などの環境変数が使える
  7. PreToolUseexit 2 するとツール実行をブロック
  8. Stop 通知で長時間ジョブの完了を物理的に知る
  9. 失敗5パターン:クォート/ツール名/JSON構文/再起動忘れ/標準出力の汚染
  10. デバッグは「単独実行」「ログファイル」「/permissions」の3点

これで Hooks シリーズの2本(とは/書き方)が読み終わりました。Skills が「知識」、MCP が「外部接続」、Hooks が「タイミング」——と、Claude Code の 3軸の拡張機構 がひと通り出揃いました。

次の記事「Claude Code の SubAgent とは」では、Step 3 の最後のテーマ SubAgent(サブエージェント) に進みます。SubAgent は「メイン会話のコンテキストを汚さず、別働隊として独立した Claude を走らせるしくみ」で、重い調査やコードベースの大掃除を任せるのに向いています。続けて読むと、Claude Code の使いこなしが「1人の Claude と話す」から「複数の Claude を采配する」段階に進みます。

WordPressを実際に動かしてきたサーバー:ロリポップ

Claude Code でWordPressサイトを組み立てるとき、最初に置く先として無理のないレンタルサーバー。月数百円から始められ、WordPressの自動インストールにも対応しています。設定で詰まりがちな初期段階の時間をかなり減らせます。

🌱
ロリポップ!レンタルサーバー
GMOペパボ運営 / 月額¥220〜
ロリポップを見る →
Roadmap 学習マップでこの記事の位置を見る
Step 1 →