Free

Panduan Lengkap Claude Code Hooks: Kendalikan Setiap Tindakan Claude

Penjelasan lengkap mekanisme Claude Code Hooks: lima jenis event, kontrol exit code, dan empat konfigurasi nyata untuk membuat tindakan AI dapat diaudit, dicegat, dan diotomatisasi.


Setiap kali Claude Code membaca file, menulis kode, atau menjalankan perintah, ada sistem event yang bekerja di latar belakang. Hooks adalah antarmuka untuk terhubung ke sistem itu — kamu bisa menyuntikkan logika sendiri kapan saja: menjalankan linter secara otomatis, mencatat operasi, memblokir tindakan berbahaya, atau memicu perintah shell apa pun.

Artikel ini membahas mekanisme Hooks, konfigurasi, dan penggunaan praktisnya secara lengkap.


Apa itu Hooks

Hooks adalah perintah shell yang dikonfigurasi di settings.json dan dijalankan otomatis oleh Claude Code saat event tertentu terjadi.

Analogi paling tepat: git hooks. Git bisa memicu skrip sebelum dan sesudah operasi seperti commit dan push. Claude Code Hooks bekerja persis sama — perbedaannya adalah titik pemicunya adalah pemanggilan alat oleh AI.

Mengapa ini penting?

Semakin kuat Claude Code, semakin kamu butuh lapisan kontrol yang deterministik. Hooks memberikan:
- Jaminan eksekusi yang tidak bergantung pada prompt (Claude bisa mengabaikan instruksi, tapi hook selalu berjalan)
- Catatan operasi yang dapat diaudit
- Pemeriksaan kualitas otomatis


Lima Jenis Hook

Tipe Pemicu Kegunaan Umum
PreToolUse Sebelum pemanggilan alat Memblokir operasi berbahaya, mencatat niat
PostToolUse Sesudah pemanggilan alat Auto-lint, menjalankan tes
PreCompact Sebelum kompresi konteks Menyimpan snapshot status saat ini
Notification Saat Claude mengirim notifikasi Alert desktop, pesan Slack
Stop Saat Claude selesai merespons Merangkum log, memicu alur berikutnya

Yang paling sering digunakan adalah PreToolUse dan PostToolUse, untuk mencegat dan memproses pemanggilan alat.


Format Konfigurasi

Hooks ditulis di ~/.claude/settings.json (global) atau .claude/settings.json di root proyek (level proyek):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --silent"
          }
        ]
      }
    ]
  }
}

Tiga field utama:

  • matcher: Regex yang cocok dengan nama alat, menentukan pemanggilan alat mana yang memicu hook ini. "Write|Edit" aktif saat menulis atau mengedit file. Biarkan kosong atau gunakan ".*" untuk mencocokkan semua alat.
  • type: Saat ini hanya "command".
  • command: Perintah shell apa pun.

Referensi Nama Alat

Nama alat yang perlu kamu ketahui untuk menulis matcher:

Nama Alat Operasi
Write Menulis file baru
Edit Mengedit file
Bash Menjalankan perintah shell
Read Membaca file
Glob Pencarian file
Grep Pencarian konten
TodoWrite Memperbarui daftar tugas

Lingkungan Eksekusi Hook

Saat dijalankan, data dikirim sebagai JSON melalui stdin:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.rb",
    "content": "..."
  },
  "tool_response": "..."
}

PreToolUse menerima tool_input (argumen sebelum pemanggilan). PostToolUse juga menerima tool_response (nilai kembalian alat).

Contoh hook dengan logika kondisional:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')

# Jalankan rubocop hanya untuk file .rb
if [[ "$file" == *.rb ]]; then
  rubocop "$file" --autocorrect-all --no-color
fi

Arti Kode Keluar

Kode keluar hook mengontrol perilaku Claude Code selanjutnya:

Kode Keluar Arti
0 Berhasil, lanjutkan eksekusi
2 Blokir: batalkan pemanggilan alat saat ini, kirim output stderr ke Claude
Non-zero lainnya Catat error, tapi lanjutkan

Kode keluar 2 paling berguna — memungkinkan kamu menulis logika intersepsi di PreToolUse untuk mencegah Claude melakukan operasi dan menjelaskan alasannya.


Praktik Nyata: Empat Konfigurasi Hook

1. Auto-format setelah menulis file

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

2. Blokir penghapusan direktori tertentu

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"Menghapus direktori migrations atau seeds tidak diizinkan\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

3. Log audit operasi

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
          }
        ]
      }
    ]
  }
}

4. Notifikasi desktop saat selesai

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude telah selesai\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Global vs. Level Proyek

Hooks bisa ditempatkan di dua lokasi:

Global (~/.claude/settings.json): Aturan yang berlaku untuk semua proyek — log, notifikasi.

Level proyek (.claude/settings.json): Pemeriksaan khusus proyek. Hooks level proyek dan global digabung dan keduanya dijalankan.

Untuk proyek tim, commit settings.json level proyek ke git agar semua anggota menjalankan hooks yang sama.


Debugging Hook

Langkah troubleshooting ketika hook tidak berjalan:

  1. Periksa ejaan matcher: Nama alat case-sensitive — write bukan Write
  2. Jalankan perintah sendiri: Salin perintah hook dan jalankan langsung di terminal
  3. Periksa output Claude Code: Stderr hook muncul di percakapan
  4. Sederhanakan untuk testing: Mulai dengan echo "hook triggered" >> /tmp/hook.log untuk konfirmasi

Kesimpulan

Nilai inti Hooks adalah menghubungkan perilaku AI yang tidak dapat diprediksi dengan standar rekayasa yang deterministik. Claude mungkin lupa menjalankan tes, tapi PostToolUse hook tidak. Claude mungkin menghapus file secara tidak sengaja, tapi interceptor PreToolUse tidak akan membiarkannya.

Mulai dengan satu hook: auto-lint setelah menulis file. Setelah berjalan lancar, tambahkan yang lain.