hooks-create について
hooks-createは、要件をClaude Codeのライフサイクルフック、スクリプト、設定に落とし込むエージェントスキルです。イベント選定、対象の絞り込み、実装、動作確認を案内し、操作をブロックするかどうかも扱います。
主な機能
- 01要件をライフサイクルイベントに対応付け
- 02イベントのマッチャーとブロック動作を選択
- 03フックスクリプトを作成して設定
- 04既存設定を上書きせずにフックを追加
- 05許可とブロックの動作をテスト
活用シーン
- 01指定ファイルへのアクセスを防止
- 02エージェントの完了前にチェックを実行
- 03セッション開始時にコンテキストを注入
- 04ツール操作後に整形やログ記録を実行
MCP 対応
フックの安全性についてMCPサーバーと同様の注意を促しますが、MCPへの接続やサーバー管理機能は記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ coleam00/ skills --skill hooks-createインストール後、Claude Code で/hooks-createを入力して読み込まれたか確認します。
詳細
フックを作成する:アイデアを実用的な Claude Code フックに変える
ユーザーがフックに求める動作:$ARGUMENTS
入力されている場合は、それを動作仕様として扱い、そこから始めます。すでに伝えられた内容を聞き直さず、 不足している点(正確なパス/コマンド/パターン、ブロックが必要かどうか)だけを確認します。空欄の場合は、 フックに何を保証させたいのか、何をさせたいのかを尋ねるところから始めます(ワークフローのステップ 1)。
フックとは(30 秒でわかる概要)
フックは、Claude Code のライフサイクルイベント(ツールの実行前後、エージェントが終了しようとするとき、 セッションの開始時など)に自動的に実行される決定論的なコードです。スキルやサブエージェントとは異なり、 エージェントがフックを呼び出すかどうかを選ぶことはありません。モデルが「覚えている」かどうかに関係なく実行されます。 これが要点です。ルールはエージェントにある振る舞いを「求める」だけですが、フックはモデルが言い逃れできない ツール層で、それを保証します。
ユーザーが持ち込むのはアイデア(「エージェントに .env を絶対に読ませない」)であり、このスキルがコードを書いて
設定します。Python の知識は必要ありません。
すべては組み合わせです。フックは、ハーネスが決められたタイミングで実行する小さなスクリプトにすぎず、
.claude/settings.json で設定します。AI レイヤーに決定論的な保証をもう 1 つ追加するだけです。
重要なのは 1 つだけ:どのイベントを使うか、ブロックできるか
ユーザーが求める動作は、1 つのライフサイクルイベントに対応します。実行すべきタイミングと、 何かを停止する必要があるかどうかで選びます。
| ユーザーの要望 | イベント | ブロックできるか |
|---|---|---|
| エージェントが何かをするのを止める(秘密情報を読む、保護されたパスを編集する、破壊的なコマンドを実行する) | PreToolUse ⭐ | はい - ツールの実行前にブロックします |
| アクションの後に反応する(編集したファイルを自動整形する、コマンドを記録する、コンテキストを注入する) | PostToolUse | いいえ - ツールはすでに実行済みです。監視/整形/注入のみ行います |
| チェックに合格するまで作業が「完了」しないことを保証する(テスト/lint/型チェックがすべて成功するまで停止させない) | Stop(または SubagentStop) | はい - 停止をブロックし、エージェントを作業に戻します |
| モデルが見る前に、ユーザーのプロンプトをゲートまたはスキャンする | UserPromptSubmit | はい - プロンプトをブロックできます。コンテキストの注入も可能です |
| セッションが始まるたびにコンテキストを読み込む | SessionStart | いいえ - コンテキストの注入のみ行います |
| エージェントがユーザーを必要としたとき/完了したときに通知を受け取る | Notification(または Stop) | いいえ - 副作用のみ(デスクトップ/Slack/サウンド) |
| コンテキストの圧縮前に状態のスナップショットを取る | PreCompact | いいえ |
Pre = 保証/ゲート。Post = 反応/記録。 目的が「X を絶対に起こさせない」または「Y が終わるまで完了させない」 なら、ブロックするフック(PreToolUse/Stop/UserPromptSubmit)が必要です。「W が起きたら Z を実行する」 なら、監視/反応するフック(PostToolUse/SessionStart/Notification)が必要です。
必読の資料(最初に確認してください)
フックのイベント一覧と標準入力/標準出力の正確な仕様は変更されるため、古い情報に頼らないでください。作成する前に、 最新のドキュメントを取得し、イベント名、入力フィールド、制御プロトコルを確認してください。
- フックのリファレンス:https://code.claude.com/docs/en/hooks
- フックガイド(例):https://code.claude.com/docs/en/hooks-guide
これらに WebFetch を使用し、これから書く内容と照合してください。取得に失敗した場合は、上の表にある標準のイベントを使って作業を進め、
レポートでその旨を伝え、ユーザーが再確認できるようにしてください。
実行プロトコル(フックが Claude Code と通信する方法)
- 入力: Claude Code は stdin に JSON オブジェクトを渡します。常に
session_id、cwd、hook_event_nameが含まれます。tool_nameとtool_input(ツールイベント)、prompt(UserPromptSubmit)、source(SessionStart)など、イベント固有のフィールドも含まれます。 - 出力/制御:
exit 0- 許可/成功です。UserPromptSubmitとSessionStartでは、stdout に出力された内容が エージェントのコンテキストに挿入されます。exit 2- ブロックします。 アクションが阻止され、stderr に出力した内容が理由として エージェントに渡されるため、エージェントはそれに応じて対応します。(これをサポートするイベントのみで有効です。表を参照してください。)- その他の終了コード - ブロックしないエラーです。ユーザーに表示され、実行は続行されます。
- 上級者向け(任意): 終了コードの代わりに、stdout に JSON オブジェクトを出力します。例:
{"decision":"block", "reason":"…"}(Stop)、または{"hookSpecificOutput":{"hookEventName":"PreToolUse", "permissionDecision":"deny","permissionDecisionReason":"…"}}。ユーザーが入力/出力を変更する必要がある場合や、additionalContextでコンテキストを挿入する必要がある場合を除き、シンプルな終了コード形式を使用してください。 フィールド名は取得したドキュメントで確認してください。
ワークフロー
1. アイデアを把握する($ARGUMENTS から始め、不足点のみを確認する)
ユーザーが $ARGUMENTS ですでに説明した内容から始めます(ユーザーは技術に詳しくない場合があります)。平易な言葉で次の2点を確認し、不足している情報だけを尋ねてください。
- 何を実行または防止するのか、そしていつ実行するのか(アクションの前/後、終了時、セッション開始時など)。
- どの程度正確に一致させるのか(「すべての
.envファイル」、「migrations/フォルダー」、「rm -rf」、「テストコマンドがゼロ以外で終了する」など)。具体的なファイルパス/コマンド/パターンを確認してください。一致条件の精度が保証の精度を左右するため、推測しないでください。依頼が曖昧な場合は、具体的な解釈を提案して確認してください。
2. ドキュメントを読む
フックのリファレンス/ガイド(上記)を取得し、対象イベントの名前、stdin のフィールド、制御プロトコルを確認してください。
3. イベントを選ぶ(およびマッチャーを設定する)
表を使って、対象イベントを1つ選びます。対象を厳密に絞るマッチャーを選んでください。ツールイベントの場合はツール名(例:"Bash"、"Edit|Write"、"mcp__.*")を指定します。空欄/"*" はすべての発生を意味します。目的が特定のものなら、すべてに対して発火させないでください。
4. フックスクリプトを書く
- デフォルトでは、
.claude/hooks/<event_snake_case>.pyにuvの単一ファイル Python スクリプトを作成します。uv run --scriptの shebang と、インラインの# /// scriptメタデータを使うことで、インストールも専用のプロジェクト仮想環境も不要になります。ユーザーが希望する場合に限り、別の言語を使ってください。 - stdin から JSON を読み取り、チェックを実行します。
- ブロックする場合:明確な理由を
stderrに出力し、sys.exit(2)を実行します。 - 許可する場合:
sys.exit(0)を実行します(コンテキストを挿入するイベントでは、任意で stdout にコンテキストを出力できます)。
- ブロックする場合:明確な理由を
- フェイルオープンにします。 予期しないエラーが発生した場合は
0で終了するように、処理全体をラップしてください。壊れたフックによってユーザーのセッションが決して使えなくならないようにします。意図的にゼロ以外の値で終了するのは、ブロックするexit 2のみです。 - 簡潔で読みやすくしてください。ユーザーは後から一致させるパスやコマンドを調整したくなるでしょう。
- そのイベントにすでにフックがある場合は、上書きせずに拡張してください。ユーザーの既存チェックを残したまま、新しいチェックを追加します。
Stop/SubagentStopのみ: 最初にstop_hook_activeを確認し、true の場合はsys.exit(0)を実行してください。このガードがないと、フックが停止をブロックし、エージェントが作業して再び停止しようとし、またブロックされるというループが発生します。
⚠️ フックからプロジェクトのコマンドを実行する場合(作成前にお読みください)
フック自体は、
uv runによる分離された一時的な環境で実行されます。そのインタープリターはプロジェクトのインタープリターではなく、プロジェクトの依存関係も含まれていません。そのため、次の点に注意してください。
- 絶対に
sys.executableや、["python", "-m", "pytest", ...]のような裸のリストを使ってコマンドを組み立て直さないでください。実行されるのはフック用の使い捨て Python で、pytest もプロジェクトのパッケージも何もありません。コマンドは毎回No module named …で失敗します。そのため、「テストが通るまで終了しない」フックは、失敗時だけでなく成功時にも処理をブロックし、見当違いの理由を報告します。動いているように見えますが、実際には動いていません。- ユーザーのコマンドをそのままシェル文字列として、プロジェクトディレクトリで実行してください。その際、uv の一時的な venv を取り除き、プロジェクト独自の venv を先頭に配置してください。 両方の手順が必要です。
shell=Trueにするだけでは不十分です。uv runは使い捨てのインタープリターをPATHの先頭に配置し、VIRTUAL_ENVも設定します。しかし、uv の venv を取り除いただけでは、プロジェクトの venv は有効になりません。フックはユーザーのシェルではないため、そもそも.venvはPATHにありません。そのままではpythonはマシン上の任意のグローバルインタープリターを使います。グローバル環境のパッケージ構成は異なり(多くの場合、壊れています)、テストがすべて成功していてもフックは終了コード 2 で終了し、無関係なモジュールのせいにします。次のヘルパーをそのままコピーしてください。コマンドはファイルの先頭に名前付き定数として1行で記述し、ユーザーがどこを編集すればよいか一目で分かるようにしてください。TEST_COMMAND = "python -m pytest -q" # ユーザーが入力したコマンドをそのまま記述。編集するのはこの1行だけ def _project_env(project_root: Path) -> dict: """uv の一時的な venv を取り除き、プロジェクトの venv を先頭に配置した os.environ。""" env = os.environ.copy() ephemeral = env.pop("VIRTUAL_ENV", None) parts = env.get("PATH", "").split(os.pathsep) if ephemeral: # 1. uv の使い捨て venv を取り除く drop = {os.path.join(ephemeral, "Scripts"), os.path.join(ephemeral, "bin")} parts = [p for p in parts if p not in drop] for candidate in (".venv", "venv"): # 2. プロジェクト独自の venv を先頭に配置 for bindir in ("Scripts", "bin"): venv_bin = project_root / candidate / bindir if venv_bin.is_dir(): env["VIRTUAL_ENV"] = str(project_root / candidate) parts.insert(0, str(venv_bin)) env["PATH"] = os.pathsep.join(parts) return env env["PATH"] = os.pathsep.join(parts) return env root = Path(hook_input["cwd"]) # Claude Code が渡すプロジェクトルート result = subprocess.run( TEST_COMMAND, shell=True, capture_output=True, text=True, cwd=str(root), env=_project_env(root), )- コマンドをサブディレクトリから実行する必要がある場合(モノレポ、またはテスト設定がより深い場所にあるプロジェクトなど。例:
app/backend/)は、その場所を確認して指定してください。cwd=Path(hook_input["cwd"]) / "app/backend"のようにします。ここを間違えるとフックは常に処理をブロックし、ユーザーには「フックが壊れている」と受け取られます。
5. settings.json に組み込む
.claude/settings.json を編集します(存在しない場合は作成します)。既存の hooks ブロックにはマージし、ほかのイベントや同じイベントに登録されているほかのフックを上書きしないでください。形式:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit|Write|Read|Bash",
"hooks": [ { "type": "command", "command": "uv run .claude/hooks/pre_tool_use.py" } ] }
]
}
}
6. 自分で実証してから、説明と注意喚起を行います
引き渡す前にフックを実行してください。 サンプルイベントを標準入力から渡して終了コードを確認します。読んだだけのフックを出荷してはいけません。常にブロックするフックも、決してブロックしないフックも、誤ったタイミングで発動するまでは正常なフックと見分けがつきません。
# should ALLOW (exit 0)
echo '{"session_id":"t","cwd":"<project-root>","hook_event_name":"Stop","stop_hook_active":false}' | uv run .claude/hooks/stop.py; echo "exit=$?"
- コマンド実行フック(テスト/lint/型チェック)の場合、これは必須です。両方の状況を確認してください。コマンドが成功したら終了コードが 0 になり、実際に失敗したら終了コードが 2 になることを確認します。どちらの状況でも終了コードが 2 になる場合、コマンドが解決されていません。手順 4 の
sys.executableに関する警告をもう一度読んでください。 - ブロッキングガードの場合、ブロックされるべきペイロードと、通過するべきペイロードを 1 つずつ渡します。
- チェック結果が正しくなければ、スクリプトを修正して、成功を報告する前に再実行してください。
続けて、次のことを行います。
- 何を作成したのかを、わかりやすい言葉でユーザーに伝えます。どのイベントに対応するか、何を保証するか、調整するにはどの 1 行を変更すればよいかを説明します。
- エージェント内で動作を実証する方法を伝えます。ブロッキングフックの場合は、ブロックされるはずの操作(「env ファイルを読んで」と頼み、拒否されることを確認するなど)を示します。監視フックの場合は、出力先(ログや通知)を説明します。
- 検証した内容を報告し、検証できなかったことがあれば、明確に伝えます。
- セキュリティに関する注意(必ず伝えてください): フックは、該当するイベントが発生するたびに、あなたの認証情報を使って任意のコードを自動実行します。CI 設定と同じようにフックを確認し、信頼できるフックだけを実行してください。(MCP サーバーと同様の注意が必要です。)
品質チェック
- ✅ 動作が適切なイベントに対応しており、ブロックを目的とする場合は、PostToolUse ではなく、ブロック可能なイベント(PreToolUse/Stop/UserPromptSubmit)を使用していること。
- ✅ マッチャーが、ユーザーの意図した範囲に限定されていること(意図せずすべてのイベントで発火しないこと)。
- ✅ スクリプトがフェイルオープンであること。エラー時はすべて終了コード 0 で終了し、終了コード 2 になるのは意図したブロックの場合のみで、その理由が stderr に明確に出力されること。
- ✅ プロジェクトのコマンドは、プロジェクトの
cwdでshell=Trueを介してそのまま実行し、uv の一時的な venv を取り除いたうえで、プロジェクト独自の venv をPATHの先頭に置くこと。sys.executableを使って再構築してはいけません(フック自身のインタープリターにはプロジェクトの依存関係がなく、グローバルのインタープリターには適切な依存関係がありません)。 - ✅
Stop/SubagentStopフックがstop_hook_activeを考慮し、ループしないこと。 - ✅
settings.jsonを上書きせず、既存のフックを残して統合していること。 - ✅ フックを実行し、許可すべき場合は終了コード 0、ブロックすべき場合は終了コード 2 になることを確認していること。読むだけでは不十分です。
- ✅ ユーザーにわかりやすい説明、テスト方法、セキュリティに関する注意を伝えていること。
注意事項
- フックは AI レイヤーの決定論的な基盤です。機密情報、保護対象のパス、「すべて成功するまで完了しない」といった譲れない要件に使い、ルールやスキルで十分に扱えることには使わないでください。
- ブロッキングフックの対象範囲はマッチャーの性能に左右されます。フックが実行されることは保証しますが、何を検出するかはユーザーが決めます。たとえば
.envに対するマッチャーでは、Base64 エンコードされた読み取りは検出できないなど、対象範囲の限界について正直に伝えてください。 - フックは高速に保ってください。該当イベントが発生するたびに実行されます。負荷の高い処理は、非同期フックまたはスキルで行ってください。