Pengantar

Hook Claude Code adalah perintah shell yang didefinisikan pengguna yang dieksekusi pada berbagai titik dalam siklus hidup Claude Code. Hook menyediakan kontrol deterministik atas perilaku Claude Code, memastikan tindakan tertentu selalu terjadi daripada mengandalkan LLM untuk memilih menjalankannya.

Contoh kasus penggunaan meliputi:

  • Notifikasi: Kustomisasi cara Anda mendapatkan notifikasi ketika Claude Code menunggu input atau izin Anda untuk menjalankan sesuatu.
  • Pemformatan otomatis: Jalankan prettier pada file .ts, gofmt pada file .go, dll. setelah setiap pengeditan file.
  • Logging: Lacak dan hitung semua perintah yang dieksekusi untuk kepatuhan atau debugging.
  • Umpan balik: Berikan umpan balik otomatis ketika Claude Code menghasilkan kode yang tidak mengikuti konvensi codebase Anda.
  • Izin kustom: Blokir modifikasi pada file produksi atau direktori sensitif.

Dengan mengkodekan aturan-aturan ini sebagai hook daripada instruksi prompting, Anda mengubah saran menjadi kode tingkat aplikasi yang dieksekusi setiap kali diharapkan untuk berjalan.

Hook mengeksekusi perintah shell dengan izin pengguna penuh Anda tanpa konfirmasi. Anda bertanggung jawab untuk memastikan hook Anda aman dan secure. Anthropic tidak bertanggung jawab atas kehilangan data atau kerusakan sistem yang diakibatkan oleh penggunaan hook. Tinjau Pertimbangan Keamanan.

Quickstart

Dalam quickstart ini, Anda akan menambahkan hook yang mencatat perintah shell yang dijalankan Claude Code.

Prasyarat Quickstart: Instal jq untuk pemrosesan JSON di command line.

Langkah 1: Buka konfigurasi hooks

Jalankan slash command /hooks dan pilih event hook PreToolUse.

Hook PreToolUse berjalan sebelum panggilan tool dan dapat memblokirnya sambil memberikan umpan balik Claude tentang apa yang harus dilakukan secara berbeda.

Langkah 2: Tambahkan matcher

Pilih + Add new matcher… untuk menjalankan hook Anda hanya pada panggilan tool Bash.

Ketik Bash untuk matcher.

Langkah 3: Tambahkan hook

Pilih + Add new hook… dan masukkan perintah ini:

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

Langkah 4: Simpan konfigurasi Anda

Untuk lokasi penyimpanan, pilih User settings karena Anda logging ke direktori home Anda. Hook ini kemudian akan berlaku untuk semua proyek, bukan hanya proyek Anda saat ini.

Kemudian tekan Esc sampai Anda kembali ke REPL. Hook Anda sekarang terdaftar!

Langkah 5: Verifikasi hook Anda

Jalankan /hooks lagi atau periksa ~/.claude/settings.json untuk melihat konfigurasi Anda:

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

Konfigurasi

Hook Claude Code dikonfigurasi dalam file pengaturan Anda:

  • ~/.claude/settings.json - Pengaturan pengguna
  • .claude/settings.json - Pengaturan proyek
  • .claude/settings.local.json - Pengaturan proyek lokal (tidak di-commit)
  • Pengaturan kebijakan yang dikelola enterprise

Struktur

Hook diorganisir berdasarkan matcher, di mana setiap matcher dapat memiliki beberapa hook:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: Pola untuk mencocokkan nama tool (hanya berlaku untuk PreToolUse dan PostToolUse)
    • String sederhana cocok persis: Write hanya cocok dengan tool Write
    • Mendukung regex: Edit|Write atau Notebook.*
    • Jika dihilangkan atau string kosong, hook berjalan untuk semua event yang cocok
  • hooks: Array perintah untuk dieksekusi ketika pola cocok
    • type: Saat ini hanya "command" yang didukung
    • command: Perintah bash untuk dieksekusi
    • timeout: (Opsional) Berapa lama perintah harus berjalan, dalam detik, sebelum membatalkan semua hook yang sedang berjalan.

Event Hook

PreToolUse

Berjalan setelah Claude membuat parameter tool dan sebelum memproses panggilan tool.

Matcher umum:

  • Task - Tugas agen
  • Bash - Perintah shell
  • Glob - Pencocokan pola file
  • Grep - Pencarian konten
  • Read - Pembacaan file
  • Edit, MultiEdit - Pengeditan file
  • Write - Penulisan file
  • WebFetch, WebSearch - Operasi web

PostToolUse

Berjalan segera setelah tool selesai dengan sukses.

Mengenali nilai matcher yang sama dengan PreToolUse.

Notification

Berjalan ketika Claude Code mengirim notifikasi.

Stop

Berjalan ketika agen utama Claude Code telah selesai merespons.

SubagentStop

Berjalan ketika subagen Claude Code (panggilan tool Task) telah selesai merespons.

