Claude Codeで作業していると、毎回同じ確認を自分の手でやっていることに気付きます。ファイルを保存したら整形されているか目で見て確認する。危険そうなコマンドが提案されたら、実行前に「これ本当に大丈夫か」と読み直す。長い作業を投げたら、終わるまで画面の前で待つ。
以前スラッシュコマンドの記事で「毎回同じ指示をテキストで打つのをやめる」話を書きました。今回はその一歩先です。「毎回同じ指示を打つ」のではなく、「そもそも自分が手で確認しているものを仕組みに任せる」というやり方があります。それがHooksです。
Hooksとは何か
Hooksは、Claude Codeのライフサイクルの決まったタイミングで、自分で書いたシェルコマンドを必ず実行させる仕組みです。settings.json(プロジェクト直下の.claude/settings.jsonか、ユーザー全体の設定)に書いておくと、Claude自身の判断を待たずに、指定したイベントで確実に動きます。
「確実に」というのがポイントです。CLAUDE.mdに「コミット前は必ずフォーマッタを走らせてください」と書いても、Claudeが忘れることはあります。Hooksはそこをプロンプトの指示ではなく、OSレベルのコマンド実行に落とし込みます。
公式docs(code.claude.com/docs/en/hooks)で確認できた主なイベントは以下の通りです。
| イベント | 発火タイミング |
|---|---|
| PreToolUse | ツール(Bash・Write・Editなど)を実行する直前 |
| PostToolUse | ツールの実行が終わった直後 |
| Notification | 権限確認など、Claudeがユーザーに通知を出すタイミング |
| Stop | Claudeが応答(一連の作業)を終えたタイミング |
| SubagentStop | サブエージェントの作業が終わったタイミング |
| UserPromptSubmit | ユーザーがプロンプトを送信した直後 |
| SessionStart / SessionEnd | セッションの開始・終了 |
| PreCompact | コンテキストの圧縮(compact)が走る直前 |
(正直に書いておくと、二次媒体の記事の中には「Hookイベントは全部で30種類ある」と書いているものが複数ありました。ですが今回公式docsで名前を確認できたのはこの8〜9個程度です。今後増える可能性はありますが、執筆時点で「30種類」を裏付ける記述は公式docsには見つけられませんでした。数字を鵜呑みにせず、自分の`settings.json`が実際に発火するかは手元で確認するのが確実です。)
設定は、イベント名ごとに「どのツールにマッチさせるか(matcher)」と「実行するコマンド」を並べる形です。PreToolUse・PostToolUseはmatcherにツール名(BashやWriteなど)を正規表現で指定できます。Stop・Notification・SessionStartはmatcher自体を省略します。
では実際に、私が「毎回手でやっていた確認」だった3つを仕組みに変えていきましょう。
パターン1: 保存時に自動フォーマット
ファイルを編集させた後、毎回自分でフォーマッタを走らせるのは地味に面倒です。PostToolUseでWrite・Editの直後にフォーマッタを挟めば、フォーマットのかかっていないコードがそもそも生まれなくなります。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATHS\""
}
]
}
]
}
}
PostToolUseのフックには、実行結果(tool_output)まで含めたJSONが標準入力で渡ってきます。今回のように単純に対象ファイルへコマンドを打つだけなら、環境変数の展開だけで足りることがほとんどです。
これで保存のたびにprettierが走るので、フォーマットのずれたコードをコミットしてCIで怒られる、ということがなくなります。瞬殺ですね。
パターン2: 危険なコマンドを実行前にブロックする
これが一番効果を実感したパターンです。Claude Codeは基本的に危険な操作の前には確認を求めてきますが、慣れてくると「はい、はい」と読まずに許可してしまう瞬間が出てきます。rm -rfやgit push --forceを、確認プロンプトの文面をろくに読まずに通してしまった経験、私にはあります。
PreToolUseでBashコマンドの中身を見て、危ないパターンならそもそも実行させない、という仕組みにできます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/guard.py"
}
]
}
]
}
}
guard.py側では、標準入力にJSONで渡ってくるtool_input.commandを見て、危険なパターンにマッチしたら終了コード2で終了します。公式docsによると、PreToolUseで終了コード2を返すとそのツール呼び出しがブロックされ、標準エラー出力の内容が「なぜブロックされたか」としてClaudeに返ります。
import json, re, sys
data = json.load(sys.stdin)
command = data.get("tool_input", {}).get("command", "")
DANGEROUS = [
r"rm\s+-rf\s+/",
r"git\s+push\s+.*--force",
r"git\s+reset\s+--hard",
]
for pattern in DANGEROUS:
if re.search(pattern, command):
print(f"危険なコマンドのためブロックしました: {pattern}", file=sys.stderr)
sys.exit(2)
sys.exit(0)
ブロックされると、Claudeは「なぜダメだったか」を理由付きで受け取るので、そのまま別のやり方を考え直してくれます。ここは自分でリストを育てていく前提の仕組みです。案件によって危ないコマンドは変わるので、force-pushや本番デプロイ系のコマンドなど、自分の現場でヒヤッとしたものから足していくのが良いと思います。
(ここは白状しておくと、正規表現だけでの防御には限界があります。書き方を少し変えられたら素通りする可能性はゼロではありません。あくまで「うっかり」を防ぐための仕組みで、権限管理そのものの代わりにはならないと考えています。)
パターン3: 長い作業の完了を通知する
これも地味に効きます。Claude Codeに大きめのタスクを投げると、数分から数十分画面を見ていないといけない気がしてしまいますが、実際は見ている必要はありません。Stopフックで、応答が終わったタイミングに音や通知を鳴らせば、その間は別の作業に集中できます。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff"
}
]
}
]
}
}
macOSなら上のように音を鳴らすだけでも十分ですし、私は自分の日次の自動化タスク運用では、作業が終わったタイミングでメール通知を飛ばす仕組みを組んでいます。仕組みとしては同じで、Stopフックからスクリプトを1本叩いているだけです。
これをやってから、終わるまで画面に張り付かなくてよくなりました。他の作業を進めておいて、通知が来たら結果を見に戻る、という流れに変わっています。地味な変化ですが、積み重なると結構な時間になります。
設定する場所とスコープ
Hooksは.claude/settings.json(プロジェクト単位・チームで共有したい設定)と、ユーザー全体の設定ファイルの両方に書けます。危険コマンドのブロックのように「どのプロジェクトでも常に有効にしたい」ものはユーザー全体側に、フォーマッタのようにプロジェクトごとに使うツールが違うものはプロジェクト側に書くのが素直だと思います。
ここで一つ注意です。Hooksはあくまでシェルコマンドをそのまま実行する仕組みなので、書いたコマンドは自分のマシンの権限で動きます。共有リポジトリの.claude/settings.jsonに見慣れないコマンドが入っていたら、中身を確認してから使うようにしてください。ここは公式docsでもセキュリティ上の注意点として書かれている部分です。
まずはPostToolUseかStopから
3パターンとも紹介しましたが、いきなり全部揃える必要はありません。まずはPostToolUseのフォーマット自動化かStopの完了通知から試してみましょう。どちらも失敗しても実害が小さいので、Hooksの動きに慣れるのにちょうど良いと思います。危険コマンドのブロックは、自分がよく使うコマンドを誤検知でブロックしてしまうこともあるので、慣れてから育てていくのがいいはずです。
「コマンドを覚える」から「確認作業そのものを仕組みにする」への一歩、思っていたより小さい設定ファイル1つで済みました。サブエージェントへの委任の使い分けについてはこちらの記事でも書いているので、Claude Codeを「仕組み」として使う話に興味があれば合わせて読んでみてください。テスト自動化の文脈でツールの使い分けを整理したSkills回とも通じるところがあります。
自分の環境で他にも「これは仕組みにできるな」という確認作業があれば、コメントで教えてもらえると嬉しいです。


コメント