Hooks adalah perintah shell yang ditentukan pengguna, endpoint HTTP, atau prompt LLM yang dijalankan secara otomatis pada titik-titik tertentu dalam siklus hidup Claude Code. Gunakan referensi ini untuk mencari skema event, opsi konfigurasi, format JSON input/output, dan fitur lanjutan seperti async hooks, HTTP hooks, dan MCP tool hooks. Jika Anda menyiapkan hooks untuk pertama kalinya, mulai dengan panduan sebagai gantinya.
Siklus hidup hook
Hooks dijalankan pada titik-titik tertentu selama sesi Claude Code. Ketika event dijalankan dan matcher cocok, Claude Code meneruskan konteks JSON tentang event ke handler hook Anda. Untuk command hooks, input tiba di stdin. Untuk HTTP hooks, input tiba sebagai badan permintaan POST. Handler Anda kemudian dapat memeriksa input, mengambil tindakan, dan secara opsional mengembalikan keputusan. Beberapa event dijalankan sekali per sesi, sementara yang lain dijalankan berulang kali di dalam loop agentic:
Tabel di bawah merangkum kapan setiap event dijalankan. Bagian Hook events mendokumentasikan skema input lengkap dan opsi kontrol keputusan untuk masing-masing.
| Event | When it fires |
|---|
SessionStart | When a session begins or resumes |
UserPromptSubmit | When you submit a prompt, before Claude processes it |
PreToolUse | Before a tool call executes. Can block it |
PermissionRequest | When a permission dialog appears |
PermissionDenied | When a tool call is denied by the auto mode classifier. Return {retry: true} to tell the model it may retry the denied tool call |
PostToolUse | After a tool call succeeds |
PostToolUseFailure | After a tool call fails |
Notification | When Claude Code sends a notification |
SubagentStart | When a subagent is spawned |
SubagentStop | When a subagent finishes |
TaskCreated | When a task is being created via TaskCreate |
TaskCompleted | When a task is being marked as completed |
Stop | When Claude finishes responding |
StopFailure | When the turn ends due to an API error. Output and exit code are ignored |
TeammateIdle | When an agent team teammate is about to go idle |
InstructionsLoaded | When a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session |
ConfigChange | When a configuration file changes during a session |
CwdChanged | When the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv |
FileChanged | When a watched file changes on disk. The matcher field specifies which filenames to watch |
WorktreeCreate | When a worktree is being created via --worktree or isolation: "worktree". Replaces default git behavior |
WorktreeRemove | When a worktree is being removed, either at session exit or when a subagent finishes |
PreCompact | Before context compaction |
PostCompact | After context compaction completes |
Elicitation | When an MCP server requests user input during a tool call |
ElicitationResult | After a user responds to an MCP elicitation, before the response is sent back to the server |
SessionEnd | When a session terminates |
Bagaimana hook diselesaikan
Untuk melihat bagaimana potongan-potongan ini cocok bersama, pertimbangkan hook PreToolUse ini yang memblokir perintah shell yang merusak. matcher mempersempit ke pemanggilan tool Bash dan kondisi if mempersempit lebih lanjut ke perintah yang dimulai dengan rm, jadi block-rm.sh hanya spawn ketika kedua filter cocok:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh"
}
]
}
]
}
}
Skrip membaca input JSON dari stdin, mengekstrak perintah, dan mengembalikan permissionDecision dari "deny" jika berisi rm -rf:
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0 # allow the command
fi
Sekarang anggaplah Claude Code memutuskan untuk menjalankan Bash "rm -rf /tmp/build". Inilah yang terjadi:
Event dijalankan
Event PreToolUse dijalankan. Claude Code mengirimkan input tool sebagai JSON di stdin ke hook:{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
Matcher memeriksa
Matcher "Bash" cocok dengan nama tool, jadi grup hook ini diaktifkan. Jika Anda menghilangkan matcher atau menggunakan "*", grup diaktifkan pada setiap kemunculan event.
Kondisi if memeriksa
Kondisi if "Bash(rm *)" cocok karena perintah dimulai dengan rm, jadi handler ini spawn. Jika perintah telah npm test, pemeriksaan if akan gagal dan block-rm.sh tidak akan pernah dijalankan, menghindari overhead spawn proses. Bidang if bersifat opsional; tanpanya, setiap handler dalam grup yang cocok dijalankan.
Handler hook dijalankan
Skrip memeriksa perintah lengkap dan menemukan rm -rf, jadi itu mencetak keputusan ke stdout:{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}
Jika perintah telah menjadi varian rm yang lebih aman seperti rm file.txt, skrip akan mencapai exit 0 sebagai gantinya, yang memberitahu Claude Code untuk mengizinkan pemanggilan tool tanpa tindakan lebih lanjut. Claude Code bertindak atas hasil
Claude Code membaca keputusan JSON, memblokir pemanggilan tool, dan menunjukkan alasannya kepada Claude.
Bagian Configuration di bawah mendokumentasikan skema lengkap, dan setiap bagian hook event mendokumentasikan input apa yang diterima perintah Anda dan output apa yang dapat dikembalikan.
Konfigurasi
Hooks didefinisikan dalam file pengaturan JSON. Konfigurasi memiliki tiga tingkat nesting:
- Pilih hook event untuk merespons, seperti
PreToolUse atau Stop
- Tambahkan matcher group untuk memfilter kapan dijalankan, seperti “hanya untuk tool Bash”
- Tentukan satu atau lebih hook handlers untuk dijalankan saat cocok
Lihat Bagaimana hook diselesaikan di atas untuk panduan lengkap dengan contoh beranotasi.
Halaman ini menggunakan istilah spesifik untuk setiap tingkat: hook event untuk titik siklus hidup, matcher group untuk filter, dan hook handler untuk perintah shell, endpoint HTTP, prompt, atau agent yang dijalankan. “Hook” sendiri merujuk pada fitur umum.
Lokasi hook
Tempat Anda mendefinisikan hook menentukan cakupannya:
| Lokasi | Cakupan | Dapat Dibagikan |
|---|
~/.claude/settings.json | Semua proyek Anda | Tidak, lokal ke mesin Anda |
.claude/settings.json | Proyek tunggal | Ya, dapat dikomit ke repo |
.claude/settings.local.json | Proyek tunggal | Tidak, gitignored |
| Pengaturan kebijakan terkelola | Seluruh organisasi | Ya, dikendalikan admin |
Plugin hooks/hooks.json | Ketika plugin diaktifkan | Ya, dibundel dengan plugin |
| Skill atau agent frontmatter | Saat komponen aktif | Ya, didefinisikan dalam file komponen |
Untuk detail tentang resolusi file pengaturan, lihat settings. Administrator enterprise dapat menggunakan allowManagedHooksOnly untuk memblokir hooks pengguna, proyek, dan plugin. Lihat Hook configuration.
Pola matcher
Bidang matcher adalah string regex yang memfilter kapan hooks dijalankan. Gunakan "*", "", atau hilangkan matcher sepenuhnya untuk mencocokkan semua kemunculan. Setiap tipe event mencocokkan pada bidang yang berbeda:
| Event | Apa yang difilter matcher | Contoh nilai matcher |
|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest | nama tool | Bash, Edit|Write, mcp__.* |
SessionStart | bagaimana sesi dimulai | startup, resume, clear, compact |
SessionEnd | mengapa sesi berakhir | clear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other |
Notification | tipe notifikasi | permission_prompt, idle_prompt, auth_success, elicitation_dialog |
SubagentStart | tipe agent | Bash, Explore, Plan, atau nama agent kustom |
PreCompact, PostCompact | apa yang memicu compaction | manual, auto |
SubagentStop | tipe agent | nilai yang sama seperti SubagentStart |
ConfigChange | sumber konfigurasi | user_settings, project_settings, local_settings, policy_settings, skills |
CwdChanged | tidak ada dukungan matcher | selalu dijalankan pada setiap perubahan direktori |
FileChanged | nama file (basename dari file yang berubah) | .envrc, .env, nama file apa pun yang ingin Anda pantau |
StopFailure | tipe kesalahan | rate_limit, authentication_failed, billing_error, invalid_request, server_error, max_output_tokens, unknown |
InstructionsLoaded | alasan load | session_start, nested_traversal, path_glob_match, include, compact |
Elicitation | nama server MCP | nama server MCP yang dikonfigurasi Anda |
ElicitationResult | nama server MCP | nilai yang sama seperti Elicitation |
UserPromptSubmit, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove | tidak ada dukungan matcher | selalu dijalankan pada setiap kemunculan |
Matcher adalah regex, jadi Edit|Write mencocokkan salah satu tool dan Notebook.* mencocokkan tool apa pun yang dimulai dengan Notebook. Matcher dijalankan terhadap bidang dari JSON input yang Claude Code kirimkan ke hook Anda di stdin. Untuk tool events, bidang itu adalah tool_name. Setiap bagian hook event mencantumkan set lengkap nilai matcher dan skema input untuk event itu.
Contoh ini menjalankan skrip linting hanya ketika Claude menulis atau mengedit file:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}
UserPromptSubmit, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, dan CwdChanged tidak mendukung matchers dan selalu dijalankan pada setiap kemunculan. Jika Anda menambahkan bidang matcher ke event ini, itu akan diabaikan secara diam-diam.
Untuk tool events, Anda dapat memfilter lebih sempit dengan menetapkan bidang if pada handler hook individual. if menggunakan sintaks aturan izin untuk mencocokkan terhadap nama tool dan argumen bersama-sama, jadi "Bash(git *)" dijalankan hanya untuk perintah git dan "Edit(*.ts)" dijalankan hanya untuk file TypeScript.
Tool server MCP muncul sebagai tool reguler dalam tool events (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest), jadi Anda dapat mencocokkannya dengan cara yang sama seperti Anda mencocokkan nama tool lainnya.
MCP tools mengikuti pola penamaan mcp__<server>__<tool>, misalnya:
mcp__memory__create_entities: tool create entities dari Memory server
mcp__filesystem__read_file: tool read file dari Filesystem server
mcp__github__search_repositories: tool search dari GitHub server
Gunakan pola regex untuk menargetkan MCP tools tertentu atau grup tools:
mcp__memory__.* mencocokkan semua tools dari server memory
mcp__.*__write.* mencocokkan tool apa pun yang berisi “write” dari server apa pun
Contoh ini mencatat semua operasi memory server dan memvalidasi operasi write dari server MCP apa pun:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/validate-mcp-write.py"
}
]
}
]
}
}
Bidang hook handler
Setiap objek dalam array hooks inner adalah hook handler: perintah shell, endpoint HTTP, prompt LLM, atau agent yang dijalankan saat matcher cocok. Ada empat tipe:
- Command hooks (
type: "command"): jalankan perintah shell. Skrip Anda menerima JSON input event di stdin dan mengkomunikasikan hasil kembali melalui kode keluar dan stdout.
- HTTP hooks (
type: "http"): kirimkan JSON input event sebagai permintaan HTTP POST ke URL. Endpoint mengkomunikasikan hasil kembali melalui badan respons menggunakan format JSON output yang sama seperti command hooks.
- Prompt hooks (
type: "prompt"): kirimkan prompt ke model Claude untuk evaluasi single-turn. Model mengembalikan keputusan yes/no sebagai JSON. Lihat Prompt-based hooks.
- Agent hooks (
type: "agent"): spawn subagent yang dapat menggunakan tools seperti Read, Grep, dan Glob untuk memverifikasi kondisi sebelum mengembalikan keputusan. Lihat Agent-based hooks.
Bidang umum
Bidang-bidang ini berlaku untuk semua tipe hook:
| Bidang | Diperlukan | Deskripsi |
|---|
type | ya | "command", "http", "prompt", atau "agent" |
if | tidak | Sintaks aturan izin untuk memfilter kapan hook ini dijalankan, seperti "Bash(git *)" atau "Edit(*.ts)". Hook hanya spawn jika pemanggilan tool cocok dengan pola. Hanya dievaluasi pada tool events: PreToolUse, PostToolUse, PostToolUseFailure, dan PermissionRequest. Pada event lain, hook dengan if yang ditetapkan tidak akan pernah dijalankan. Menggunakan sintaks yang sama seperti aturan izin |
timeout | tidak | Detik sebelum membatalkan. Default: 600 untuk command, 30 untuk prompt, 60 untuk agent |
statusMessage | tidak | Pesan spinner kustom ditampilkan saat hook dijalankan |
once | tidak | Jika true, dijalankan hanya sekali per sesi kemudian dihapus. Hanya skills, bukan agents. Lihat Hooks in skills and agents |
Bidang command hook
Selain bidang umum, command hooks menerima bidang-bidang ini:
| Bidang | Diperlukan | Deskripsi |
|---|
command | ya | Perintah shell untuk dijalankan |
async | tidak | Jika true, dijalankan di latar belakang tanpa memblokir. Lihat Run hooks in the background |
shell | tidak | Shell untuk digunakan untuk hook ini. Menerima "bash" (default) atau "powershell". Menetapkan "powershell" menjalankan perintah melalui PowerShell di Windows. Tidak memerlukan CLAUDE_CODE_USE_POWERSHELL_TOOL karena hooks spawn PowerShell secara langsung |
Bidang HTTP hook
Selain bidang umum, HTTP hooks menerima bidang-bidang ini:
| Bidang | Diperlukan | Deskripsi |
|---|
url | ya | URL untuk mengirimkan permintaan POST ke |
headers | tidak | Header HTTP tambahan sebagai pasangan kunci-nilai. Nilai mendukung interpolasi variabel lingkungan menggunakan sintaks $VAR_NAME atau ${VAR_NAME}. Hanya variabel yang tercantum dalam allowedEnvVars yang diselesaikan |
allowedEnvVars | tidak | Daftar nama variabel lingkungan yang dapat diinterpolasi ke nilai header. Referensi ke variabel yang tidak tercantum diganti dengan string kosong. Diperlukan untuk interpolasi variabel env apa pun untuk bekerja |
Claude Code mengirimkan JSON input hook sebagai badan permintaan POST dengan Content-Type: application/json. Badan respons menggunakan format JSON output yang sama seperti command hooks.
Penanganan kesalahan berbeda dari command hooks: respons non-2xx, kegagalan koneksi, dan timeout semuanya menghasilkan kesalahan non-blocking yang memungkinkan eksekusi berlanjut. Untuk memblokir pemanggilan tool atau menolak izin, kembalikan respons 2xx dengan badan JSON yang berisi decision: "block" atau hookSpecificOutput dengan permissionDecision: "deny".
Contoh ini mengirimkan event PreToolUse ke layanan validasi lokal, mengautentikasi dengan token dari variabel lingkungan MY_TOKEN:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
Bidang prompt dan agent hook
Selain bidang umum, prompt dan agent hooks menerima bidang-bidang ini:
| Bidang | Diperlukan | Deskripsi |
|---|
prompt | ya | Teks prompt untuk dikirim ke model. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook |
model | tidak | Model untuk digunakan untuk evaluasi. Default ke model cepat |
Semua matching hooks dijalankan secara paralel, dan handler identik dideduplikasi secara otomatis. Command hooks dideduplikasi berdasarkan string perintah, dan HTTP hooks dideduplikasi berdasarkan URL. Handlers dijalankan di direktori saat ini dengan lingkungan Claude Code. Variabel lingkungan $CLAUDE_CODE_REMOTE diatur ke "true" di lingkungan web jarak jauh dan tidak diatur di CLI lokal.
Referensi skrip berdasarkan path
Gunakan variabel lingkungan untuk mereferensikan skrip hook relatif terhadap akar proyek atau plugin, terlepas dari direktori kerja saat hook dijalankan:
$CLAUDE_PROJECT_DIR: akar proyek. Bungkus dalam tanda kutip untuk menangani path dengan spasi.
${CLAUDE_PLUGIN_ROOT}: direktori instalasi plugin, untuk skrip yang dibundel dengan plugin. Berubah pada setiap pembaruan plugin.
${CLAUDE_PLUGIN_DATA}: direktori data persisten plugin, untuk dependensi dan status yang harus bertahan pembaruan plugin.
Skrip proyek
Skrip plugin
Contoh ini menggunakan $CLAUDE_PROJECT_DIR untuk menjalankan pemeriksa gaya dari direktori .claude/hooks/ proyek setelah pemanggilan tool Write atau Edit apa pun:{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
}
]
}
]
}
}
Tentukan plugin hooks dalam hooks/hooks.json dengan bidang description tingkat atas opsional. Ketika plugin diaktifkan, hooks-nya bergabung dengan hooks pengguna dan proyek Anda.Contoh ini menjalankan skrip pemformatan yang dibundel dengan plugin:{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
}
Lihat plugin components reference untuk detail tentang membuat plugin hooks.
Hooks dalam skills dan agents
Selain file pengaturan dan plugin, hooks dapat didefinisikan langsung dalam skills dan subagents menggunakan frontmatter. Hooks ini dibatasi pada siklus hidup komponen dan hanya dijalankan ketika komponen itu aktif.
Semua hook events didukung. Untuk subagents, Stop hooks secara otomatis dikonversi ke SubagentStop karena itu adalah event yang dijalankan ketika subagent selesai.
Hooks menggunakan format konfigurasi yang sama seperti hooks berbasis pengaturan tetapi dibatasi pada masa hidup komponen dan dibersihkan saat selesai.
Skill ini mendefinisikan hook PreToolUse yang menjalankan skrip validasi keamanan sebelum setiap perintah Bash:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
---
Agents menggunakan format yang sama dalam frontmatter YAML mereka.
Ketik /hooks di Claude Code untuk membuka manajer hooks read-only. Menu menampilkan setiap hook event dengan jumlah hooks yang dikonfigurasi, memungkinkan Anda menggali ke dalam matchers, dan menampilkan detail lengkap setiap hook handler. Gunakan untuk memverifikasi konfigurasi, memeriksa file pengaturan mana hook berasal, atau memeriksa perintah, prompt, atau URL hook.
Menu menampilkan semua empat tipe hook: command, prompt, agent, dan http. Setiap hook diberi label dengan awalan [type] dan sumber menunjukkan di mana itu didefinisikan:
User: dari ~/.claude/settings.json
Project: dari .claude/settings.json
Local: dari .claude/settings.local.json
Plugin: dari hooks/hooks.json plugin
Session: terdaftar dalam memori untuk sesi saat ini
Built-in: terdaftar secara internal oleh Claude Code
Memilih hook membuka tampilan detail menampilkan event, matcher, tipe, file sumber, dan perintah lengkap, prompt, atau URL. Menu adalah read-only: untuk menambah, memodifikasi, atau menghapus hooks, edit JSON pengaturan secara langsung atau minta Claude membuat perubahan.
Nonaktifkan atau hapus hooks
Untuk menghapus hook, hapus entrinya dari file JSON pengaturan.
Untuk menonaktifkan semua hooks sementara tanpa menghapusnya, atur "disableAllHooks": true dalam file pengaturan Anda. Tidak ada cara untuk menonaktifkan hook individual sambil menyimpannya dalam konfigurasi.
Pengaturan disableAllHooks menghormati hierarki pengaturan terkelola. Jika administrator telah mengonfigurasi hooks melalui pengaturan kebijakan terkelola, disableAllHooks yang diatur dalam pengaturan pengguna, proyek, atau lokal tidak dapat menonaktifkan hooks terkelola tersebut. Hanya disableAllHooks yang diatur pada tingkat pengaturan terkelola yang dapat menonaktifkan hooks terkelola.
Pengeditan langsung ke hooks dalam file pengaturan biasanya diambil secara otomatis oleh file watcher.
Command hooks menerima data JSON melalui stdin dan mengkomunikasikan hasil melalui kode keluar, stdout, dan stderr. HTTP hooks menerima JSON yang sama sebagai badan permintaan POST dan mengkomunikasikan hasil melalui badan respons HTTP. Bagian ini mencakup bidang dan perilaku yang umum untuk semua events. Setiap bagian event di bawah Hook events mencakup skema input spesifiknya dan opsi kontrol keputusan.
Semua hook events menerima bidang-bidang ini sebagai JSON, selain bidang spesifik event yang didokumentasikan dalam setiap bagian hook event. Untuk command hooks, JSON ini tiba melalui stdin. Untuk HTTP hooks, itu tiba sebagai badan permintaan POST.
| Bidang | Deskripsi |
|---|
session_id | Pengenal sesi saat ini |
transcript_path | Path ke JSON percakapan |
cwd | Direktori kerja saat hook dipanggil |
permission_mode | Mode izin saat ini: "default", "plan", "acceptEdits", "auto", "dontAsk", atau "bypassPermissions". Tidak semua events menerima bidang ini: lihat contoh JSON setiap event di bawah untuk memeriksa |
hook_event_name | Nama event yang dijalankan |
Saat berjalan dengan --agent atau di dalam subagent, dua bidang tambahan disertakan:
| Bidang | Deskripsi |
|---|
agent_id | Pengenal unik untuk subagent. Hadir hanya ketika hook dijalankan di dalam pemanggilan subagent. Gunakan ini untuk membedakan pemanggilan hook subagent dari pemanggilan thread utama. |
agent_type | Nama agent (misalnya, "Explore" atau "security-reviewer"). Hadir ketika sesi menggunakan --agent atau hook dijalankan di dalam subagent. Untuk subagents, tipe subagent mengambil alih nilai --agent sesi. |
Misalnya, hook PreToolUse untuk perintah Bash menerima ini di stdin:
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
Bidang tool_name dan tool_input spesifik untuk event. Setiap bagian hook event mendokumentasikan bidang tambahan untuk event itu.
Output kode keluar
Kode keluar dari perintah hook Anda memberitahu Claude Code apakah tindakan harus dilanjutkan, diblokir, atau diabaikan.
Exit 0 berarti sukses. Claude Code mengurai stdout untuk bidang output JSON. Output JSON hanya diproses pada exit 0. Untuk sebagian besar events, stdout hanya ditampilkan dalam mode verbose (Ctrl+O). Pengecualiannya adalah UserPromptSubmit dan SessionStart, di mana stdout ditambahkan sebagai konteks yang dapat dilihat dan ditindaklanjuti Claude.
Exit 2 berarti kesalahan blocking. Claude Code mengabaikan stdout dan JSON apa pun di dalamnya. Sebagai gantinya, teks stderr diumpankan kembali ke Claude sebagai pesan kesalahan. Efeknya tergantung pada event: PreToolUse memblokir pemanggilan tool, UserPromptSubmit menolak prompt, dan sebagainya. Lihat perilaku kode keluar 2 untuk daftar lengkap.
Kode keluar lainnya adalah kesalahan non-blocking. stderr ditampilkan dalam mode verbose (Ctrl+O) dan eksekusi berlanjut.
Misalnya, skrip perintah hook yang memblokir perintah Bash berbahaya:
#!/bin/bash
# Membaca input JSON dari stdin, memeriksa perintah
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm* ]]; then
echo "Blocked: rm commands are not allowed" >&2
exit 2 # Blocking error: tool call is prevented
fi
exit 0 # Success: tool call proceeds
Perilaku kode keluar 2 per event
Kode keluar 2 adalah cara hook menandakan “berhenti, jangan lakukan ini.” Efeknya tergantung pada event, karena beberapa event mewakili tindakan yang dapat diblokir (seperti pemanggilan tool yang belum terjadi) dan yang lain mewakili hal-hal yang sudah terjadi atau tidak dapat dicegah.
| Hook event | Dapat diblokir? | Apa yang terjadi pada exit 2 |
|---|
PreToolUse | Ya | Memblokir pemanggilan tool |
PermissionRequest | Ya | Menolak izin |
UserPromptSubmit | Ya | Memblokir pemrosesan prompt dan menghapus prompt |
Stop | Ya | Mencegah Claude berhenti, melanjutkan percakapan |
SubagentStop | Ya | Mencegah subagent berhenti |
TeammateIdle | Ya | Mencegah teammate menjadi idle (teammate terus bekerja) |
TaskCreated | Ya | Membatalkan pembuatan tugas |
TaskCompleted | Ya | Mencegah tugas ditandai sebagai selesai |
ConfigChange | Ya | Memblokir perubahan konfigurasi dari berlaku (kecuali policy_settings) |
StopFailure | Tidak | Output dan kode keluar diabaikan |
PostToolUse | Tidak | Menampilkan stderr ke Claude (tool sudah dijalankan) |
PostToolUseFailure | Tidak | Menampilkan stderr ke Claude (tool sudah gagal) |
Notification | Tidak | Menampilkan stderr ke pengguna saja |
SubagentStart | Tidak | Menampilkan stderr ke pengguna saja |
SessionStart | Tidak | Menampilkan stderr ke pengguna saja |
SessionEnd | Tidak | Menampilkan stderr ke pengguna saja |
CwdChanged | Tidak | Menampilkan stderr ke pengguna saja |
FileChanged | Tidak | Menampilkan stderr ke pengguna saja |
PreCompact | Tidak | Menampilkan stderr ke pengguna saja |
PostCompact | Tidak | Menampilkan stderr ke pengguna saja |
Elicitation | Ya | Menolak elicitation |
ElicitationResult | Ya | Memblokir respons (tindakan menjadi decline) |
WorktreeCreate | Ya | Kode keluar non-zero apa pun menyebabkan pembuatan worktree gagal |
WorktreeRemove | Tidak | Kegagalan dicatat dalam mode debug saja |
InstructionsLoaded | Tidak | Kode keluar diabaikan |
Penanganan respons HTTP
HTTP hooks menggunakan kode status HTTP dan badan respons sebagai pengganti kode keluar dan stdout:
- 2xx dengan badan kosong: sukses, setara dengan kode keluar 0 tanpa output
- 2xx dengan badan teks biasa: sukses, teks ditambahkan sebagai konteks
- 2xx dengan badan JSON: sukses, diurai menggunakan skema JSON output yang sama seperti command hooks
- Status non-2xx: kesalahan non-blocking, eksekusi berlanjut
- Kegagalan koneksi atau timeout: kesalahan non-blocking, eksekusi berlanjut
Tidak seperti command hooks, HTTP hooks tidak dapat menandakan kesalahan blocking hanya melalui kode status. Untuk memblokir pemanggilan tool atau menolak izin, kembalikan respons 2xx dengan badan JSON yang berisi bidang keputusan yang sesuai.
Output JSON
Kode keluar memungkinkan Anda mengizinkan atau memblokir, tetapi output JSON memberikan kontrol yang lebih halus. Alih-alih keluar dengan kode 2 untuk memblokir, keluar 0 dan cetak objek JSON ke stdout. Claude Code membaca bidang tertentu dari JSON itu untuk mengontrol perilaku, termasuk decision control untuk memblokir, mengizinkan, atau meningkatkan ke pengguna.
Anda harus memilih satu pendekatan per hook, bukan keduanya: gunakan kode keluar saja untuk signaling, atau keluar 0 dan cetak JSON untuk kontrol terstruktur. Claude Code hanya memproses JSON pada exit 0. Jika Anda keluar 2, JSON apa pun diabaikan.
Stdout hook Anda harus berisi hanya objek JSON. Jika profil shell Anda mencetak teks saat startup, itu dapat mengganggu parsing JSON. Lihat JSON validation failed dalam panduan troubleshooting.
Objek JSON mendukung tiga jenis bidang:
- Bidang universal seperti
continue bekerja di semua events. Ini tercantum dalam tabel di bawah.
- Top-level
decision dan reason digunakan oleh beberapa events untuk memblokir atau memberikan umpan balik.
hookSpecificOutput adalah objek bersarang untuk events yang memerlukan kontrol yang lebih kaya. Ini memerlukan bidang hookEventName yang diatur ke nama event.
| Bidang | Default | Deskripsi |
|---|
continue | true | Jika false, Claude berhenti memproses sepenuhnya setelah hook dijalankan. Mengambil alih bidang keputusan spesifik event apa pun |
stopReason | tidak ada | Pesan ditampilkan ke pengguna saat continue adalah false. Tidak ditampilkan ke Claude |
suppressOutput | false | Jika true, menyembunyikan stdout dari output mode verbose |
systemMessage | tidak ada | Pesan peringatan ditampilkan ke pengguna |
Untuk menghentikan Claude sepenuhnya terlepas dari tipe event:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
Kontrol keputusan
Tidak setiap event mendukung pemblokiran atau kontrol perilaku melalui JSON. Events yang melakukannya masing-masing menggunakan set bidang yang berbeda untuk mengekspresikan keputusan itu. Gunakan tabel ini sebagai referensi cepat sebelum menulis hook:
| Events | Pola keputusan | Bidang kunci |
|---|
| UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange | Top-level decision | decision: "block", reason |
| TeammateIdle, TaskCreated, TaskCompleted | Kode keluar atau continue: false | Kode keluar 2 memblokir tindakan dengan umpan balik stderr. JSON {"continue": false, "stopReason": "..."} juga menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop |
| PreToolUse | hookSpecificOutput | permissionDecision (allow/deny/ask), permissionDecisionReason |
| PermissionRequest | hookSpecificOutput | decision.behavior (allow/deny) |
| WorktreeCreate | path return | Command hook mencetak path di stdout; HTTP hook mengembalikan hookSpecificOutput.worktreePath. Kegagalan hook atau path yang hilang gagal membuat |
| Elicitation | hookSpecificOutput | action (accept/decline/cancel), content (nilai field form untuk accept) |
| ElicitationResult | hookSpecificOutput | action (accept/decline/cancel), content (nilai field form override) |
| WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged | Tidak ada | Tidak ada kontrol keputusan. Digunakan untuk efek samping seperti logging atau cleanup |
Berikut adalah contoh setiap pola dalam aksi:
Top-level decision
PreToolUse
PermissionRequest
Digunakan oleh UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, dan ConfigChange. Satu-satunya nilai adalah "block". Untuk mengizinkan tindakan dilanjutkan, hilangkan decision dari JSON Anda, atau keluar 0 tanpa JSON apa pun:{
"decision": "block",
"reason": "Test suite must pass before proceeding"
}
Menggunakan hookSpecificOutput untuk kontrol yang lebih kaya: izinkan, tolak, atau tingkatkan ke pengguna. Anda juga dapat memodifikasi input tool sebelum dijalankan atau menyuntikkan konteks tambahan untuk Claude. Lihat PreToolUse decision control untuk set lengkap opsi.{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes are not allowed"
}
}
Menggunakan hookSpecificOutput untuk mengizinkan atau menolak permintaan izin atas nama pengguna. Saat mengizinkan, Anda juga dapat memodifikasi input tool atau menerapkan aturan izin sehingga pengguna tidak diminta lagi. Lihat PermissionRequest decision control untuk set lengkap opsi.{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
Untuk contoh yang diperluas termasuk validasi perintah Bash, pemfilteran prompt, dan skrip persetujuan otomatis, lihat What you can automate dalam panduan dan Bash command validator reference implementation.
Hook events
Setiap event sesuai dengan titik dalam siklus hidup Claude Code di mana hooks dapat dijalankan. Bagian-bagian di bawah diurutkan untuk mencocokkan siklus hidup: dari pengaturan sesi melalui loop agentic ke akhir sesi. Setiap bagian menjelaskan kapan event dijalankan, matcher apa yang didukungnya, JSON input yang diterima, dan cara mengontrol perilaku melalui output.
SessionStart
Dijalankan ketika Claude Code memulai sesi baru atau melanjutkan sesi yang ada. Berguna untuk memuat konteks pengembangan seperti masalah yang ada atau perubahan terbaru pada codebase Anda, atau menyiapkan variabel lingkungan. Untuk konteks statis yang tidak memerlukan skrip, gunakan CLAUDE.md sebagai gantinya.
SessionStart dijalankan pada setiap sesi, jadi jaga hooks ini tetap cepat. Hanya hooks type: "command" yang didukung.
Nilai matcher sesuai dengan cara sesi dimulai:
| Matcher | Kapan dijalankan |
|---|
startup | Sesi baru |
resume | --resume, --continue, atau /resume |
clear | /clear |
compact | Compaction otomatis atau manual |
Selain bidang input umum, SessionStart hooks menerima source, model, dan secara opsional agent_type. Bidang source menunjukkan bagaimana sesi dimulai: "startup" untuk sesi baru, "resume" untuk sesi yang dilanjutkan, "clear" setelah /clear, atau "compact" setelah compaction. Bidang model berisi pengenal model. Jika Anda memulai Claude Code dengan claude --agent <name>, bidang agent_type berisi nama agent.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-6"
}
Kontrol keputusan SessionStart
Teks apa pun yang dicetak skrip hook ke stdout ditambahkan sebagai konteks untuk Claude. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan bidang spesifik event ini:
| Bidang | Deskripsi |
|---|
additionalContext | String ditambahkan ke konteks Claude. Nilai dari beberapa hooks digabungkan |
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "My additional context here"
}
}
Pertahankan variabel lingkungan
SessionStart hooks memiliki akses ke variabel lingkungan CLAUDE_ENV_FILE, yang menyediakan path file di mana Anda dapat mempertahankan variabel lingkungan untuk perintah Bash berikutnya.
Untuk menetapkan variabel lingkungan individual, tulis pernyataan export ke CLAUDE_ENV_FILE. Gunakan append (>>) untuk mempertahankan variabel yang ditetapkan oleh hooks lain:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
Untuk menangkap semua perubahan lingkungan dari perintah setup, bandingkan variabel yang diekspor sebelum dan sesudah:
#!/bin/bash
ENV_BEFORE=$(export -p | sort)
# Jalankan perintah setup Anda yang memodifikasi lingkungan
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0
Variabel apa pun yang ditulis ke file ini akan tersedia dalam semua perintah Bash berikutnya yang dijalankan Claude Code selama sesi.
CLAUDE_ENV_FILE tersedia untuk SessionStart, CwdChanged, dan FileChanged hooks. Tipe hook lainnya tidak memiliki akses ke variabel ini.
InstructionsLoaded
Dijalankan ketika file CLAUDE.md atau .claude/rules/*.md dimuat ke dalam konteks. Event ini dijalankan saat startup sesi untuk file yang dimuat dengan eager dan lagi nanti ketika file dimuat dengan lazy, misalnya ketika Claude mengakses subdirektori yang berisi CLAUDE.md bersarang atau ketika aturan bersyarat dengan frontmatter paths: cocok. Hook tidak mendukung pemblokiran atau kontrol keputusan. Itu dijalankan secara asinkron untuk tujuan observabilitas.
Matcher dijalankan terhadap load_reason. Misalnya, gunakan "matcher": "session_start" untuk dijalankan hanya untuk file yang dimuat saat startup sesi, atau "matcher": "path_glob_match|nested_traversal" untuk dijalankan hanya untuk lazy loads.
Selain bidang input umum, InstructionsLoaded hooks menerima bidang-bidang ini:
| Bidang | Deskripsi |
|---|
file_path | Path absolut ke file instruksi yang dimuat |
memory_type | Cakupan file: "User", "Project", "Local", atau "Managed" |
load_reason | Mengapa file dimuat: "session_start", "nested_traversal", "path_glob_match", "include", atau "compact". Nilai "compact" dijalankan ketika file instruksi dimuat ulang setelah event compaction |
globs | Pola glob path dari frontmatter paths: file, jika ada. Hadir hanya untuk load path_glob_match |
trigger_file_path | Path ke file yang akses memicu load ini, untuk lazy loads |
parent_file_path | Path ke file instruksi induk yang menyertakan ini, untuk load include |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
Kontrol keputusan InstructionsLoaded
InstructionsLoaded hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir atau memodifikasi pemuatan instruksi. Gunakan event ini untuk audit logging, compliance tracking, atau observabilitas.
UserPromptSubmit
Dijalankan ketika pengguna mengirimkan prompt, sebelum Claude memproses. Ini memungkinkan Anda menambahkan konteks tambahan berdasarkan prompt/percakapan, memvalidasi prompts, atau memblokir jenis prompts tertentu.
Selain bidang input umum, UserPromptSubmit hooks menerima bidang prompt yang berisi teks yang dikirimkan pengguna.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}
Kontrol keputusan UserPromptSubmit
Hooks UserPromptSubmit dapat mengontrol apakah prompt pengguna diproses dan menambahkan konteks. Semua bidang output JSON tersedia.
Ada dua cara untuk menambahkan konteks ke percakapan pada kode keluar 0:
- Plain text stdout: teks non-JSON apa pun yang ditulis ke stdout ditambahkan sebagai konteks
- JSON dengan
additionalContext: gunakan format JSON di bawah untuk kontrol lebih. Bidang additionalContext ditambahkan sebagai konteks
Plain stdout ditampilkan sebagai output hook dalam transkrip. Bidang additionalContext ditambahkan lebih diskrit.
Untuk memblokir prompt, kembalikan objek JSON dengan decision diatur ke "block":
| Bidang | Deskripsi |
|---|
decision | "block" mencegah prompt diproses dan menghapusnya dari konteks. Hilangkan untuk mengizinkan prompt dilanjutkan |
reason | Ditampilkan ke pengguna saat decision adalah "block". Tidak ditambahkan ke konteks |
additionalContext | String ditambahkan ke konteks Claude |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here"
}
}
Format JSON tidak diperlukan untuk kasus penggunaan sederhana. Untuk menambahkan konteks, Anda dapat mencetak teks biasa ke stdout dengan kode keluar 0. Gunakan JSON ketika Anda perlu memblokir prompts atau menginginkan kontrol yang lebih terstruktur.
Dijalankan setelah Claude membuat parameter tool dan sebelum memproses pemanggilan tool. Cocok pada nama tool: Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode, dan nama MCP tool apa pun.
Gunakan PreToolUse decision control untuk mengizinkan, menolak, atau meminta izin untuk menggunakan tool.
Selain bidang input umum, PreToolUse hooks menerima tool_name, tool_input, dan tool_use_id. Bidang tool_input tergantung pada tool:
Bash
Menjalankan perintah shell.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
command | string | "npm test" | Perintah shell untuk dijalankan |
description | string | "Run test suite" | Deskripsi opsional tentang apa yang dilakukan perintah |
timeout | number | 120000 | Timeout opsional dalam milidetik |
run_in_background | boolean | false | Apakah menjalankan perintah di latar belakang |
Write
Membuat atau menimpa file.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
file_path | string | "/path/to/file.txt" | Path absolut ke file untuk ditulis |
content | string | "file content" | Konten untuk ditulis ke file |
Edit
Mengganti string dalam file yang ada.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
file_path | string | "/path/to/file.txt" | Path absolut ke file untuk diedit |
old_string | string | "original text" | Teks untuk dicari dan diganti |
new_string | string | "replacement text" | Teks pengganti |
replace_all | boolean | false | Apakah mengganti semua kemunculan |
Read
Membaca konten file.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
file_path | string | "/path/to/file.txt" | Path absolut ke file untuk dibaca |
offset | number | 10 | Nomor baris opsional untuk mulai membaca dari |
limit | number | 50 | Jumlah baris opsional untuk dibaca |
Glob
Menemukan file yang cocok dengan pola glob.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
pattern | string | "**/*.ts" | Pola glob untuk mencocokkan file terhadap |
path | string | "/path/to/dir" | Direktori opsional untuk dicari. Default ke direktori kerja saat ini |
Grep
Mencari konten file dengan ekspresi reguler.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
pattern | string | "TODO.*fix" | Pola ekspresi reguler untuk dicari |
path | string | "/path/to/dir" | File atau direktori opsional untuk dicari |
glob | string | "*.ts" | Pola glob opsional untuk memfilter file |
output_mode | string | "content" | "content", "files_with_matches", atau "count". Default ke "files_with_matches" |
-i | boolean | true | Pencarian case insensitive |
multiline | boolean | false | Aktifkan pencocokan multiline |
WebFetch
Mengambil dan memproses konten web.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
url | string | "https://example.com/api" | URL untuk mengambil konten dari |
prompt | string | "Extract the API endpoints" | Prompt untuk dijalankan pada konten yang diambil |
WebSearch
Mencari web.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
query | string | "react hooks best practices" | Query pencarian |
allowed_domains | array | ["docs.example.com"] | Opsional: hanya sertakan hasil dari domain ini |
blocked_domains | array | ["spam.example.com"] | Opsional: kecualikan hasil dari domain ini |
Agent
Spawn subagent.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
prompt | string | "Find all API endpoints" | Tugas untuk agent lakukan |
description | string | "Find API endpoints" | Deskripsi singkat tugas |
subagent_type | string | "Explore" | Tipe agent khusus untuk digunakan |
model | string | "sonnet" | Alias model opsional untuk menimpa default |
AskUserQuestion
Mengajukan pertanyaan multiple-choice satu hingga empat kepada pengguna.
| Bidang | Tipe | Contoh | Deskripsi |
|---|
questions | array | [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}] | Pertanyaan untuk disajikan, masing-masing dengan string question, header pendek, array options, dan flag multiSelect opsional |
answers | object | {"Which framework?": "React"} | Opsional. Memetakan teks pertanyaan ke label opsi yang dipilih. Jawaban multi-select menggabungkan label dengan koma. Claude tidak menetapkan bidang ini; sediakan melalui updatedInput untuk menjawab secara programatis |
Hooks PreToolUse dapat mengontrol apakah pemanggilan tool dilanjutkan. Tidak seperti hooks lain yang menggunakan bidang decision tingkat atas, PreToolUse mengembalikan keputusannya di dalam objek hookSpecificOutput. Ini memberikannya kontrol yang lebih kaya: tiga hasil (izinkan, tolak, atau tanya) ditambah kemampuan untuk memodifikasi input tool sebelum eksekusi.
| Bidang | Deskripsi |
|---|
permissionDecision | "allow" melewati prompt izin. "deny" mencegah pemanggilan tool. "ask" meminta pengguna untuk mengkonfirmasi. Deny and ask rules masih berlaku ketika hook mengembalikan "allow" |
permissionDecisionReason | Untuk "allow" dan "ask", ditampilkan ke pengguna tetapi bukan Claude. Untuk "deny", ditampilkan ke Claude |
updatedInput | Memodifikasi parameter input tool sebelum eksekusi. Menggantikan seluruh objek input, jadi sertakan bidang yang tidak berubah bersama yang dimodifikasi. Gabungkan dengan "allow" untuk persetujuan otomatis, atau "ask" untuk menampilkan input yang dimodifikasi ke pengguna |
additionalContext | String ditambahkan ke konteks Claude sebelum tool dijalankan |
Ketika hook mengembalikan "ask", dialog izin yang ditampilkan kepada pengguna mencakup label yang mengidentifikasi dari mana hook berasal: misalnya, [User], [Project], [Plugin], atau [Local]. Ini membantu pengguna memahami sumber konfigurasi mana yang meminta konfirmasi.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
AskUserQuestion dan ExitPlanMode memerlukan interaksi pengguna dan biasanya memblokir dalam mode non-interaktif dengan flag -p. Mengembalikan permissionDecision: "allow" bersama dengan updatedInput memenuhi persyaratan itu: hook membaca input tool dari stdin, mengumpulkan jawaban melalui UI Anda sendiri, dan mengembalikannya dalam updatedInput sehingga tool dijalankan tanpa meminta. Mengembalikan "allow" saja tidak cukup untuk tools ini. Untuk AskUserQuestion, kembalikan array questions asli dan tambahkan objek answers yang memetakan teks setiap pertanyaan ke jawaban yang dipilih.
PreToolUse sebelumnya menggunakan bidang decision dan reason tingkat atas, tetapi ini sudah usang untuk event ini. Gunakan hookSpecificOutput.permissionDecision dan hookSpecificOutput.permissionDecisionReason sebagai gantinya. Nilai usang "approve" dan "block" memetakan ke "allow" dan "deny" masing-masing. Events lain seperti PostToolUse dan Stop terus menggunakan decision dan reason tingkat atas sebagai format saat ini mereka.
PermissionRequest
Dijalankan ketika pengguna ditampilkan dialog izin.
Gunakan PermissionRequest decision control untuk mengizinkan atau menolak atas nama pengguna.
Cocok pada nama tool, nilai yang sama seperti PreToolUse.
PermissionRequest hooks menerima bidang tool_name dan tool_input seperti PreToolUse hooks, tetapi tanpa tool_use_id. Array permission_suggestions opsional berisi opsi “selalu izinkan” yang biasanya dilihat pengguna dalam dialog izin. Perbedaannya adalah kapan hook dijalankan: PermissionRequest hooks dijalankan ketika dialog izin akan ditampilkan ke pengguna, sementara PreToolUse hooks dijalankan sebelum eksekusi tool terlepas dari status izin.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
Kontrol keputusan PermissionRequest
Hooks PermissionRequest dapat mengizinkan atau menolak permintaan izin. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan objek decision dengan bidang spesifik event ini:
| Bidang | Deskripsi |
|---|
behavior | "allow" memberikan izin, "deny" menolaknya |
updatedInput | Untuk "allow" saja: memodifikasi parameter input tool sebelum eksekusi. Menggantikan seluruh objek input, jadi sertakan bidang yang tidak berubah bersama yang dimodifikasi |
updatedPermissions | Untuk "allow" saja: array dari permission update entries untuk diterapkan, seperti menambahkan aturan allow atau mengubah mode izin sesi |
message | Untuk "deny" saja: memberitahu Claude mengapa izin ditolak |
interrupt | Untuk "deny" saja: jika true, menghentikan Claude |
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
Permission update entries
Bidang output updatedPermissions dan bidang input permission_suggestions keduanya menggunakan array objek entry yang sama. Setiap entry memiliki type yang menentukan bidang lainnya, dan destination yang mengontrol di mana perubahan ditulis.
type | Bidang | Efek |
|---|
addRules | rules, behavior, destination | Menambahkan aturan izin. rules adalah array dari objek {toolName, ruleContent?}. Hilangkan ruleContent untuk mencocokkan seluruh tool. behavior adalah "allow", "deny", atau "ask" |
replaceRules | rules, behavior, destination | Mengganti semua aturan dari behavior yang diberikan di destination dengan rules yang disediakan |
removeRules | rules, behavior, destination | Menghapus aturan yang cocok dari behavior yang diberikan |
setMode | mode, destination | Mengubah mode izin. Mode yang valid adalah default, acceptEdits, dontAsk, bypassPermissions, dan plan |
addDirectories | directories, destination | Menambahkan direktori kerja. directories adalah array dari string path |
removeDirectories | directories, destination | Menghapus direktori kerja |
Bidang destination pada setiap entry menentukan apakah perubahan tetap dalam memori atau persisten ke file pengaturan.
destination | Menulis ke |
|---|
session | hanya dalam memori, dibuang ketika sesi berakhir |
localSettings | .claude/settings.local.json |
projectSettings | .claude/settings.json |
userSettings | ~/.claude/settings.json |
Hook dapat mengembalikan salah satu dari permission_suggestions yang diterima sebagai output updatedPermissions miliknya sendiri, yang setara dengan pengguna memilih opsi “selalu izinkan” itu dalam dialog.
PostToolUse
Dijalankan segera setelah tool selesai dengan sukses.
Cocok pada nama tool, nilai yang sama seperti PreToolUse.
Input PostToolUse
Hooks PostToolUse dijalankan setelah tool sudah dijalankan dengan sukses. Input mencakup tool_input, argumen yang dikirim ke tool, dan tool_response, hasil yang dikembalikan. Skema yang tepat untuk keduanya tergantung pada tool.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": {
"filePath": "/path/to/file.txt",
"success": true
},
"tool_use_id": "toolu_01ABC123..."
}
Kontrol keputusan PostToolUse
Hooks PostToolUse dapat memberikan umpan balik ke Claude setelah eksekusi tool. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
| Bidang | Deskripsi |
|---|
decision | "block" meminta Claude dengan reason. Hilangkan untuk mengizinkan tindakan dilanjutkan |
reason | Penjelasan ditampilkan ke Claude saat decision adalah "block" |
additionalContext | Konteks tambahan untuk Claude pertimbangkan |
updatedMCPToolOutput | Untuk MCP tools saja: mengganti output tool dengan nilai yang disediakan |
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude"
}
}
PostToolUseFailure
Dijalankan ketika eksekusi tool gagal. Event ini dijalankan untuk pemanggilan tool yang melempar kesalahan atau mengembalikan hasil kegagalan. Gunakan ini untuk mencatat kegagalan, mengirim alert, atau memberikan umpan balik korektif ke Claude.
Cocok pada nama tool, nilai yang sama seperti PreToolUse.
Input PostToolUseFailure
PostToolUseFailure hooks menerima bidang tool_name dan tool_input yang sama seperti PostToolUse, bersama dengan informasi kesalahan sebagai bidang tingkat atas:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUseFailure",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
},
"tool_use_id": "toolu_01ABC123...",
"error": "Command exited with non-zero status code 1",
"is_interrupt": false
}
| Bidang | Deskripsi |
|---|
error | String menjelaskan apa yang salah |
is_interrupt | Boolean opsional menunjukkan apakah kegagalan disebabkan oleh interupsi pengguna |
Kontrol keputusan PostToolUseFailure
Hooks PostToolUseFailure dapat memberikan konteks ke Claude setelah kegagalan tool. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
| Bidang | Deskripsi |
|---|
additionalContext | Konteks tambahan untuk Claude pertimbangkan bersama kesalahan |
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for Claude"
}
}
Notification
Dijalankan ketika Claude Code mengirimkan notifikasi. Cocok pada tipe notifikasi: permission_prompt, idle_prompt, auth_success, elicitation_dialog. Hilangkan matcher untuk menjalankan hooks untuk semua tipe notifikasi.
Gunakan matchers terpisah untuk menjalankan handler berbeda tergantung pada tipe notifikasi. Konfigurasi ini memicu skrip alert khusus izin ketika Claude memerlukan persetujuan izin dan notifikasi berbeda ketika Claude telah idle:
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/permission-alert.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/idle-notification.sh"
}
]
}
]
}
}
Selain bidang input umum, Notification hooks menerima message dengan teks notifikasi, title opsional, dan notification_type menunjukkan tipe mana yang dijalankan.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Notification",
"message": "Claude needs your permission to use Bash",
"title": "Permission needed",
"notification_type": "permission_prompt"
}
Notification hooks tidak dapat memblokir atau memodifikasi notifikasi. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan additionalContext untuk menambahkan konteks ke percakapan:
| Bidang | Deskripsi |
|---|
additionalContext | String ditambahkan ke konteks Claude |
SubagentStart
Dijalankan ketika subagent Claude Code dispawn melalui tool Agent. Mendukung matchers untuk memfilter berdasarkan nama tipe agent (agent bawaan seperti Bash, Explore, Plan, atau nama agent kustom dari .claude/agents/).
Selain bidang input umum, SubagentStart hooks menerima agent_id dengan pengenal unik untuk subagent dan agent_type dengan nama agent (agent bawaan seperti "Bash", "Explore", "Plan", atau nama agent kustom).
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SubagentStart",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}
SubagentStart hooks tidak dapat memblokir pembuatan subagent, tetapi mereka dapat menyuntikkan konteks ke subagent. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan:
| Bidang | Deskripsi |
|---|
additionalContext | String ditambahkan ke konteks subagent |
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Follow security guidelines for this task"
}
}
SubagentStop
Dijalankan ketika subagent Claude Code telah selesai merespons. Cocok pada tipe agent, nilai yang sama seperti SubagentStart.
Selain bidang input umum, SubagentStop hooks menerima stop_hook_active, agent_id, agent_type, agent_transcript_path, dan last_assistant_message. Bidang agent_type adalah nilai yang digunakan untuk pemfilteran matcher. transcript_path adalah transkrip sesi utama, sementara agent_transcript_path adalah transkrip subagent sendiri yang disimpan dalam folder subagents/ bersarang. Bidang last_assistant_message berisi konten teks respons akhir subagent, jadi hooks dapat mengaksesnya tanpa mengurai file transkrip.
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../abc123.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false,
"agent_id": "def456",
"agent_type": "Explore",
"agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
"last_assistant_message": "Analysis complete. Found 3 potential issues..."
}
SubagentStop hooks menggunakan format kontrol keputusan yang sama seperti Stop hooks.
TaskCreated
Dijalankan ketika tugas sedang dibuat melalui tool TaskCreate. Gunakan ini untuk menegakkan konvensi penamaan, memerlukan deskripsi tugas, atau mencegah tugas tertentu dari dibuat.
Ketika hook TaskCreated keluar dengan kode 2, tugas tidak dibuat dan pesan stderr diumpankan kembali ke model sebagai umpan balik. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TaskCreated hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.
Selain bidang input umum, TaskCreated hooks menerima task_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCreated",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
| Bidang | Deskripsi |
|---|
task_id | Pengenal tugas yang sedang dibuat |
task_subject | Judul tugas |
task_description | Deskripsi detail tugas. Mungkin tidak ada |
teammate_name | Nama teammate yang membuat tugas. Mungkin tidak ada |
team_name | Nama team. Mungkin tidak ada |
Kontrol keputusan TaskCreated
TaskCreated hooks mendukung dua cara untuk mengontrol pembuatan tugas:
- Kode keluar 2: tugas tidak dibuat dan pesan stderr diumpankan kembali ke model sebagai umpan balik.
- JSON
{"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini memblokir tugas yang subjeknya tidak mengikuti format yang diperlukan:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
exit 2
fi
exit 0
TaskCompleted
Dijalankan ketika tugas sedang ditandai sebagai selesai. Ini dijalankan dalam dua situasi: ketika agent apa pun secara eksplisit menandai tugas sebagai selesai melalui tool TaskUpdate, atau ketika agent team teammate menyelesaikan giliran dengan tugas yang sedang berlangsung. Gunakan ini untuk menegakkan kriteria penyelesaian seperti passing tests atau lint checks sebelum tugas dapat ditutup.
Ketika hook TaskCompleted keluar dengan kode 2, tugas tidak ditandai sebagai selesai dan pesan stderr diumpankan kembali ke model sebagai umpan balik. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TaskCompleted hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.
Selain bidang input umum, TaskCompleted hooks menerima task_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCompleted",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "my-project"
}
| Bidang | Deskripsi |
|---|
task_id | Pengenal tugas yang sedang diselesaikan |
task_subject | Judul tugas |
task_description | Deskripsi detail tugas. Mungkin tidak ada |
teammate_name | Nama teammate yang menyelesaikan tugas. Mungkin tidak ada |
team_name | Nama team. Mungkin tidak ada |
Kontrol keputusan TaskCompleted
TaskCompleted hooks mendukung dua cara untuk mengontrol penyelesaian tugas:
- Kode keluar 2: tugas tidak ditandai sebagai selesai dan pesan stderr diumpankan kembali ke model sebagai umpan balik.
- JSON
{"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini menjalankan tests dan memblokir penyelesaian tugas jika gagal:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# Jalankan test suite
if ! npm test 2>&1; then
echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
exit 2
fi
exit 0
Stop
Dijalankan ketika agent Claude Code utama telah selesai merespons. Tidak dijalankan jika penghentian terjadi karena interupsi pengguna. Kesalahan API menjalankan StopFailure sebagai gantinya.
Selain bidang input umum, Stop hooks menerima stop_hook_active dan last_assistant_message. Bidang 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. Bidang last_assistant_message berisi konten teks respons akhir Claude, jadi hooks dapat mengaksesnya tanpa mengurai file transkrip.
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true,
"last_assistant_message": "I've completed the refactoring. Here's a summary..."
}
Kontrol keputusan Stop
Hooks Stop dan SubagentStop dapat mengontrol apakah Claude melanjutkan. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
| Bidang | Deskripsi |
|---|
decision | "block" mencegah Claude berhenti. Hilangkan untuk mengizinkan Claude berhenti |
reason | Diperlukan saat decision adalah "block". Memberitahu Claude mengapa itu harus melanjutkan |
{
"decision": "block",
"reason": "Must be provided when Claude is blocked from stopping"
}
StopFailure
Dijalankan alih-alih Stop ketika giliran berakhir karena kesalahan API. Output dan kode keluar diabaikan. Gunakan ini untuk mencatat kegagalan, mengirim alert, atau mengambil tindakan pemulihan ketika Claude tidak dapat menyelesaikan respons karena rate limits, masalah autentikasi, atau kesalahan API lainnya.
Selain bidang input umum, StopFailure hooks menerima error, error_details opsional, dan last_assistant_message opsional. Bidang error mengidentifikasi tipe kesalahan dan digunakan untuk pemfilteran matcher.
| Bidang | Deskripsi |
|---|
error | Tipe kesalahan: rate_limit, authentication_failed, billing_error, invalid_request, server_error, max_output_tokens, atau unknown |
error_details | Detail tambahan tentang kesalahan, ketika tersedia |
last_assistant_message | Teks kesalahan yang dirender ditampilkan dalam percakapan. Tidak seperti Stop dan SubagentStop, di mana bidang ini menyimpan output percakapan Claude, untuk StopFailure itu berisi string kesalahan API itu sendiri, seperti "API Error: Rate limit reached" |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "StopFailure",
"error": "rate_limit",
"error_details": "429 Too Many Requests",
"last_assistant_message": "API Error: Rate limit reached"
}
StopFailure hooks tidak memiliki kontrol keputusan. Mereka dijalankan untuk tujuan notifikasi dan logging saja.
TeammateIdle
Dijalankan ketika agent team teammate akan menjadi idle setelah menyelesaikan giliran. Gunakan ini untuk menegakkan quality gates sebelum teammate berhenti bekerja, seperti memerlukan passing lint checks atau memverifikasi bahwa file output ada.
Ketika hook TeammateIdle keluar dengan kode 2, teammate menerima pesan stderr sebagai umpan balik dan terus bekerja alih-alih menjadi idle. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TeammateIdle hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.
Selain bidang input umum, TeammateIdle hooks menerima teammate_name dan team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "my-project"
}
| Bidang | Deskripsi |
|---|
teammate_name | Nama teammate yang akan menjadi idle |
team_name | Nama team |
Kontrol keputusan TeammateIdle
TeammateIdle hooks mendukung dua cara untuk mengontrol perilaku teammate:
- Kode keluar 2: teammate menerima pesan stderr sebagai umpan balik dan terus bekerja alih-alih menjadi idle.
- JSON
{"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini memeriksa bahwa artefak build ada sebelum mengizinkan teammate menjadi idle:
#!/bin/bash
if [ ! -f "./dist/output.js" ]; then
echo "Build artifact missing. Run the build before stopping." >&2
exit 2
fi
exit 0
ConfigChange
Dijalankan ketika file konfigurasi berubah selama sesi. Gunakan ini untuk mengaudit perubahan pengaturan, menegakkan kebijakan keamanan, atau memblokir modifikasi tidak sah ke file konfigurasi.
ConfigChange hooks dijalankan untuk perubahan ke file pengaturan, pengaturan kebijakan terkelola, dan file skill. Bidang source dalam input memberitahu Anda tipe konfigurasi mana yang berubah, dan bidang file_path opsional menyediakan path ke file yang berubah.
Matcher memfilter pada sumber konfigurasi:
| Matcher | Kapan dijalankan |
|---|
user_settings | ~/.claude/settings.json berubah |
project_settings | .claude/settings.json berubah |
local_settings | .claude/settings.local.json berubah |
policy_settings | Pengaturan kebijakan terkelola berubah |
skills | File skill dalam .claude/skills/ berubah |
Contoh ini mencatat semua perubahan konfigurasi untuk audit keamanan:
{
"hooks": {
"ConfigChange": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-config-change.sh"
}
]
}
]
}
}
Selain bidang input umum, ConfigChange hooks menerima source dan secara opsional file_path. Bidang source menunjukkan tipe konfigurasi mana yang berubah, dan file_path menyediakan path ke file spesifik yang dimodifikasi.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "ConfigChange",
"source": "project_settings",
"file_path": "/Users/.../my-project/.claude/settings.json"
}
Kontrol keputusan ConfigChange
ConfigChange hooks dapat memblokir perubahan konfigurasi dari berlaku. Gunakan kode keluar 2 atau JSON decision untuk mencegah perubahan. Ketika diblokir, pengaturan baru tidak diterapkan ke sesi yang berjalan.
| Bidang | Deskripsi |
|---|
decision | "block" mencegah perubahan konfigurasi diterapkan. Hilangkan untuk mengizinkan perubahan |
reason | Penjelasan ditampilkan ke pengguna saat decision adalah "block" |
{
"decision": "block",
"reason": "Configuration changes to project settings require admin approval"
}
Perubahan policy_settings tidak dapat diblokir. Hooks masih dijalankan untuk sumber policy_settings, jadi Anda dapat menggunakannya untuk audit logging, tetapi keputusan blocking apa pun diabaikan. Ini memastikan pengaturan yang dikelola enterprise selalu berlaku.
CwdChanged
Dijalankan ketika direktori kerja berubah selama sesi, misalnya ketika Claude menjalankan perintah cd. Gunakan ini untuk bereaksi terhadap perubahan direktori: muat ulang variabel lingkungan, aktifkan toolchains khusus proyek, atau jalankan skrip setup secara otomatis. Berpasangan dengan FileChanged untuk tools seperti direnv yang mengelola lingkungan per-direktori.
CwdChanged hooks memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file itu bertahan ke perintah Bash berikutnya untuk sesi, sama seperti dalam SessionStart hooks. Hanya hooks type: "command" yang didukung.
CwdChanged tidak mendukung matchers dan dijalankan pada setiap perubahan direktori.
Selain bidang input umum, CwdChanged hooks menerima old_cwd dan new_cwd.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project/src",
"hook_event_name": "CwdChanged",
"old_cwd": "/Users/my-project",
"new_cwd": "/Users/my-project/src"
}
Output CwdChanged
Selain bidang output JSON yang tersedia untuk semua hooks, CwdChanged hooks dapat mengembalikan watchPaths untuk secara dinamis menetapkan path file mana yang FileChanged pantau:
| Bidang | Deskripsi |
|---|
watchPaths | Array path absolut. Menggantikan daftar watch dinamis saat ini (path dari konfigurasi matcher Anda selalu dipantau). Mengembalikan array kosong menghapus daftar dinamis, yang khas saat memasuki direktori baru |
CwdChanged hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir perubahan direktori.
FileChanged
Dijalankan ketika file yang dipantau berubah di disk. Bidang matcher dalam konfigurasi hook Anda mengontrol nama file mana yang dipantau: itu adalah daftar yang dipisahkan pipe dari basenames (nama file tanpa path direktori, misalnya ".envrc|.env"). Nilai matcher yang sama juga digunakan untuk memfilter hooks mana yang dijalankan ketika file berubah, mencocokkan terhadap basename dari file yang berubah. Berguna untuk memuat ulang variabel lingkungan ketika file konfigurasi proyek dimodifikasi.
FileChanged hooks memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file itu bertahan ke perintah Bash berikutnya untuk sesi, sama seperti dalam SessionStart hooks. Hanya hooks type: "command" yang didukung.
Selain bidang input umum, FileChanged hooks menerima file_path dan event.
| Bidang | Deskripsi |
|---|
file_path | Path absolut ke file yang berubah |
event | Apa yang terjadi: "change" (file dimodifikasi), "add" (file dibuat), atau "unlink" (file dihapus) |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "FileChanged",
"file_path": "/Users/my-project/.envrc",
"event": "change"
}
Output FileChanged
Selain bidang output JSON yang tersedia untuk semua hooks, FileChanged hooks dapat mengembalikan watchPaths untuk secara dinamis memperbarui path file mana yang dipantau:
| Bidang | Deskripsi |
|---|
watchPaths | Array path absolut. Menggantikan daftar watch dinamis saat ini (path dari konfigurasi matcher Anda selalu dipantau). Gunakan ini ketika skrip hook Anda menemukan file tambahan untuk dipantau berdasarkan file yang berubah |
FileChanged hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir perubahan file dari terjadi.
WorktreeCreate
Ketika Anda menjalankan claude --worktree atau subagent menggunakan isolation: "worktree", Claude Code membuat salinan kerja terisolasi menggunakan git worktree. Jika Anda mengonfigurasi hook WorktreeCreate, itu menggantikan perilaku git default, memungkinkan Anda menggunakan sistem kontrol versi berbeda seperti SVN, Perforce, atau Mercurial.
Karena hook menggantikan perilaku default sepenuhnya, .worktreeinclude tidak diproses. Jika Anda perlu menyalin file konfigurasi lokal seperti .env ke worktree baru, lakukan di dalam skrip hook Anda.
Hook harus mengembalikan path absolut ke direktori worktree yang dibuat. Claude Code menggunakan path ini sebagai direktori kerja untuk sesi terisolasi. Command hooks mencetaknya di stdout; HTTP hooks mengembalikannya melalui hookSpecificOutput.worktreePath.
Contoh ini membuat salinan kerja SVN dan mencetak path untuk Claude Code gunakan. Ganti URL repositori dengan milik Anda sendiri:
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
Hook membaca name worktree dari input JSON di stdin, melakukan checkout salinan segar ke direktori baru, dan mencetak path direktori. echo pada baris terakhir adalah apa yang Claude Code baca sebagai path worktree. Alihkan output lainnya ke stderr sehingga tidak mengganggu path.
Selain bidang input umum, WorktreeCreate hooks menerima bidang name. Ini adalah pengenal slug untuk worktree baru, baik ditentukan oleh pengguna atau auto-generated (misalnya, bold-oak-a3f2).
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeCreate",
"name": "feature-auth"
}
Output WorktreeCreate
WorktreeCreate hooks tidak menggunakan model keputusan allow/block standar. Sebaliknya, kesuksesan atau kegagalan hook menentukan hasil. Hook harus mengembalikan path absolut ke direktori worktree yang dibuat:
- Command hooks (
type: "command"): cetak path di stdout.
- HTTP hooks (
type: "http"): kembalikan { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } dalam badan respons.
Jika hook gagal atau tidak menghasilkan path, pembuatan worktree gagal dengan kesalahan.
WorktreeRemove
Pasangan cleanup untuk WorktreeCreate. Hook ini dijalankan ketika worktree sedang dihapus, baik ketika Anda keluar dari sesi --worktree dan memilih untuk menghapusnya, atau ketika subagent dengan isolation: "worktree" selesai. Untuk worktrees berbasis git, Claude menangani cleanup secara otomatis dengan git worktree remove. Jika Anda mengonfigurasi hook WorktreeCreate untuk sistem kontrol versi non-git, pasangkan dengan hook WorktreeRemove untuk menangani cleanup. Tanpanya, direktori worktree ditinggalkan di disk.
Claude Code meneruskan path yang dikembalikan oleh WorktreeCreate sebagai worktree_path dalam input hook. Contoh ini membaca path itu dan menghapus direktori:
{
"hooks": {
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
}
]
}
]
}
}
Selain bidang input umum, WorktreeRemove hooks menerima bidang worktree_path, yang merupakan path absolut ke worktree yang sedang dihapus.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeRemove",
"worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}
WorktreeRemove hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir penghapusan worktree tetapi dapat melakukan tugas cleanup seperti menghapus status kontrol versi atau mengarsipkan perubahan. Kegagalan hook dicatat dalam mode debug saja.
PreCompact
Dijalankan sebelum Claude Code akan menjalankan operasi compact.
Nilai matcher menunjukkan apakah compaction dipicu secara manual atau otomatis:
| Matcher | Kapan dijalankan |
|---|
manual | /compact |
auto | Auto-compact ketika context window penuh |
Selain bidang input umum, PreCompact hooks menerima trigger dan custom_instructions. Untuk manual, custom_instructions berisi apa yang diteruskan pengguna ke /compact. Untuk auto, custom_instructions kosong.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}
PostCompact
Dijalankan setelah Claude Code menyelesaikan operasi compact. Gunakan event ini untuk bereaksi terhadap status compacted baru, misalnya untuk mencatat ringkasan yang dihasilkan atau memperbarui status eksternal.
Nilai matcher yang sama berlaku seperti untuk PreCompact:
| Matcher | Kapan dijalankan |
|---|
manual | Setelah /compact |
auto | Setelah auto-compact ketika context window penuh |
Input PostCompact
Selain bidang input umum, PostCompact hooks menerima trigger dan compact_summary. Bidang compact_summary berisi ringkasan percakapan yang dihasilkan oleh operasi compact.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PostCompact",
"trigger": "manual",
"compact_summary": "Summary of the compacted conversation..."
}
PostCompact hooks tidak memiliki kontrol keputusan. Mereka tidak dapat mempengaruhi hasil compaction tetapi dapat melakukan tugas follow-up.
SessionEnd
Dijalankan ketika sesi Claude Code berakhir. Berguna untuk tugas cleanup, logging statistik sesi, atau menyimpan status sesi. Mendukung matchers untuk memfilter berdasarkan alasan keluar.
Bidang reason dalam input hook menunjukkan mengapa sesi berakhir:
| Alasan | Deskripsi |
|---|
clear | Sesi dihapus dengan perintah /clear |
resume | Sesi beralih melalui /resume interaktif |
logout | Pengguna logout |
prompt_input_exit | Pengguna keluar saat input prompt terlihat |
bypass_permissions_disabled | Mode bypass permissions dinonaktifkan |
other | Alasan keluar lainnya |
Selain bidang input umum, SessionEnd hooks menerima bidang reason menunjukkan mengapa sesi berakhir. Lihat tabel reason di atas untuk semua nilai.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionEnd",
"reason": "other"
}
SessionEnd hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir penghentian sesi tetapi dapat melakukan tugas cleanup.
SessionEnd hooks memiliki timeout default 1.5 detik. Ini berlaku untuk keluar sesi, /clear, dan beralih sesi melalui /resume interaktif. Jika hooks Anda memerlukan lebih banyak waktu, atur variabel lingkungan CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS ke nilai lebih tinggi dalam milidetik. Pengaturan timeout per-hook apa pun juga dibatasi oleh nilai ini.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
Elicitation
Dijalankan ketika server MCP meminta input pengguna mid-task. Secara default, Claude Code menampilkan dialog interaktif untuk pengguna merespons. Hooks dapat mengintersepsi permintaan ini dan merespons secara programatis, melewati dialog sepenuhnya.
Bidang matcher mencocokkan nama server MCP.
Selain bidang input umum, Elicitation hooks menerima mcp_server_name, message, dan bidang opsional mode, url, elicitation_id, dan requested_schema.
Untuk form-mode elicitation (kasus paling umum):
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please provide your credentials",
"mode": "form",
"requested_schema": {
"type": "object",
"properties": {
"username": { "type": "string", "title": "Username" }
}
}
}
Untuk URL-mode elicitation (autentikasi berbasis browser):
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please authenticate",
"mode": "url",
"url": "https://auth.example.com/login"
}
Output Elicitation
Untuk merespons secara programatis tanpa menampilkan dialog, kembalikan objek JSON dengan hookSpecificOutput:
{
"hookSpecificOutput": {
"hookEventName": "Elicitation",
"action": "accept",
"content": {
"username": "alice"
}
}
}
| Bidang | Nilai | Deskripsi |
|---|
action | accept, decline, cancel | Apakah menerima, menolak, atau membatalkan permintaan |
content | object | Nilai field form untuk dikirimkan. Hanya digunakan saat action adalah accept |
Kode keluar 2 menolak elicitation dan menampilkan stderr ke pengguna.
ElicitationResult
Dijalankan setelah pengguna merespons elicitation MCP. Hooks dapat mengamati, memodifikasi, atau memblokir respons sebelum dikirim kembali ke server MCP.
Bidang matcher mencocokkan nama server MCP.
Selain bidang input umum, ElicitationResult hooks menerima mcp_server_name, action, dan bidang opsional mode, elicitation_id, dan content.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "ElicitationResult",
"mcp_server_name": "my-mcp-server",
"action": "accept",
"content": { "username": "alice" },
"mode": "form",
"elicitation_id": "elicit-123"
}
Output ElicitationResult
Untuk menimpa respons pengguna, kembalikan objek JSON dengan hookSpecificOutput:
{
"hookSpecificOutput": {
"hookEventName": "ElicitationResult",
"action": "decline",
"content": {}
}
}
| Bidang | Nilai | Deskripsi |
|---|
action | accept, decline, cancel | Menimpa tindakan pengguna |
content | object | Menimpa nilai field form. Hanya bermakna saat action adalah accept |
Kode keluar 2 memblokir respons, mengubah tindakan efektif menjadi decline.
Prompt-based hooks
Selain command dan HTTP hooks, Claude Code mendukung prompt-based hooks (type: "prompt") yang menggunakan LLM untuk mengevaluasi apakah akan mengizinkan atau memblokir tindakan, dan agent hooks (type: "agent") yang spawn agentic verifier dengan akses tool. Tidak semua events mendukung setiap tipe hook.
Events yang mendukung semua empat tipe hook (command, http, prompt, dan agent):
PermissionRequest
PostToolUse
PostToolUseFailure
PreToolUse
Stop
SubagentStop
TaskCompleted
TaskCreated
UserPromptSubmit
Events yang mendukung hooks command dan http tetapi bukan prompt atau agent:
ConfigChange
CwdChanged
Elicitation
ElicitationResult
FileChanged
InstructionsLoaded
Notification
PostCompact
PreCompact
SessionEnd
StopFailure
SubagentStart
TeammateIdle
WorktreeCreate
WorktreeRemove
SessionStart mendukung hanya hooks command.
Bagaimana prompt-based hooks bekerja
Alih-alih menjalankan perintah Bash, prompt-based hooks:
- Mengirimkan input hook dan prompt Anda ke model Claude, Haiku secara default
- LLM merespons dengan JSON terstruktur yang berisi keputusan
- Claude Code memproses keputusan secara otomatis
Konfigurasi prompt hook
Atur type ke "prompt" dan sediakan string prompt alih-alih command. Gunakan placeholder $ARGUMENTS untuk menyuntikkan data JSON input hook ke dalam teks prompt Anda. Claude Code mengirimkan prompt gabungan dan input ke model Claude cepat, yang mengembalikan keputusan JSON.
Hook Stop ini meminta LLM untuk mengevaluasi apakah Claude harus berhenti sebelum mengizinkan Claude selesai:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}
| Bidang | Diperlukan | Deskripsi |
|---|
type | ya | Harus "prompt" |
prompt | ya | Teks prompt untuk dikirim ke LLM. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook. Jika $ARGUMENTS tidak ada, JSON input ditambahkan ke prompt |
model | tidak | Model untuk digunakan untuk evaluasi. Default ke model cepat |
timeout | tidak | Timeout dalam detik. Default: 30 |
Skema respons
LLM harus merespons dengan JSON yang berisi:
{
"ok": true | false,
"reason": "Explanation for the decision"
}
| Bidang | Deskripsi |
|---|
ok | true mengizinkan tindakan, false mencegahnya |
reason | Diperlukan saat ok adalah false. Penjelasan ditampilkan ke Claude |
Contoh: Multi-criteria Stop hook
Hook Stop ini menggunakan prompt detail untuk memeriksa tiga kondisi sebelum mengizinkan Claude berhenti. Jika "ok" adalah false, Claude terus bekerja dengan alasan yang disediakan sebagai instruksi berikutnya. Hooks SubagentStop menggunakan format yang sama untuk mengevaluasi apakah subagent harus berhenti:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
"timeout": 30
}
]
}
]
}
}
Agent-based hooks
Agent-based hooks (type: "agent") seperti prompt-based hooks tetapi dengan akses tool multi-turn. Alih-alih pemanggilan LLM tunggal, hook agent spawn subagent yang dapat membaca file, mencari kode, dan memeriksa codebase untuk memverifikasi kondisi. Agent hooks mendukung events yang sama seperti prompt-based hooks.
Bagaimana agent hooks bekerja
Ketika hook agent dijalankan:
- Claude Code spawn subagent dengan prompt Anda dan JSON input hook
- Subagent dapat menggunakan tools seperti Read, Grep, dan Glob untuk menyelidiki
- Setelah hingga 50 turn, subagent mengembalikan keputusan terstruktur
{ "ok": true/false }
- Claude Code memproses keputusan dengan cara yang sama seperti prompt hook
Agent hooks berguna ketika verifikasi memerlukan memeriksa file aktual atau output test, bukan hanya mengevaluasi data input hook saja.
Konfigurasi agent hook
Atur type ke "agent" dan sediakan string prompt. Bidang konfigurasi sama seperti prompt hooks, dengan timeout default lebih lama:
| Bidang | Diperlukan | Deskripsi |
|---|
type | ya | Harus "agent" |
prompt | ya | Prompt menjelaskan apa yang diverifikasi. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook |
model | tidak | Model untuk digunakan. Default ke model cepat |
timeout | tidak | Timeout dalam detik. Default: 60 |
Skema respons sama seperti prompt hooks: { "ok": true } untuk mengizinkan atau { "ok": false, "reason": "..." } untuk memblokir.
Hook Stop ini memverifikasi bahwa semua unit tests lulus sebelum mengizinkan Claude selesai:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
Jalankan hooks di latar belakang
Secara default, hooks memblokir eksekusi Claude sampai selesai. Untuk tugas yang berjalan lama seperti deployments, test suites, atau panggilan API eksternal, atur "async": true untuk menjalankan hook di latar belakang sementara Claude terus bekerja. Async hooks tidak dapat memblokir atau mengontrol perilaku Claude: bidang respons seperti decision, permissionDecision, dan continue tidak berpengaruh, karena tindakan yang akan mereka kontrol sudah selesai.
Konfigurasi async hook
Tambahkan "async": true ke konfigurasi command hook untuk menjalankannya di latar belakang tanpa memblokir Claude. Bidang ini hanya tersedia pada hooks type: "command".
Hook ini menjalankan skrip test setelah setiap pemanggilan tool Write. Claude terus bekerja segera sementara run-tests.sh dijalankan hingga 120 detik. Ketika skrip selesai, outputnya disampaikan pada turn percakapan berikutnya:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true,
"timeout": 120
}
]
}
]
}
}
Bidang timeout menetapkan waktu maksimum dalam detik untuk proses latar belakang. Jika tidak ditentukan, async hooks menggunakan default 10 menit yang sama seperti sync hooks.
Bagaimana async hooks dijalankan
Ketika async hook dijalankan, Claude Code memulai proses hook dan segera melanjutkan tanpa menunggu selesai. Hook menerima JSON input yang sama melalui stdin seperti hook sinkron.
Setelah proses latar belakang keluar, jika hook menghasilkan respons JSON dengan bidang systemMessage atau additionalContext, konten itu disampaikan ke Claude sebagai konteks pada turn percakapan berikutnya.
Notifikasi penyelesaian async hook ditekan secara default. Untuk melihatnya, aktifkan mode verbose dengan Ctrl+O atau mulai Claude Code dengan --verbose.
Contoh: jalankan tests setelah perubahan file
Hook ini memulai test suite di latar belakang setiap kali Claude menulis file, kemudian melaporkan hasil kembali ke Claude ketika tests selesai. Simpan skrip ini ke .claude/hooks/run-tests-async.sh dalam proyek Anda dan buat dapat dijalankan dengan chmod +x:
#!/bin/bash
# run-tests-async.sh
# Baca hook input dari stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Hanya jalankan tests untuk file sumber
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
exit 0
fi
# Jalankan tests dan laporkan hasil via systemMessage
RESULT=$(npm test 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
else
echo "{\"systemMessage\": \"Tests failed after editing $FILE_PATH: $RESULT\"}"
fi
Kemudian tambahkan konfigurasi ini ke .claude/settings.json dalam akar proyek Anda. Flag async: true memungkinkan Claude terus bekerja sementara tests dijalankan:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
"async": true,
"timeout": 300
}
]
}
]
}
}
Keterbatasan
Async hooks memiliki beberapa batasan dibandingkan dengan hooks sinkron:
- Hanya hooks
type: "command" yang mendukung async. Prompt-based hooks tidak dapat dijalankan secara asinkron.
- Async hooks tidak dapat memblokir pemanggilan tool atau mengembalikan keputusan. Pada saat hook selesai, tindakan pemicu sudah dilanjutkan.
- Output hook disampaikan pada turn percakapan berikutnya. Jika sesi idle, respons menunggu sampai interaksi pengguna berikutnya.
- Setiap eksekusi membuat proses latar belakang terpisah. Tidak ada deduplikasi di seluruh beberapa penjalankan hook async yang sama.
Pertimbangan keamanan
Penafian
Command hooks dijalankan dengan izin pengguna sistem penuh Anda.
Command hooks menjalankan perintah shell dengan izin pengguna penuh Anda. Mereka dapat memodifikasi, menghapus, atau mengakses file apa pun yang dapat diakses akun pengguna Anda. Tinjau dan uji semua perintah hook sebelum menambahkannya ke konfigurasi Anda.
Praktik terbaik keamanan
Ingat praktik-praktik ini saat menulis hooks:
- Validasi dan sanitasi input: jangan pernah mempercayai data input secara membabi buta
- Selalu kutip variabel shell: gunakan
"$VAR" bukan $VAR
- Blokir path traversal: periksa
.. dalam path file
- Gunakan path absolut: tentukan path lengkap untuk skrip, menggunakan
"$CLAUDE_PROJECT_DIR" untuk akar proyek
- Lewati file sensitif: hindari
.env, .git/, keys, dll.
Debug hooks
Jalankan claude --debug untuk melihat detail eksekusi hook, termasuk hooks mana yang cocok, kode keluar mereka, dan output.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
Untuk granularitas yang lebih halus pada detail pencocokan hook, atur CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose untuk melihat baris log tambahan seperti jumlah matcher hook dan pencocokan query.
Untuk troubleshooting masalah umum seperti hooks tidak dijalankan, infinite Stop hook loops, atau kesalahan konfigurasi, lihat Limitations and troubleshooting dalam panduan.