料理ブログをClaude Codeで作っている初心者向け
「またこのフレーズ書いてしまった」「画像を消すなと毎回言ってるのに消される」「保存後に毎回同じチェックを通したい」のように、Claude Codeに対して『絶対にやってほしい/やらないでほしい』処理を機械的に効かせたい場面で仕込む。料理ブログだと禁止フレーズの自動ブロックや、`/recipes/` 画像の誤削除防止などに使う
料理ブログをClaude Codeで作っていると、小事故が地味に積み重なります。「またこのフレーズ書いちゃった」「画像消えてる」「またあのチェックを通し忘れた」のたぐい。
そういう「やりがちなミス」を機械的に止めるのがフック。Claude Codeが何かをしようとした節目に、私が書いた小さなプログラムを差し込めます。具体的にはプロンプトを送る前、Bashコマンドを走らせる前、ファイルを書き換えた直後など。
例えば「『結論から言うと』を書こうとしたら強制ストップ」「`/recipes/` フォルダの画像を `rm` しようとしたら確認プロンプトを出す」。こういう仕組みを、設定ファイルに数行書くだけで仕込めます。
この記事は料理ブログをClaude Codeで作っている初心者向け。
噛み砕くと
ビルの入退室カードリーダーを思い浮かべてください。ドアの前にカードリーダーがあって、社員証をかざすとドアが開きます。社員証がなければ開きません。場合によってはアラームが鳴って警備員が飛んできます。
フックはこのカードリーダーです。Claude Codeが「これからプロンプトを送ろう」「これから `rm -rf` を走らせよう」とした瞬間、その手前にカードリーダー、つまり自前のチェックプログラムが立っていて、通していいかダメかを判定します。OKならそのまま通る。NGならその場で止まる。
違いは、止める条件を私が書き足せる点です。「『結論から言うと』が含まれていたらブロック」「Bashコマンドの中身に `rm` と `/recipes/` が両方入っていたらブロック」みたいな専用ルールを、シェルスクリプトやPythonで自由に書けます。
設定ファイルに5行書くと、送信ボタンを押した瞬間にチェックが走る
フックの設定は、プロジェクトのルートにある `.claude/settings.json` というファイルに書きます。料理ブログのプロジェクトフォルダ(例: `~/projects/cooking-blog`)の中に `.claude` フォルダを作って、その中に置く形です。
最小例として、こんな中身を書きます。
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": ".claude/hooks/check-banned-phrases.py"
}
]
}
]
}
}
これだけで、私がClaude Codeに何か送信ボタンを押すたびに、`UserPromptSubmit` イベントが発火して `.claude/hooks/check-banned-phrases.py` が呼び出されます。
呼び出されたスクリプトは、私が送ろうとしたプロンプト本文をJSONで受け取ります。中身を読んで「『結論から言うと』が入ってる」と判定したら、終了コード `2` で抜ける。すると Claude Code はその送信を止めて、スクリプトが標準エラー出力に書いたメッセージをClaudeに見せます。
送信ボタンが効かなくなった、ではなく「送信前に検閲が入る」イメージです。
料理ブログで「禁止フレーズ自動ブロック」を作る再現手順
記事の冒頭でつい「結論から言うと」と書いてしまう癖を、機械的に止める仕掛けを作ります。料理ブログのプロジェクト直下で順に進めます。
ステップ1: フォルダとファイルを用意する
$ cd ~/projects/cooking-blog
$ mkdir -p .claude/hooks
$ touch .claude/hooks/check-banned-phrases.py
$ chmod +x .claude/hooks/check-banned-phrases.py
4行目の `chmod +x` は「このファイルを実行可能にする」操作です。これがないとClaude Codeから呼び出しても起動しません。
ステップ2: チェックスクリプトを書く
`.claude/hooks/check-banned-phrases.py` の中身です。
#!/usr/bin/env python3
import json, sys, re
try:
data = json.load(sys.stdin)
except Exception:
sys.exit(0)
prompt = data.get("prompt", "")
BANNED = [
"結論から言うと",
"正直に書きます",
"ぶっちゃけて言うと",
]
for word in BANNED:
if word in prompt:
print(f"[blocked] 禁止フレーズ「{word}」が混入しています。", file=sys.stderr)
sys.exit(2)
sys.exit(0)
標準入力からJSONを読み、`prompt` 欄、つまり私が送ろうとした本文に禁止語が含まれていたら、`sys.exit(2)` で終了します。
ステップ3: settings.json にフックを登録する
`.claude/settings.json` を作って、前のセクションの最小例をそのまま貼り付けます。書き終わったらClaude Codeを再起動。
ステップ4: 動作確認
Claude Codeのチャットに「結論から言うと、今日はカレーのレシピを書いて」と入力して送信。すると送信ボタンを押した瞬間、こんな表示が出ます。
[blocked] 禁止フレーズ「結論から言うと」が混入しています。
そのまま「カレーのレシピを書いて」と書き直して送信すると、今度は普通に通ります。
初心者がここで勘違いしやすいのが、「フックがエラーになると Claude Code 全体が止まる」と思ってしまう点。実際は止まりません。`exit 2` で返した送信だけが取り消されて、別の文章を書き直して送れば普通に動きます。安全装置として軽く効いてくれる感じ。
フックの種類(イベント別の使い分け)
公式のイベント一覧は30種類近くありますが、料理ブログで実用的なのは下の5つくらいです。
| イベント名 | 発火タイミング | 料理ブログでの使いどころ |
|---|---|---|
UserPromptSubmit |
私がプロンプトを送信した直後 | 禁止フレーズ検閲、文字数制限 |
PreToolUse |
Claude Codeがツールを使う直前 | `rm` で画像を消そうとしたら確認、危険なBashコマンドのブロック |
PostToolUse |
ツールの実行が成功した直後 | 記事HTMLを保存した後の文字数カウント、リンク切れチェック |
SessionStart |
Claude Codeを起動・再開したとき | 「今日のタスク一覧を読み込む」「下書き状態のファイルを表示」など |
SessionEnd |
セッションを終了したとき | その日触ったファイルのバックアップ、作業ログの記録 |
ポイントは「Pre」と「Post」の違い。`PreToolUse` は実行する前に呼ばれて、`exit 2` で止められます。`PostToolUse` は実行後なので止められませんが、結果を見て次のメッセージとしてClaudeにフィードバックできます。
例えば「Edit ツールで記事HTMLを直した直後に文字数を数えて、3000字を超えてたら『長すぎ、削れ』とClaudeに伝える」みたいな後処理は `PostToolUse` で書きます。
料理ブログでの使いどころ
シナリオ1: `/recipes/` フォルダの画像を誤って消すのを防ぐ
料理ブログだと撮影済みの画像が `~/projects/cooking-blog/recipes/curry/main.jpg` みたいな場所に山ほど溜まります。Claude Codeに「いらないファイル整理しといて」とお願いした結果、Bashで `rm -rf` を走らせて画像まで消える事故が起きえます。
これを防ぐには `PreToolUse` に Bash 用フックを仕掛けて、`tool_input.command` に `rm` と `recipes/` が両方含まれていたら `exit 2` でブロック。stderr に「画像フォルダの削除はブロックしました」と書けばClaudeも別の手段、たとえば確認プロンプトを出す、特定ファイルだけ消す、に切り替える方向で動きます。
シナリオ2: 記事保存後に自動で文字数チェック
レシピ記事をClaude Codeに書かせた後、「800字以下だと薄い、3000字超えると読まれない」みたいな目安に外れていたら一発で気づきたい。`PostToolUse` の matcher を `Write|Edit` にして、書き込まれたファイルの文字数を数えるシェルスクリプトを呼び出します。範囲外なら標準出力に「現在 4200字、削れ」と書く。Claudeがそれを読んで自動で短縮にかかります。
シナリオ3: セッション開始時に「今日のレシピ一覧」を読み込む
料理ブログを毎日続けていると、「昨日まで何書いた?」を毎セッション聞くのが面倒になります。`SessionStart` で `recipes/` 配下のフォルダ名を列挙するシェルスクリプトを呼び出して、その出力を `additionalContext` としてClaudeに渡せば、起動直後に「カレー、肉じゃが、味噌汁の3記事が下書き中ですね」と認識した状態でスタートできます。
初心者が踏みやすい落とし穴
- `chmod +x` を忘れる。スクリプトを置いただけでは動きません。実行可能にする印を立てないと、Claude Codeが呼び出そうとして「permission denied」で静かに落ちます。落ちても画面には大きく出ないので気づきにくい。
- `exit 2` と `exit 1` を取り違える。ブロックしたいときは必ず `exit 2`。`exit 1` は「ふつうのエラー」扱いで、ブロックの効果がありません。公式仕様で `exit 2` だけが特別な意味を持ちます。
- `PostToolUse` でブロックしようとする。`PostToolUse` は「実行が終わった後」のイベントなので、`exit 2` で返してもファイル書き込みは戻りません。事前ブロックしたいなら必ず `PreToolUse` を選ぶ。
- `.claude/settings.local.json` と `.claude/settings.json` を混同する。前者はGitに含めない自分専用、後者はチーム共有用。料理ブログを1人で運用するなら基本は `.claude/settings.json` でOKですが、API キーを使うようなフックは `settings.local.json` 側に書く。
- 無限ループを作る。`PostToolUse` でファイルを `Edit` しなおすフックを書くと、その Edit がまた `PostToolUse` を発火させて永久に終わらない事故が起きえます。フックの中で別のClaude Code 操作を呼ぶときは、無限ループ条件を必ず確認する。
- 標準入力のJSONを壊す。フックスクリプトは `sys.stdin` からJSONを読みます。`json.load()` を try で囲まないと、想定外の入力が来た瞬間にスクリプトが落ちて、毎回プロンプト送信がエラー扱いされる地獄になります。
関連するコマンドへの動線
フックは「Claude Codeの行動を機械的に制御する仕組み」。これと近い関係にあるのが下記です。
/init- プロジェクトにCLAUDE.md(プロジェクト用の覚え書き)の雛形を作るコマンド。フックを仕掛ける前に、まずこれでプロジェクト構造を整える/memory-CLAUDE.mdをエディタで開いて編集するコマンド。フックの仕様や禁止フレーズ一覧をCLAUDE.mdに書いておくと、Claudeも参照できるSkills- スキル機能。フックが「節目で機械的に呼ばれる小プログラム」なのに対し、スキルは「特定の文脈でClaude自身が呼び出す手順書」。役割が逆方向で、組み合わせて使う/agents- サブエージェントを管理するコマンド。フックの中でサブエージェント関連イベント(SubagentStart、SubagentStop)に仕掛けることもできる/clear- 会話履歴をクリアするコマンド。SessionEndフックで毎回作業ログを記録しておけば、/clearで文脈を消しても次回再構築できる
フックとスキルの違いだけ補足。スキルは「Claudeが状況に応じて使う」もので、Claude側の判断が入ります。フックは「決めた節目で必ず呼ばれる」もので、Claudeの判断は介在しません。「絶対に止めたい」「絶対に毎回走らせたい」処理はフック、「状況によって出し入れしたい」処理はスキル、と切り分けると整理しやすい。
参考リンク
書き方
.claude/settings.json の "hooks" キーに { イベント名: [{ matcher, hooks: [{ type, command }] }] } を書く
やってみるとこうなる
入力
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": ".claude/hooks/check-banned-phrases.py"
}
]
}
]
}
}
出力例
[blocked] 禁止フレーズ「結論から言うと」が混入しています。
(プロンプト送信が exit 2 で取り消され、書き直しを促される)
このページに出てきた言葉
- イベント
- 「プロンプト送信」「ツール実行」など、Claude Codeの動作の節目のこと。フックはこの節目に紐付けて仕込む
- 発火
- そのイベントが起きてフックが実際に呼び出されること
- 終了コード
- プログラムが終わるときに返す数字。<code>0</code>が正常、<code>2</code>がフック専用の「ブロックして」を意味する特別な値
- 標準入力
- プログラムが読み込み側から受け取る入力。フックの場合、Claude Codeが状況情報をJSONで流し込んでくる
- 標準エラー出力
- プログラムが「これはエラーメッセージだよ」と出す側の出力経路。フックでは exit 2 と組み合わせてClaudeにフィードバックを返す
- PreToolUse
- Claude Codeがツールを使う直前に発火するイベント。<code>exit 2</code>で実行をブロックできる
- PostToolUse
- ツール実行が成功した直後に発火するイベント。後処理(文字数カウントなど)に使う。ブロックはできない
- UserPromptSubmit
- 私がClaude Codeにプロンプトを送信した直後に発火するイベント。送信内容のチェックに使える
- matcher
- フックを発火させる条件を絞るフィールド。例えば<code>"Bash"</code>と書けばBashツールのときだけ発火する
- settings.json
- Claude Codeの設定を書くファイル。フックの登録もここに書く。プロジェクト共有用は<code>.claude/settings.json</code>、自分専用は<code>.claude/settings.local.json</code>