Input Hook

Hook menerima data JSON melalui stdin yang berisi informasi sesi dan data spesifik event:

{
  // Field umum
  session_id: string
  transcript_path: string  // Path ke JSON percakapan

  // Field spesifik event
  ...
}

Input PreToolUse

Skema yang tepat untuk tool_input tergantung pada tool.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

Input PostToolUse

Skema yang tepat untuk tool_input dan tool_response tergantung pada tool.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  }
}

Input Notification

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "message": "Task completed successfully",
  "title": "Claude Code"
}

Input Stop dan SubagentStop

stop_hook_active adalah true ketika Claude Code sudah melanjutkan sebagai hasil dari stop hook. Periksa nilai ini atau proses transkrip untuk mencegah Claude Code berjalan tanpa batas.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "stop_hook_active": true
}

Output Hook

Ada dua cara untuk hook mengembalikan output kembali ke Claude Code. Output mengkomunikasikan apakah akan memblokir dan umpan balik apa pun yang harus ditunjukkan kepada Claude dan pengguna.

Sederhana: Exit Code

Hook mengkomunikasikan status melalui exit code, stdout, dan stderr:

  • Exit code 0: Sukses. stdout ditunjukkan kepada pengguna dalam mode transkrip (CTRL-R).
  • Exit code 2: Error pemblokiran. stderr diberikan kembali ke Claude untuk diproses secara otomatis. Lihat perilaku per-hook-event di bawah.
  • Exit code lainnya: Error non-pemblokiran. stderr ditunjukkan kepada pengguna dan eksekusi berlanjut.

Pengingat: Claude Code tidak melihat stdout jika exit code adalah 0.

Perilaku Exit Code 2

Event HookPerilaku
PreToolUseMemblokir panggilan tool, menunjukkan error ke Claude
PostToolUseMenunjukkan error ke Claude (tool sudah berjalan)
NotificationN/A, menunjukkan stderr hanya ke pengguna
StopMemblokir penghentian, menunjukkan error ke Claude
SubagentStopMemblokir penghentian, menunjukkan error ke subagen Claude

Lanjutan: Output JSON

Hook dapat mengembalikan JSON terstruktur dalam stdout untuk kontrol yang lebih canggih:

Field JSON Umum

Semua tipe hook dapat menyertakan field opsional ini:

{
  "continue": true, // Apakah Claude harus melanjutkan setelah eksekusi hook (default: true)
  "stopReason": "string" // Pesan yang ditunjukkan ketika continue adalah false
  "suppressOutput": true, // Sembunyikan stdout dari mode transkrip (default: false)
}

Jika continue adalah false, Claude berhenti memproses setelah hook berjalan.

  • Untuk PreToolUse, ini berbeda dari "decision": "block", yang hanya memblokir panggilan tool spesifik dan memberikan umpan balik otomatis ke Claude.
  • Untuk PostToolUse, ini berbeda dari "decision": "block", yang memberikan umpan balik otomatis ke Claude.
  • Untuk Stop dan SubagentStop, ini mengambil prioritas atas output "decision": "block" apa pun.
  • Dalam semua kasus, "continue" = false mengambil prioritas atas output "decision": "block" apa pun.

stopReason menyertai continue dengan alasan yang ditunjukkan kepada pengguna, tidak ditunjukkan kepada Claude.

Kontrol Keputusan PreToolUse

Hook PreToolUse dapat mengontrol apakah panggilan tool berlanjut.

  • “approve” melewati sistem izin. reason ditunjukkan kepada pengguna tetapi tidak kepada Claude.
  • “block” mencegah panggilan tool dari dieksekusi. reason ditunjukkan kepada Claude.
  • undefined mengarah ke alur izin yang ada. reason diabaikan.
{
  "decision": "approve" | "block" | undefined,
  "reason": "Penjelasan untuk keputusan"
}

Kontrol Keputusan PostToolUse

Hook PostToolUse dapat mengontrol apakah panggilan tool berlanjut.

  • “block” secara otomatis meminta Claude dengan reason.
  • undefined tidak melakukan apa-apa. reason diabaikan.
{
  "decision": "block" | undefined,
  "reason": "Penjelasan untuk keputusan"
}

Kontrol Keputusan Stop/SubagentStop

Hook Stop dan SubagentStop dapat mengontrol apakah Claude harus melanjutkan.

  • “block” mencegah Claude dari berhenti. Anda harus mengisi reason agar Claude tahu bagaimana melanjutkan.
  • undefined memungkinkan Claude untuk berhenti. reason diabaikan.
{
  "decision": "block" | undefined,
  "reason": "Harus disediakan ketika Claude diblokir dari berhenti"
}

Contoh Output JSON: Pengeditan Perintah Bash

#!/usr/bin/env python3
import json
import re
import sys

