Claude Code フックは、Claude Codeのライフサイクルの様々な時点で実行される、ユーザー定義のシェルコマンドです。フックは、Claude Codeの動作に対する決定論的な制御を提供し、LLMが実行を選択することに依存するのではなく、特定のアクションが常に実行されることを保証します。

フックのリファレンスドキュメントについては、フックリファレンスを参照してください。

フックの使用例には以下があります:

  • 通知: Claude Codeがあなたの入力や何かを実行する許可を待っているときの通知方法をカスタマイズします。
  • 自動フォーマット: ファイル編集後に.tsファイルに対してprettierを、.goファイルに対してgofmtを実行します。
  • ログ記録: コンプライアンスやデバッグのために、実行されたすべてのコマンドを追跡・カウントします。
  • フィードバック: Claude Codeがあなたのコードベース規約に従わないコードを生成した場合に、自動化されたフィードバックを提供します。
  • カスタム権限: 本番ファイルや機密ディレクトリへの変更をブロックします。

これらのルールをプロンプト指示ではなくフックとしてエンコードすることで、提案を実行が期待されるたびに実行されるアプリレベルのコードに変換します。

フックはエージェントループ中に現在の環境の認証情報で自動的に実行されるため、フックを追加する際はセキュリティへの影響を考慮する必要があります。 例えば、悪意のあるフックコードはあなたのデータを流出させる可能性があります。フックを登録する前に、常にフックの実装を確認してください。

完全なセキュリティベストプラクティスについては、フックリファレンスドキュメントのセキュリティ考慮事項を参照してください。

フックイベント概要

Claude Codeは、ワークフローの異なる時点で実行される複数のフックイベントを提供します:

  • PreToolUse: ツール呼び出しの前に実行(ブロック可能)
  • PostToolUse: ツール呼び出し完了後に実行
  • UserPromptSubmit: ユーザーがプロンプトを送信したときに、Claudeが処理する前に実行
  • Notification: Claude Codeが通知を送信するときに実行
  • Stop: Claude Codeが応答を終了するときに実行
  • SubagentStop: サブエージェントタスクが完了するときに実行
  • PreCompact: Claude Codeがコンパクト操作を実行しようとする前に実行
  • SessionStart: Claude Codeが新しいセッションを開始するか、既存のセッションを再開するときに実行
  • SessionEnd: Claude Codeセッションが終了するときに実行

各イベントは異なるデータを受信し、Claudeの動作を異なる方法で制御できます。

クイックスタート

このクイックスタートでは、Claude Codeが実行するシェルコマンドをログに記録するフックを追加します。

前提条件

コマンドラインでのJSON処理のためにjqをインストールしてください。

ステップ1: フック設定を開く

/hooks スラッシュコマンドを実行し、PreToolUseフックイベントを選択します。

PreToolUseフックはツール呼び出しの前に実行され、Claudeに何を異なって行うべきかのフィードバックを提供しながらそれらをブロックできます。

ステップ2: マッチャーを追加

+ Add new matcher…を選択して、Bashツール呼び出しでのみフックを実行するようにします。

マッチャーにBashと入力します。

すべてのツールにマッチさせるには*を使用できます。

ステップ3: フックを追加

+ Add new hook…を選択し、このコマンドを入力します:

jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt

ステップ4: 設定を保存

保存場所として、ホームディレクトリにログを記録するためUser settingsを選択します。このフックは現在のプロジェクトだけでなく、すべてのプロジェクトに適用されます。

その後、REPLに戻るまでEscを押します。フックが登録されました!

ステップ5: フックを確認

再度/hooksを実行するか、~/.claude/settings.jsonをチェックして設定を確認します:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
          }
        ]
      }
    ]
  }
}

ステップ6: フックをテスト

Claudeにlsのような簡単なコマンドを実行するよう依頼し、ログファイルを確認します:

cat ~/.claude/bash-command-log.txt

