Skip to content

フックシステムの例

**フック(Hook)**は、特定のイベントが発生したときに自動的に実行されるスクリプトです。

タイプ実行タイミング用途
SessionStartセッション開始時初期設定、スタイル変更
PreToolUseツール実行前バリデーション、警告
PostToolUseツール実行後(将来の機能)

例1: セキュリティ警告フック(PreToolUse)

Section titled “例1: セキュリティ警告フック(PreToolUse)”
plugins/security-guidance/
├── .claude-plugin/
│ └── plugin.json
├── hooks/
│ ├── hooks.json
│ └── security_reminder_hook.py
└── README.md
{
"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": {
"PreToolUse": [...]
}
  • PreToolUse: ツール実行にフックを発動
"matcher": "Edit|Write|MultiEdit"
  • パターン: 正規表現(パイプ|で区切る)
  • マッチ: Edit, Write, MultiEdit ツールのいずれか
  • 結果: これらのツールが使われる前にフックが実行される
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py"
  • ${CLAUDE_PLUGIN_ROOT}: プラグインのルートディレクトリ
  • 環境変数が自動的に展開される
#!/usr/bin/env python3
"""
Security Reminder Hook for Claude Code
Checks for security patterns in file edits and warns about potential vulnerabilities.
"""
import json
import 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: commands
2. **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)”
plugins/learning-output-style/
├── .claude-plugin/
│ └── plugin.json
├── hooks/
│ ├── hooks.json
│ └── learning-mode.md
└── README.md
{
"description": "Learning mode output style",
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "cat ${CLAUDE_PLUGIN_ROOT}/hooks/learning-mode.md"
}
]
}
]
}
}
"hooks": {
"SessionStart": [...]
}
  • セッション開始時に1回だけ実行される
  • 初期設定やスタイル変更に使用
"command": "cat ${CLAUDE_PLUGIN_ROOT}/hooks/learning-mode.md"
  • catコマンドでファイル内容を出力
  • 出力内容がシステムプロンプトに追加される
# Learning Mode
When helping users write code, prioritize educational value:
1. **Explain your reasoning**: Share why you chose a particular approach
2. **Highlight learning opportunities**: Point out interesting patterns or techniques
3. **Ask questions**: Encourage critical thinking by asking the user to make meaningful decisions
4. **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. カスタムソート関数を実装する方法(柔軟性高い)
どちらが良いと思いますか?それとも私が推奨する方法を実装しましょうか?

目標: TypeScript ファイル編集前の Lint リマインダー

Section titled “目標: TypeScript ファイル編集前の Lint リマインダー”
Terminal window
mkdir -p my-lint-reminder/.claude-plugin
mkdir -p my-lint-reminder/hooks
{
"description": "Lint reminder for TypeScript files",
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/lint_reminder.py"
}
],
"matcher": "Edit|Write"
}
]
}
}
#!/usr/bin/env python3
import json
import 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)
Terminal window
chmod +x my-lint-reminder/hooks/lint_reminder.py
{
"name": "lint-reminder",
"version": "1.0.0",
"description": "Reminds to run lint after editing TypeScript files"
}
Terminal window
claude
# TypeScriptファイルを編集してみる

フックスクリプトは、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');"
}

スクリプトの標準出力は、プロンプトに追加されます。

print("⚠️ Warning: Security issue detected")
print("Please review the following...")

標準エラー出力は、デバッグログとして使用できます(プロンプトには含まれません)。

import sys
sys.stderr.write("Debug: Checking file path\n")
  • 0: 正常終了
  • 非0: エラー(実行は中断されない)

#!/usr/bin/env python3
import json
import sys
from 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")
Terminal window
tail -f /tmp/my-hook-debug.log
Terminal window
# フックスクリプトを直接テスト
echo '{"file_path": "test.ts"}' | python3 lint_reminder.py

悪い例: 重い処理を実行

# 全ファイルをスキャン(遅い)
subprocess.run(["find", ".", "-name", "*.ts"])

良い例: 必要最小限のチェック

# 対象ファイルだけチェック(速い)
if file_path.endswith(".ts"):
print("Reminder: Run lint")

悪い例: エラーでクラッシュ

file_path = input_data["file_path"] # KeyErrorの可能性

良い例: 安全にデータを取得

file_path = input_data.get("file_path", "")
if not file_path:
sys.exit(0) # 静かに終了

悪い例: 曖昧なメッセージ

print("Warning!")

良い例: 具体的な指示

print("⚠️ GitHub Actions workflow detected")
print("Use environment variables to prevent command injection")
print("See: https://...")

悪い例: すべてのファイルで警告

print("Remember to run tests!")

良い例: 関連ファイルのみ

if file_path.endswith(".test.ts"):
print("Remember to run: npm test")

症状: 何も表示されない

原因:

  1. hooks.json の配置場所が間違っている
  2. matcher パターンが間違っている
  3. スクリプトに実行権限がない

解決:

Terminal window
# 配置確認
ls -la plugins/my-plugin/hooks/
# 実行権限付与
chmod +x plugins/my-plugin/hooks/script.py
# matcherを確認
"matcher": "Edit|Write" # 正しい
"matcher": "edit" # 間違い(大文字小文字を区別)

症状: フックが途中で止まる

原因: Python スクリプトでエラー

解決:

import sys
import 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) # エラーでもセッションを壊さない

症状: print() しても何も表示されない

原因: 出力バッファリング

解決:

# バッファリングを無効化
print("Warning", flush=True)
# または
import sys
sys.stdout.flush()