# Definisikan aturan validasi sebagai daftar tuple (pola regex, pesan)
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "Gunakan 'rg' (ripgrep) daripada 'grep' untuk performa dan fitur yang lebih baik",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "Gunakan 'rg --files | rg pattern' atau 'rg --files -g pattern' daripada 'find -name' untuk performa yang lebih baik",
    ),
]


def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues


try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Input JSON tidak valid: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# Validasi perintah
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # Exit code 2 memblokir panggilan tool dan menunjukkan stderr ke Claude
    sys.exit(2)

Bekerja dengan Tool MCP

Hook Claude Code bekerja dengan mulus dengan tool Model Context Protocol (MCP). Ketika server MCP menyediakan tool, mereka muncul dengan pola penamaan khusus yang dapat Anda cocokkan dalam hook Anda.

Penamaan Tool MCP

Tool MCP mengikuti pola mcp__<server>__<tool>, misalnya:

  • mcp__memory__create_entities - Tool create entities server Memory
  • mcp__filesystem__read_file - Tool read file server Filesystem
  • mcp__github__search_repositories - Tool search server GitHub

Mengkonfigurasi Hook untuk Tool MCP

Anda dapat menargetkan tool MCP spesifik atau seluruh server MCP:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Operasi memory dimulai' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Contoh

Pemformatan Kode

Secara otomatis memformat kode setelah modifikasi file:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/format-code.sh"
          }
        ]
      }
    ]
  }
}

Notifikasi

Kustomisasi notifikasi yang dikirim ketika Claude Code meminta izin atau ketika input prompt menjadi idle.

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/my_custom_notifier.py"
          }
        ]
      }
    ]
  }
}

Pertimbangan Keamanan

Disclaimer

GUNAKAN DENGAN RISIKO ANDA SENDIRI: Hook Claude Code mengeksekusi perintah shell arbitrer pada sistem Anda secara otomatis. Dengan menggunakan hook, Anda mengakui bahwa:

  • Anda sepenuhnya bertanggung jawab atas perintah yang Anda konfigurasi
  • Hook dapat memodifikasi, menghapus, atau mengakses file apa pun yang dapat diakses akun pengguna Anda
  • Hook yang berbahaya atau ditulis dengan buruk dapat menyebabkan kehilangan data atau kerusakan sistem
  • Anthropic tidak memberikan jaminan dan tidak bertanggung jawab atas kerusakan apa pun yang diakibatkan oleh penggunaan hook
  • Anda harus menguji hook secara menyeluruh dalam lingkungan yang aman sebelum penggunaan produksi

Selalu tinjau dan pahami perintah hook apa pun sebelum menambahkannya ke konfigurasi Anda.

Praktik Terbaik Keamanan

Berikut adalah beberapa praktik kunci untuk menulis hook yang lebih aman:

  1. Validasi dan sanitasi input - Jangan pernah mempercayai data input secara membabi buta
  2. Selalu kutip variabel shell - Gunakan "$VAR" bukan $VAR
  3. Blokir path traversal - Periksa .. dalam path file
  4. Gunakan path absolut - Tentukan path lengkap untuk script
  5. Lewati file sensitif - Hindari .env, .git/, kunci, dll.

Keamanan Konfigurasi

Edit langsung pada hook dalam file pengaturan tidak berlaku segera. Claude Code:

  1. Menangkap snapshot hook saat startup
  2. Menggunakan snapshot ini sepanjang sesi
  3. Memperingatkan jika hook dimodifikasi secara eksternal
  4. Memerlukan tinjauan dalam menu /hooks agar perubahan berlaku

Ini mencegah modifikasi hook berbahaya dari mempengaruhi sesi Anda saat ini.

Detail Eksekusi Hook

  • Timeout: Batas eksekusi 60 detik secara default, dapat dikonfigurasi per perintah.
    • Jika perintah individual mana pun timeout, semua hook yang sedang berjalan dibatalkan.
  • Paralelisasi: Semua hook yang cocok berjalan secara paralel
  • Environment: Berjalan dalam direktori saat ini dengan environment Claude Code
  • Input: JSON melalui stdin
  • Output:
    • PreToolUse/PostToolUse/Stop: Progress ditunjukkan dalam transkrip (Ctrl-R)
    • Notification: Dicatat ke debug saja (--debug)

Debugging

Untuk memecahkan masalah hook:

  1. Periksa apakah menu /hooks menampilkan konfigurasi Anda
  2. Verifikasi bahwa file pengaturan Anda adalah JSON yang valid
  3. Uji perintah secara manual
  4. Periksa exit code
  5. Tinjau ekspektasi format stdout dan stderr
  6. Pastikan escape quote yang tepat
  7. Gunakan claude --debug untuk debug hook Anda. Output dari hook yang sukses muncul seperti di bawah.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

Pesan progress muncul dalam mode transkrip (Ctrl-R) yang menunjukkan:

  • Hook mana yang berjalan
  • Perintah yang dieksekusi
  • Status sukses/gagal
  • Pesan output atau error