次のようなエントリが表示されるはずです:

ls - Lists files and directories

その他の例

完全な実装例については、公開コードベースのbash command validator exampleを参照してください。

コードフォーマットフック

編集後にTypeScriptファイルを自動的にフォーマット:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"
          }
        ]
      }
    ]
  }
}

Markdownフォーマットフック

markdownファイルの言語タグの欠落やフォーマット問題を自動的に修正:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/markdown_formatter.py"
          }
        ]
      }
    ]
  }
}

この内容で.claude/hooks/markdown_formatter.pyを作成:

#!/usr/bin/env python3
"""
Markdown formatter for Claude Code output.
Fixes missing language tags and spacing issues while preserving code content.
"""
import json
import sys
import re
import os

def detect_language(code):
    """Best-effort language detection from code content."""
    s = code.strip()
    
    # JSON detection
    if re.search(r'^\s*[{\[]', s):
        try:
            json.loads(s)
            return 'json'
        except:
            pass
    
    # Python detection
    if re.search(r'^\s*def\s+\w+\s*\(', s, re.M) or \
       re.search(r'^\s*(import|from)\s+\w+', s, re.M):
        return 'python'
    
    # JavaScript detection  
    if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=)', s) or \
       re.search(r'=>|console\.(log|error)', s):
        return 'javascript'
    
    # Bash detection
    if re.search(r'^#!.*\b(bash|sh)\b', s, re.M) or \
       re.search(r'\b(if|then|fi|for|in|do|done)\b', s):
        return 'bash'
    
    # SQL detection
    if re.search(r'\b(SELECT|INSERT|UPDATE|DELETE|CREATE)\s+', s, re.I):
        return 'sql'
        
    return 'text'

def format_markdown(content):
    """Format markdown content with language detection."""
    # Fix unlabeled code fences
    def add_lang_to_fence(match):
        indent, info, body, closing = match.groups()
        if not info.strip():
            lang = detect_language(body)
            return f"{indent}```{lang}\n{body}{closing}\n"
        return match.group(0)
    
    fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'
    content = re.sub(fence_pattern, add_lang_to_fence, content)
    
    # Fix excessive blank lines (only outside code fences)
    content = re.sub(r'\n{3,}', '\n\n', content)
    
    return content.rstrip() + '\n'

# Main execution
try:
    input_data = json.load(sys.stdin)
    file_path = input_data.get('tool_input', {}).get('file_path', '')
    
    if not file_path.endswith(('.md', '.mdx')):
        sys.exit(0)  # Not a markdown file
    
    if os.path.exists(file_path):
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        formatted = format_markdown(content)
        
        if formatted != content:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(formatted)
            print(f"✓ Fixed markdown formatting in {file_path}")
    
except Exception as e:
    print(f"Error formatting markdown: {e}", file=sys.stderr)
    sys.exit(1)

スクリプトを実行可能にします:

chmod +x .claude/hooks/markdown_formatter.py

このフックは自動的に:

  • ラベルのないコードブロック内のプログラミング言語を検出
  • シンタックスハイライト用の適切な言語タグを追加
  • コードコンテンツを保持しながら過度の空白行を修正
  • markdownファイル(.md.mdx)のみを処理

カスタム通知フック

Claudeが入力を必要とするときにデスクトップ通知を取得:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Awaiting your input'"
          }
        ]
      }
    ]
  }
}

ファイル保護フック

機密ファイルへの編集をブロック:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 -c \"import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)\""
          }
        ]
      }
    ]
  }
}

さらに学ぶ

  • フックのリファレンスドキュメントについては、フックリファレンスを参照してください。
  • 包括的なセキュリティベストプラクティスと安全ガイドラインについては、フックリファレンスドキュメントのセキュリティ考慮事項を参照してください。
  • トラブルシューティング手順とデバッグ技術については、フックリファレンスドキュメントのデバッグを参照してください。