フックシステムの例
**フック(Hook)**は、特定のイベントが発生したときに自動的に実行されるスクリプトです。
フックの種類
Section titled “フックの種類”| タイプ | 実行タイミング | 用途 |
|---|---|---|
SessionStart | セッション開始時 | 初期設定、スタイル変更 |
PreToolUse | ツール実行前 | バリデーション、警告 |
PostToolUse | ツール実行後 | (将来の機能) |
例1: セキュリティ警告フック(PreToolUse)
Section titled “例1: セキュリティ警告フック(PreToolUse)”ファイル構成
Section titled “ファイル構成”plugins/security-guidance/├── .claude-plugin/│ └── plugin.json├── hooks/│ ├── hooks.json│ └── security_reminder_hook.py└── README.mdhooks.json
Section titled “hooks.json”{ "description": "Security reminder hook that warns about potential security issues when editing files", "hooks": { "PreToolUse": [ { "hooks": [ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py" } ], "matcher": "Edit|Write|MultiEdit" } ] }}hooksフィールド
Section titled “hooksフィールド”"hooks": { "PreToolUse": [...]}PreToolUse: ツール実行前にフックを発動
matcherフィールド
Section titled “matcherフィールド”"matcher": "Edit|Write|MultiEdit"- パターン: 正規表現(パイプ
|で区切る) - マッチ:
Edit,Write,MultiEditツールのいずれか - 結果: これらのツールが使われる前にフックが実行される
commandフィールド
Section titled “commandフィールド”"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py"${CLAUDE_PLUGIN_ROOT}: プラグインのルートディレクトリ- 環境変数が自動的に展開される
security_reminder_hook.py
Section titled “security_reminder_hook.py”#!/usr/bin/env python3"""Security Reminder Hook for Claude CodeChecks for security patterns in file edits and warns about potential vulnerabilities."""
import jsonimport sys
# stdinからツールパラメータを読み込みinput_data = json.load(sys.stdin)file_path = input_data.get("file_path", "")
# セキュリティパターン定義SECURITY_PATTERNS = [ { "ruleName": "github_actions_workflow", "path_check": lambda path: ".github/workflows/" in path and (path.endswith(".yml") or path.endswith(".yaml")), "reminder": """⚠️ Warning: You are editing a GitHub Actions workflow file.
Be aware of these security risks:
1. **Command Injection**: Never use untrusted input directly in run: commands2. **Use environment variables**: Instead of ${{ github.event.issue.title }}, use env:3. **Review the guide**: https://github.blog/security/...
Example of UNSAFE pattern:run: echo "${{ github.event.issue.title }}"
Example of SAFE pattern:env: TITLE: ${{ github.event.issue.title }}run: echo "$TITLE"""" }, { "ruleName": "env_file", "path_check": lambda path: path.endswith(".env") or ".env." in path, "reminder": """⚠️ Warning: You are editing an environment file.
Security reminders:- Never commit .env files with secrets- Use .env.example for templates- Ensure .env is in .gitignore""" }]
# パターンマッチングfor pattern in SECURITY_PATTERNS: if pattern["path_check"](file_path): # stdoutに警告を出力(プロンプトに追加される) print(pattern["reminder"]) sys.exit(0)
# 問題なければ何も出力しないsys.exit(0)1. ユーザーがファイル編集を試みる Edit: .github/workflows/ci.yml ↓2. CLI が PreToolUse フックをチェック ↓3. matcher で Edit ツールがマッチ ↓4. Python スクリプトを実行 - stdin に {"file_path": ".github/workflows/ci.yml", ...} を渡す ↓5. スクリプトがファイルパスをチェック - ".github/workflows/" を検出 ↓6. stdout に警告メッセージを出力 ↓7. CLI が警告をプロンプトに追加 ↓8. Claude が警告を考慮してコードを書くユーザーの操作:
Edit file: .github/workflows/deploy.ymlフックの出力:
⚠️ Warning: You are editing a GitHub Actions workflow file.
Be aware of these security risks:...Claudeの応答:
了解しました。GitHub Actionsワークフローを編集します。セキュリティ警告を考慮して、環境変数を適切に使用します。例2: セッション開始時のスタイル変更(SessionStart)
Section titled “例2: セッション開始時のスタイル変更(SessionStart)”ファイル構成
Section titled “ファイル構成”plugins/learning-output-style/├── .claude-plugin/│ └── plugin.json├── hooks/│ ├── hooks.json│ └── learning-mode.md└── README.mdhooks.json
Section titled “hooks.json”{ "description": "Learning mode output style", "hooks": { "SessionStart": [ { "hooks": [ { "type": "command", "command": "cat ${CLAUDE_PLUGIN_ROOT}/hooks/learning-mode.md" } ] } ] }}SessionStart フック
Section titled “SessionStart フック”"hooks": { "SessionStart": [...]}- セッション開始時に1回だけ実行される
- 初期設定やスタイル変更に使用
シンプルなコマンド
Section titled “シンプルなコマンド”"command": "cat ${CLAUDE_PLUGIN_ROOT}/hooks/learning-mode.md"catコマンドでファイル内容を出力- 出力内容がシステムプロンプトに追加される
learning-mode.md
Section titled “learning-mode.md”# Learning Mode
When helping users write code, prioritize educational value:
1. **Explain your reasoning**: Share why you chose a particular approach2. **Highlight learning opportunities**: Point out interesting patterns or techniques3. **Ask questions**: Encourage critical thinking by asking the user to make meaningful decisions4. **Provide alternatives**: When appropriate, mention different approaches and their trade-offs
Remember: The goal is not just to complete the task, but to help the user learn and grow as a developer.1. ユーザーが claude コマンドを実行 ↓2. セッション開始 ↓3. CLI が SessionStart フックをチェック ↓4. cat コマンドで learning-mode.md を読み込み ↓5. 内容をシステムプロンプトに追加 ↓6. Claude が学習モードで応答を開始通常モード:
User: ソート機能を追加してClaude: はい、ソート機能を追加します。[コードを書く]学習モード(フック適用後):
User: ソート機能を追加してClaude: ソート機能を追加します。いくつかアプローチがあります:
1. Array.sort() を使う方法(シンプル)2. カスタムソート関数を実装する方法(柔軟性高い)
どちらが良いと思いますか?それとも私が推奨する方法を実装しましょうか?例3: カスタムフックを作る
Section titled “例3: カスタムフックを作る”目標: TypeScript ファイル編集前の Lint リマインダー
Section titled “目標: TypeScript ファイル編集前の Lint リマインダー”1. ディレクトリ作成
Section titled “1. ディレクトリ作成”mkdir -p my-lint-reminder/.claude-pluginmkdir -p my-lint-reminder/hooks2. hooks.json 作成
Section titled “2. hooks.json 作成”{ "description": "Lint reminder for TypeScript files", "hooks": { "PreToolUse": [ { "hooks": [ { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/lint_reminder.py" } ], "matcher": "Edit|Write" } ] }}3. lint_reminder.py 作成
Section titled “3. lint_reminder.py 作成”#!/usr/bin/env python3import jsonimport sys
# stdinからツールパラメータを読み込みinput_data = json.load(sys.stdin)file_path = input_data.get("file_path", "")
# TypeScriptファイルのみチェックif file_path.endswith(".ts") or file_path.endswith(".tsx"): print("💡 Lint Reminder:") print("- Remember to run `npm run lint` after editing") print("- Follow the project's TypeScript style guide") print("- Use strict type checking") sys.exit(0)
# その他のファイルは何もしないsys.exit(0)4. 実行権限を付与
Section titled “4. 実行権限を付与”chmod +x my-lint-reminder/hooks/lint_reminder.py5. plugin.json 作成
Section titled “5. plugin.json 作成”{ "name": "lint-reminder", "version": "1.0.0", "description": "Reminds to run lint after editing TypeScript files"}6. テスト
Section titled “6. テスト”claude# TypeScriptファイルを編集してみるフックスクリプトの入出力
Section titled “フックスクリプトの入出力”stdin(標準入力)
Section titled “stdin(標準入力)”フックスクリプトは、stdinからツールパラメータをJSON形式で受け取ります。
Editツールの場合:
{ "file_path": "/path/to/file.ts", "old_string": "const foo = 1;", "new_string": "const foo = 2;"}Writeツールの場合:
{ "file_path": "/path/to/newfile.ts", "content": "console.log('Hello');"}stdout(標準出力)
Section titled “stdout(標準出力)”スクリプトの標準出力は、プロンプトに追加されます。
print("⚠️ Warning: Security issue detected")print("Please review the following...")stderr(標準エラー出力)
Section titled “stderr(標準エラー出力)”標準エラー出力は、デバッグログとして使用できます(プロンプトには含まれません)。
import syssys.stderr.write("Debug: Checking file path\n")0: 正常終了非0: エラー(実行は中断されない)
デバッグ方法
Section titled “デバッグ方法”ログファイルを使う
Section titled “ログファイルを使う”#!/usr/bin/env python3import jsonimport sysfrom datetime import datetime
DEBUG_LOG_FILE = "/tmp/my-hook-debug.log"
def debug_log(message): timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(DEBUG_LOG_FILE, "a") as f: f.write(f"[{timestamp}] {message}\n")
# stdinの内容をログに記録input_data = json.load(sys.stdin)debug_log(f"Received input: {json.dumps(input_data)}")
file_path = input_data.get("file_path", "")debug_log(f"File path: {file_path}")
# パターンチェックif file_path.endswith(".ts"): debug_log("TypeScript file detected") print("💡 TypeScript file detected")
debug_log("Hook execution completed")tail -f /tmp/my-hook-debug.log# フックスクリプトを直接テストecho '{"file_path": "test.ts"}' | python3 lint_reminder.pyベストプラクティス
Section titled “ベストプラクティス”1. 軽量に保つ
Section titled “1. 軽量に保つ”❌ 悪い例: 重い処理を実行
# 全ファイルをスキャン(遅い)subprocess.run(["find", ".", "-name", "*.ts"])✅ 良い例: 必要最小限のチェック
# 対象ファイルだけチェック(速い)if file_path.endswith(".ts"): print("Reminder: Run lint")2. エラーハンドリング
Section titled “2. エラーハンドリング”❌ 悪い例: エラーでクラッシュ
file_path = input_data["file_path"] # KeyErrorの可能性✅ 良い例: 安全にデータを取得
file_path = input_data.get("file_path", "")if not file_path: sys.exit(0) # 静かに終了3. 情報的なメッセージ
Section titled “3. 情報的なメッセージ”❌ 悪い例: 曖昧なメッセージ
print("Warning!")✅ 良い例: 具体的な指示
print("⚠️ GitHub Actions workflow detected")print("Use environment variables to prevent command injection")print("See: https://...")4. 条件付き実行
Section titled “4. 条件付き実行”❌ 悪い例: すべてのファイルで警告
print("Remember to run tests!")✅ 良い例: 関連ファイルのみ
if file_path.endswith(".test.ts"): print("Remember to run: npm test")トラブルシューティング
Section titled “トラブルシューティング”問題1: フックが実行されない
Section titled “問題1: フックが実行されない”症状: 何も表示されない
原因:
- hooks.json の配置場所が間違っている
- matcher パターンが間違っている
- スクリプトに実行権限がない
解決:
# 配置確認ls -la plugins/my-plugin/hooks/
# 実行権限付与chmod +x plugins/my-plugin/hooks/script.py
# matcherを確認"matcher": "Edit|Write" # 正しい"matcher": "edit" # 間違い(大文字小文字を区別)問題2: スクリプトエラー
Section titled “問題2: スクリプトエラー”症状: フックが途中で止まる
原因: Python スクリプトでエラー
解決:
import sysimport traceback
try: # フックの処理 input_data = json.load(sys.stdin) # ...except Exception as e: # エラーをログに記録 sys.stderr.write(f"Hook error: {e}\n") sys.stderr.write(traceback.format_exc()) sys.exit(0) # エラーでもセッションを壊さない問題3: 出力が表示されない
Section titled “問題3: 出力が表示されない”症状: print() しても何も表示されない
原因: 出力バッファリング
解決:
# バッファリングを無効化print("Warning", flush=True)
# またはimport syssys.stdout.flush()次のステップ
Section titled “次のステップ”- マルチエージェントの例 - 複雑なワークフローを学ぶ
- 学習パスガイド - 実践的な学習方法