Siklus hidup hook
Claude Code menjalankan hooks pada titik-titik tertentu selama sesi. 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. Events jatuh ke dalam tiga cadence:- per sesi:
SessionStartdanSessionEnd - per turn:
UserPromptSubmit,Stop, danStopFailure - pada setiap pemanggilan tool di dalam loop agentic:
PreToolUsedanPostToolUse, kecuali panggilanEndConversation, yang melewati keduanya
Bagaimana hook diselesaikan
Untuk melihat bagaimana event, matcher, dan handler cocok bersama, pertimbangkan hookPreToolUse ini yang memblokir perintah shell yang merusak.
- macOS/Linux
- Windows (PowerShell)
matcher mempersempit ke panggilan tool Bash dan perintah Monitor, dan kondisi if mempersempit lebih lanjut ke subperintah Bash yang cocok dengan rm *, jadi block-rm.sh hanya spawn ketika kedua filter cocok:permissionDecision dari "deny" jika berisi rm -rf. Simpan ke .claude/hooks/block-rm.sh di proyek Anda dan buat dapat dieksekusi dengan chmod +x .claude/hooks/block-rm.sh sehingga Claude Code dapat menjalankannya:jq, jadi instal jq dan pastikan itu ada di PATH Anda sebelum mencobanya.Bash "rm -rf /tmp/build" terhadap konfigurasi macOS/Linux. Inilah yang terjadi:
1
Event dijalankan
Event
PreToolUse dijalankan. Claude Code mengirimkan input tool sebagai JSON di stdin ke hook:2
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.3
Kondisi if memeriksa
Kondisi
if "Bash(rm *)" cocok karena rm -rf /tmp/build adalah subperintah yang cocok 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.4
Handler hook dijalankan
Skrip memeriksa perintah lengkap dan menemukan Jika perintah telah menjadi varian
rm -rf, jadi itu mencetak keputusan ke stdout:rm yang lebih aman seperti rm file.txt, skrip akan mencapai exit 0 sebagai gantinya. Kode keluar 0 tanpa output berarti hook tidak memiliki keputusan untuk dilaporkan, jadi pemanggilan tool berlanjut melalui alur izin normal. Hook dapat menolak pemanggilan, tetapi tetap diam tidak menyetujuinya.5
Claude Code bertindak atas hasil
Claude Code membaca keputusan JSON, memblokir pemanggilan tool, dan menunjukkan Claude alasannya.
Konfigurasi
Hook didefinisikan dalam file pengaturan JSON. Konfigurasi memiliki tiga tingkat penyarangan:- Pilih event hook yang akan direspons, seperti
PreToolUseatauStop - Tambahkan grup matcher untuk memfilter kapan hook dipicu, seperti “untuk tool Bash”
- Definisikan satu atau lebih handler hook untuk dijalankan saat cocok
Halaman ini menggunakan istilah khusus untuk setiap tingkat: event hook untuk titik siklus hidup, grup matcher untuk filter, dan handler hook untuk perintah shell, endpoint HTTP, tool MCP, prompt, atau agent yang dijalankan. “Hook” saja mengacu pada fitur secara umum.
Lokasi hook
Tempat Anda mendefinisikan hook menentukan cakupannya:
Sesi cloud tidak membaca
~/.claude/settings.json lokal Anda. Dalam lingkungan self-hosted, Claude Code juga menjalankan hook yang disiapkan operator dari ~/.claude/ milik host runner, dan menjalankan hook dalam file pengaturan terkelola milik image runner ketika file tersebut termasuk di antara sumber terkelola yang diterapkan Claude Code, yang secara default berarti hanya ketika baik pengaturan terkelola server maupun kebijakan Claude Code yang dikirim melalui MDM tidak menyediakan tingkat terkelola. Lihat apa yang terbawa dari penyiapan Anda untuk mengetahui file pengaturan dan plugin mana, dan dengan demikian hook mana, yang sampai ke sesi cloud.
Untuk detail tentang resolusi file pengaturan, lihat pengaturan.
Hook dari file pengaturan, pengaturan kebijakan terkelola, dan plugin juga berjalan di dalam subagent. Ketika subagent memanggil tool, event tool seperti PreToolUse dan PostToolUse memicu hook terkonfigurasi yang sama seperti dalam percakapan utama, dan input membawa field input umum agent_id dan agent_type yang mengidentifikasi subagent tersebut.
Administrator dapat menggunakan allowManagedHooksOnly dalam pengaturan terkelola untuk membatasi hook mana yang berjalan:
- Hook pengguna, proyek, lokal, dan plugin Anda diblokir. Hook dari plugin yang diaktifkan paksa dalam
enabledPluginspengaturan terkelola dikecualikan - Claude Code juga mempersempit pengaturan
statusLinedanfileSuggestionAnda ke pengaturan terkelola - Claude Code juga menonaktifkan plugin dengan sumber
command, termasuk plugin yang diaktifkan paksa dalamenabledPluginspengaturan terkelola, kecuali jikadisableCommandPluginSourcessecara eksplisit disetel kefalse. Sumbercommandmemerlukan Claude Code v2.1.229 atau yang lebih baru - Claude Code juga memblokir perintah
headersHelpermarketplace kecuali jikadisableCommandPluginSourcessecara eksplisit disetel kefalse, dengan pengecualian untuk marketplace yang dideklarasikan oleh pengaturan terkelola itu sendiri
allowManagedHooksOnly.
Entri hook digabungkan di seluruh tingkat pengaturan alih-alih saling menggantikan: pengaturan pengguna, proyek, dan lokal menambahkan hook mereka sendiri tanpa menghapus hook terkelola, dan pengaturan disableAllHooks tidak dapat menonaktifkan hook terkelola dari luar pengaturan terkelola.
Allowlist hook HTTP berlaku untuk hook dari setiap sumber, termasuk pengaturan kebijakan terkelola:
allowedHttpHookUrls: ketika didefinisikan di tingkat pengaturan mana pun, Claude Code menjalankan handler hook HTTP hanya jika URL-nya cocok dengan allowlist yang telah digabungkanhttpHookAllowedEnvVars: ketika didefinisikan, Claude Code hanya menginterpolasi environment variable yang ada dalam daftar tersebut ke dalam header hook
Pola matcher
Fieldmatcher memfilter kapan hook dipicu. Cara matcher dievaluasi bergantung pada karakter yang dikandungnya:
Matcher pada jalur ekspresi reguler diuji dengan
RegExp.prototype.test milik JavaScript, yang berhasil jika ada kecocokan di mana pun dalam nilai. Edit.* cocok dengan Edit maupun NotebookEdit; bungkus pola dengan ^ dan $, seperti ^Edit$, saat Anda memerlukan kecocokan seluruh string.
FileChanged dan StopFailure menggunakan himpunan pencocokan persis yang lebih sempit, yaitu hanya huruf, digit, _, dan |. Tanda hubung, spasi, atau koma dalam matcher untuk kedua event tersebut membuatnya tetap berada di jalur ekspresi reguler, dan hanya | yang memisahkan alternatif. Setiap event lain yang mendukung matcher dalam tabel berikut menerima | atau ,.
Event FileChanged tidak mengikuti aturan ini saat membangun daftar pantauannya. Lihat FileChanged.
Setiap tipe event dicocokkan pada field yang berbeda:
Mencocokkan
StopFailure pada cloud_credential_error memerlukan Claude Code v2.1.267 atau yang lebih baru, versi pertama yang melaporkan kegagalan pemuatan kredensial di bawah nilai tersebut alih-alih server_error atau unknown.
Untuk sebagian besar event, Claude Code mengevaluasi matcher terhadap field dari input JSON yang dikirimnya ke hook Anda melalui stdin. Untuk event tool, field tersebut adalah tool_name. Untuk PreModelSwitch dan PostModelSwitch, Claude Code mengevaluasi matcher terhadap nama kanonis yang diturunkannya dari to_model, seperti dijelaskan di bawah PreModelSwitch. Setiap bagian event hook mencantumkan himpunan lengkap nilai matcher dan skema input untuk event tersebut.
Contoh ini menjalankan skrip linting hanya saat Claude menulis atau mengedit file:
matcher ke event yang tidak mendukung matcher, field tersebut diabaikan tanpa pemberitahuan.
Untuk event tool, Anda dapat memfilter lebih sempit dengan menyetel field if pada handler hook individual. if menggunakan sintaks aturan izin untuk mencocokkan nama tool dan argumen secara bersamaan, sehingga "Bash(git *)" berjalan ketika subperintah mana pun dari input Bash cocok dengan git * dan "Edit(*.ts)" hanya berjalan untuk file TypeScript.
Mencocokkan tool MCP
Tool server MCP muncul sebagai tool biasa dalam event tool (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), sehingga Anda dapat mencocokkannya dengan cara yang sama seperti mencocokkan nama tool lainnya.
Tool MCP mengikuti pola penamaan mcp__<server>__<tool>, misalnya:
mcp__memory__create_entities: tool create entities milik server Memorymcp__filesystem__read_file: tool read file milik server Filesystemmcp__github__search_repositories: tool pencarian milik server GitHub
.* ke prefiks server. .* wajib ada: matcher seperti mcp__memory atau mcp__brave-search hanya berisi karakter pencocokan persis, sehingga dibandingkan sebagai string persis dan tidak cocok dengan tool apa pun.
mcp__memory__.*cocok dengan semua tool dari servermemorymcp__brave-search__.*cocok dengan semua tool dari server yang namanya mengandung tanda hubungmcp__.*__write.*cocok dengan tool apa pun yang namanya diawaliwritedari server mana pun
mcp__plugin_<plugin-name>_<server-name>__<tool>. Matcher yang ditulis terhadap kunci server polos tidak pernah dipicu untuk tool ini. Untuk plugin bernama my-plugin yang membundel server di bawah kunci db, tool query muncul sebagai mcp__plugin_my-plugin_db__query, sehingga matcher untuk setiap tool dari server tersebut adalah mcp__plugin_my-plugin_db__.*. Gunakan nama tool bercakupan yang sama dalam field if milik handler. Lihat Server MCP yang disediakan plugin untuk cara nama bercakupan dibentuk.
Contoh ini mencatat semua operasi server memory dan memvalidasi operasi tulis dari server MCP mana pun:
Mencocokkan perintah Monitor
HookPreToolUse atau PermissionRequest dengan matcher Bash juga berjalan pada perintah shell yang dimulai Claude dengan tool Monitor, tetapi allow dan updatedInput miliknya tidak berpengaruh pada perintah tersebut. Memerlukan Claude Code v2.1.314 atau yang lebih baru. Matcher seperti Bash|PowerShell, ^Bash$, dan Bash.* juga merupakan matcher Bash. Hook dengan matcher Bash tidak berjalan untuk pantauan WebSocket, dan hook PostToolUse dengan matcher tersebut tidak berjalan untuk Monitor.
Hook yang berjalan untuk perintah Monitor dapat menolaknya atau membuat Claude Code menanyakannya kepada Anda. Untuk mengizinkan atau menulis ulang perintah, beralihlah dari matcher Bash ke matcher yang menyertakan Monitor:
- Matcher yang menyertakan Monitor: dengan matcher seperti
Bash|Monitoratau.*, atau tanpa matcher, hook mendapatkan input milik panggilan Monitor itu sendiri, danallowsertaupdatedInputberlaku. Dalamif, sebutkan Monitor, seperti padaMonitor(tail *), yang mencocokkan berdasarkan aturan yang diikutiBash(tail *). Jikaif-nya menyebutkan Bash, hook tetap mendapatkan input panggilan Monitor, tetapiallowdanupdatedInputtidak berpengaruh - Matcher
Bash: hook mendapatkan panggilan Bash. Dalamif, sebutkan Bash, seperti padaBash(tail *). Hook tidak berjalan jikaif-nya menyebutkan Monitor
Field handler hook
Setiap objek dalam arrayhooks bagian dalam adalah handler hook: perintah shell, endpoint HTTP, tool MCP, prompt LLM, atau agent yang berjalan ketika matcher cocok. Ada lima tipe:
- Hook perintah (
type: "command"): menjalankan perintah shell. Skrip Anda menerima input JSON milik event melalui stdin dan mengomunikasikan hasil kembali melalui exit code dan stdout. - Hook HTTP (
type: "http"): mengirim input JSON milik event sebagai permintaan HTTP POST ke sebuah URL. Endpoint mengomunikasikan hasil kembali melalui body respons menggunakan format output JSON yang sama seperti hook perintah. - Hook tool MCP (
type: "mcp_tool"): memanggil tool pada server MCP yang dikonfigurasi. Output teks tool diperlakukan seperti stdout hook perintah. - Hook prompt (
type: "prompt"): mengirim prompt ke model Claude untuk evaluasi satu giliran. Model mengembalikan keputusannya sebagai JSON. Lihat Hook berbasis prompt. - Hook agent (
type: "agent"): memunculkan subagent yang dapat menggunakan tool seperti Read, Grep, dan Glob untuk memverifikasi kondisi sebelum mengembalikan keputusan. Hook agent bersifat eksperimental dan dapat berubah. Lihat Hook berbasis agent.
timeout setiap hook yang menunggu mulai dihitung ketika hook tersebut dimulai.
Jika Anda mendefinisikan handler yang sama di lebih dari satu file pengaturan, handler tersebut berjalan sekali. Salinan handler yang sama milik plugin atau skill tetap terpisah.
Handler berjalan di direktori saat ini dengan environment Claude Code. Jika direktori saat ini sudah tidak ada, misalnya worktree atau direktori sementara yang dihapus shell lain di tengah sesi, Claude Code menjalankan hook perintah dari direktori pertama berikut yang masih ada: direktori tempat sesi dimulai, root proyek, direktori home Anda, atau direktori sementara sistem. Claude Code mencatat peringatan yang menyebutkan direktori fallback di log debug. Untuk sesi worktree yang Anda mulai dari aplikasi desktop, lihat Apa yang dibagikan worktree dengan checkout utama.
Environment variable $CLAUDE_CODE_REMOTE bernilai "true" di lingkungan web jarak jauh dan tidak disetel di CLI lokal. Claude Code v2.1.199 dan yang lebih baru menyetel $CLAUDE_CODE_BRIDGE_SESSION_ID ke ID sesi Remote Control selama sesi lokal memiliki koneksi Remote Control yang aktif.
Field umum
Field berikut berlaku untuk semua tipe hook:
Field
if berisi tepat satu aturan izin. Tidak ada sintaks &&, ||, atau daftar untuk menggabungkan aturan; untuk menerapkan beberapa kondisi, definisikan handler hook terpisah untuk masing-masing.
Dalam kondisi if untuk tool file, pola direktori satu segmen seperti "Edit(src/**)" hanya cocok dengan direktori src di direktori kerja dan file di bawahnya. Untuk mencocokkan direktori bernama src pada kedalaman berapa pun, tulis "Edit(**/src/**)".
Cara pola if mencocokkan perintah Bash
Untuk pola Bash dalam field if, apakah perintah hook Anda berjalan bergantung pada bentuk pola dan perintah Bash yang dipanggil Claude. Penugasan VAR=value di awal dihapus sebelum pencocokan.
Ketika Claude Code tidak dapat menentukan perintah mana yang dijalankan input Bash, Claude Code menjalankan hook Anda terlepas dari polanya. Karena filter
if bersifat upaya terbaik, gunakan sistem izin alih-alih hook untuk memberlakukan allow atau deny yang tegas.
Field hook perintah
Selain field umum, hook perintah menerima field berikut:Bentuk exec dan bentuk shell
Hook perintah berjalan sebagai bentuk exec ketikaargs disetel, dan bentuk shell ketika args dihilangkan. Setel args setiap kali hook mereferensikan placeholder path, karena setiap elemen diteruskan sebagai satu argumen tanpa tanda kutip. Hilangkan args ketika Anda memerlukan fitur shell seperti pipe atau &&, atau ketika kedua pertimbangan tersebut tidak berlaku.
Bentuk exec berjalan ketika args ada. Claude Code me-resolve command sebagai executable pada PATH dan menjalankannya langsung dengan args sebagai vektor argumen. Tidak ada shell, sehingga setiap elemen args adalah satu argumen persis seperti yang ditulis, dan placeholder path seperti ${CLAUDE_PLUGIN_ROOT} disubstitusikan ke dalam command dan ke dalam setiap elemen args sebagai string biasa. Karakter khusus seperti apostrof, $, dan backtick diteruskan apa adanya karena tidak ada shell yang menginterpretasikannya. Tidak ada tokenisasi shell yang terjadi di platform mana pun.
Bentuk shell berjalan ketika args tidak ada. String command diteruskan ke shell: sh -c di macOS dan Linux, Git Bash di Windows, atau PowerShell ketika Git Bash tidak terinstal. Setel field shell untuk memilih secara eksplisit. Shell menokenisasi string, memperluas variabel, dan menginterpretasikan pipe, &&, redirect, dan glob.
Di Windows, bentuk exec mengharuskan
command di-resolve ke executable sungguhan seperti .exe. Shim .cmd dan .bat yang diinstal npm, npx, eslint, dan tool lain di node_modules/.bin bukan executable dan tidak dapat dijalankan tanpa shell. Untuk menjalankannya dalam bentuk exec, panggil skrip yang mendasarinya dengan node secara langsung, misalnya "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Pola node ditambah path skrip berfungsi di setiap platform karena node.exe adalah binary sungguhan. Untuk menjalankan shim .cmd atau .bat berdasarkan nama, gunakan bentuk shell.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, dan CLAUDE_PLUGIN_DATA pada proses yang dijalankan, sehingga skrip dapat membaca process.env.CLAUDE_PLUGIN_ROOT terlepas dari cara skrip tersebut diluncurkan.
Hook plugin juga menyubstitusikan nilai ${user_config.*}, hanya dalam bentuk exec: nilai disubstitusikan ke dalam command dan ke dalam setiap elemen args sebagai string biasa, sehingga tidak ada shell yang mengurainya ulang.
Hook plugin bentuk shell yang command-nya mereferensikan ${user_config.*} gagal dengan error alih-alih berjalan. Untuk menggunakan nilai opsi dari hook bentuk shell, baca environment variable $CLAUDE_PLUGIN_OPTION_<KEY>, seperti $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL untuk opsi webhook_url, atau setel args untuk mengalihkan hook ke bentuk exec. Sebelum v2.1.207, perintah hook plugin bentuk shell juga menyubstitusikan ${user_config.*}.
Dalam bentuk exec,
command hanyalah nama atau path executable. Jika command adalah nama polos tanpa pemisah path dan mengandung spasi bersama dengan args, Claude Code mencatat peringatan karena proses tidak akan dapat dijalankan: tidak ada executable bernama node script.js. Pindahkan token tambahan ke dalam args. Path absolut dengan spasi, seperti C:\Program Files\nodejs\node.exe, adalah satu executable yang valid dan tidak memicu peringatan.Field hook HTTP
Selain field umum, hook HTTP menerima field berikut:
Claude Code mengirim input JSON hook sebagai body permintaan POST dengan
Content-Type: application/json. Body respons menggunakan format output JSON yang sama seperti hook perintah.
Penanganan error berbeda dari hook perintah; lihat Penanganan respons HTTP.
Contoh ini mengirim event PreToolUse ke layanan validasi lokal, dengan autentikasi menggunakan token dari environment variable MY_TOKEN:
Field hook tool MCP
Selain field umum, hook tool MCP menerima field berikut:
Contoh ini memanggil tool
security_scan pada server MCP my_server setelah setiap Write atau Edit, dengan meneruskan path file yang diedit:
Cara hasil tool dibaca
Claude Code membaca konten teks tool dengan cara yang sama seperti membaca stdout hook perintah, mengikuti aturan penguraian di bawah exit code 0. Jika tool mengembalikanisError: true, hook menghasilkan error yang tidak memblokir dan eksekusi berlanjut.
Jika error hook berisi salah satu pesan berikut, Claude Code tidak dapat membaca jawaban tool, sehingga hook tidak memberikan putusan:
PreToolUse, Claude Code menolak panggilan tool yang sedang diperiksa hook. Pada event lain seperti UserPromptSubmit, PostToolUse, dan Stop, error tersebut tidak memblokir.
Jika error terus muncul, kembalikan lebih sedikit data dari tool, atau ubah tool atau input hook agar jawabannya lebih kecil.
Ketika server masih terhubung
Pada event di mana hook dapat memblokir atau mengubah hasil, sepertiPreToolUse atau Stop, Claude Code menunggu server yang sedang terhubung sebelum memanggil tool, paling lama selama MCP_TIMEOUT dan dalam batas timeout milik hook itu sendiri. Pada event observasional, seperti Notification atau SessionEnd, Claude Code tidak menunggu.
Server yang menampilkan status cached terhubung ketika hook memanggil tool-nya. Jika server tidak terhubung pada saat itu, hook menghasilkan error yang tidak memblokir dan eksekusi berlanjut. Hook tidak pernah memulai alur OAuth, jadi autentikasikan server dari /mcp terlebih dahulu.
Event yang dipicu sebelum server MCP tersedia
SessionStart saat peluncuran, termasuk dengan --continue atau --resume, dan setiap event Setup dipicu sebelum server MCP sesi tersedia bagi hook. Claude Code melewati hook mcp_tool milik event tersebut tanpa memanggil tool, dan log debug mencatat mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), atau pesan yang sama yang menyebutkan Setup. Ketika SessionStart dipicu lagi nanti dalam sesi, setelah /clear atau compaction, hook mcp_tool-nya berjalan. Untuk apa pun yang dibutuhkan sesi saat peluncuran, gunakan hook type: "command" pada SessionStart sebagai gantinya.
Field hook prompt dan agent
Selain field umum, hook prompt dan agent menerima field berikut:Mereferensikan skrip berdasarkan path
Gunakan placeholder berikut untuk mereferensikan skrip hook relatif terhadap root proyek atau plugin, terlepas dari direktori kerja saat hook berjalan:${CLAUDE_PROJECT_DIR}: root proyek tempat sesi dimulai. Claude Code juga menyetel variabel ini di environment server MCP stdio dan server LSP plugin.${CLAUDE_PLUGIN_ROOT}: direktori instalasi plugin, untuk skrip yang dibundel dengan plugin. Lihat environment variable plugin untuk perilaku path di seluruh pembaruan.${CLAUDE_PLUGIN_DATA}: direktori data persisten milik plugin, untuk dependensi dan status yang harus tetap ada setelah pembaruan plugin.
Worktree berbeda. Jika Claude masuk ke worktree selama sesi, Claude Code mempertahankan
${CLAUDE_PROJECT_DIR} di tempatnya dan meneruskan path worktree ke hook Anda dengan cara lain:${CLAUDE_PROJECT_DIR}tetap di tempatnya: variabel ini tetap menunjuk ke root proyek tempat sesi dimulai, sehingga perintah seperti${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shtetap menjalankan skrip di checkout utama.cwdmengikuti Claude: fieldcwddalam JSON input hook adalah root worktree setelah Claude masuk ke worktree, dan direktori baru setelah Claude menjalankancd. Bacalah field ini ketika hook perlu mengetahui di direktori mana Claude sedang bekerja.
${CLAUDE_PROJECT_DIR} menunjuk.- Skrip proyek
- Skrip plugin
Contoh ini menggunakan
${CLAUDE_PROJECT_DIR} untuk menjalankan pemeriksa gaya dari direktori .claude/hooks/ proyek setelah panggilan tool Write atau Edit apa pun:Hook dalam skill dan agent
Selain file pengaturan dan plugin, hook dapat didefinisikan langsung dalam skill dan subagent menggunakan frontmatter, dalam format konfigurasi yang sama seperti hook berbasis pengaturan. Berapa lama Claude Code mempertahankan pendaftarannya bergantung pada komponennya:- Hook subagent: Claude Code menjalankannya hanya selama subagent tersebut berjalan dan menghapusnya ketika subagent selesai. Claude Code mengonversi hook
Stopdi sini menjadiSubagentStop, event yang dipicunya ketika subagent selesai. - Hook skill: Claude Code mendaftarkannya ketika Anda atau Claude memanggil skill dan terus menjalankannya selama sisa sesi, termasuk pada giliran setelah giliran skill itu sendiri. Agar Claude Code menghapus hook setelah eksekusi pertamanya yang berhasil, setel
once: truepada hook tersebut.
PreToolUse yang menjalankan skrip validasi keamanan sebelum setiap perintah Bash:
-p di folder yang belum Anda percayai.
Hook frontmatter dalam subagent proyek hanya berjalan setelah Anda menerima dialog kepercayaan workspace untuk folder asal file agent. Sesi -p tidak dihitung sebagai menerimanya. Apa yang berjalan sebelum Anda memercayai folder membandingkan hal ini dengan aturan file pengaturan, dan halaman subagent mencantumkan cakupan mana yang dikecualikan.
Batasan pada hook skill
Ketika hook frontmatter skill tidak dipicu setelah Anda atau Claude memanggil skill, jalankan/hooks dalam sesi tersebut. Jika hook tersebut tercantum, lihat Hook tidak dipicu. Jika tidak ada satu pun yang tercantum, skill mungkin berada di bawah penguncian khusus plugin atau melewati batas hook, dan berjalan tanpa hook frontmatter apa pun. Periksa setiap kasus:
- Penguncian khusus plugin: administrator organisasi Anda dapat mengunci hook ke plugin dan pengaturan terkelola dengan pengaturan terkelola
strictPluginOnlyCustomization. Skill mempertahankan hook frontmatter-nya di bawah penguncian tersebut hanya jika skill tersebut adalah skill plugin, skill bawaan, atau skill di direktori pengaturan terkelola. Hook dalam file pengaturan Anda sendiri juga tidak berjalan.- Periksa: ketik
/statusdalam sesi Claude Code. Jika barisSetting sourcesmenyertakanEnterprise managed settings, tanyakan kepada administrator Anda apakahstrictPluginOnlyCustomizationmengunci hook. - Perbaiki: pindahkan skill ke dalam plugin, karena skill plugin mempertahankan hook frontmatter-nya. Cantumkan plugin dalam marketplace, lalu instal dari sana. Jika Claude Code menolak marketplace dengan
is blocked by enterprise policy, minta administrator Anda untuk mengizinkan marketplace tersebut.
- Periksa: ketik
- Batas hook: di Claude Code v2.1.304 atau yang lebih baru, setiap sumber skill memiliki batasnya sendiri per sesi. Proyek Anda, folder skill pribadi Anda, dan setiap plugin dihitung sebagai sumber terpisah. Skill dari satu sumber dapat mendaftarkan paling banyak 1.024 hook. Matcher hook tersebut dapat berjumlah total paling banyak 16.384 karakter. Matcher dengan beberapa hook di bawahnya dihitung sekali untuk setiap hook.
- Periksa: ulangi sesi di mana hook tidak dipicu, dengan logging debug aktif:
- Mulai sesi baru dari shell Anda dengan
claude --debug. Jika Anda tidak memulai sesi dari shell, ketik/debugterlebih dahulu dalam sesi baru sebagai gantinya. - Panggil skill yang sama dalam urutan yang sama, termasuk yang dipanggil Claude, karena kedua total terakumulasi skill demi skill. Minta Claude untuk memanggil skill apa pun yang tidak dapat Anda ketik sebagai perintah.
- Cari
registers none of its hooksdi log debug. Baris yang berisi teks tersebut menyebutkan skill yang melewati batas.
- Mulai sesi baru dari shell Anda dengan
- Perbaiki: deklarasikan lebih sedikit hook atau matcher yang lebih pendek dalam skill tersebut atau dalam skill lain dari sumbernya, lalu mulai sesi baru. Jika skill tersebut tidak dapat Anda edit, minta penulisnya untuk melakukan perubahan tersebut. Sampai saat itu, mulai sesi baru dan panggil skill yang Anda perlukan sebelum skill lain dari sumbernya.
- Periksa: ulangi sesi di mana hook tidak dipicu, dengan logging debug aktif:
Menu /hooks
Ketik /hooks di Claude Code untuk membuka browser read-only untuk hook yang telah Anda konfigurasikan. Daftar tersebut memberi label pada setiap hook dengan asalnya, seperti pengaturan pengguna, pengaturan proyek, pengaturan lokal, plugin, atau sesi saat ini.
Pilih hook untuk melihat teks lengkap dari apa yang dijalankannya dan di mana hook tersebut didefinisikan, seperti path file pengaturannya atau nama plugin-nya.
Untuk menelusuri semua event hook, termasuk yang tidak memiliki hook terkonfigurasi, pilih All events di akhir daftar.
Menonaktifkan atau menghapus hook
Untuk menghapus hook yang didefinisikan dalam file pengaturan, hapus entrinya dari file tersebut. Untuk menonaktifkan sementara semua hook tanpa menghapusnya, setel"disableAllHooks": true dalam file pengaturan Anda. Claude Code membaca nilai yang tersisa setelah prioritas pengaturan diterapkan, sehingga "disableAllHooks": false dalam .claude/settings.json proyek menimpa true dalam pengaturan pengguna Anda. Untuk mematikan hook selama satu eksekusi apa pun isi pengaturan proyek, teruskan --settings '{"disableAllHooks": true}', yang diutamakan daripada pengaturan proyek dan lokal. Tidak ada cara untuk menonaktifkan hook individual sambil tetap mempertahankannya dalam konfigurasi.
Pengaturan disableAllHooks menghormati hierarki pengaturan terkelola. Jika administrator telah mengonfigurasi hook melalui pengaturan kebijakan terkelola, disableAllHooks yang disetel dalam pengaturan pengguna, proyek, atau lokal tidak dapat menonaktifkan hook terkelola tersebut. Hanya disableAllHooks yang disetel di tingkat pengaturan terkelola yang dapat menonaktifkan hook terkelola. Untuk jangkauan lengkap setiap tingkat, lihat disableAllHooks.
Pengeditan langsung pada hook dalam file pengaturan biasanya terdeteksi secara otomatis oleh file watcher.
Input dan output hook
Hook perintah menerima data JSON melalui stdin dan menyampaikan hasil melalui exit code, stdout, dan stderr. Hook HTTP menerima JSON yang sama sebagai body permintaan POST dan menyampaikan hasil melalui body respons HTTP. Bagian ini membahas field dan perilaku yang umum untuk semua event. Bagian setiap event di bawah Event hook mencakup skema input spesifik dan opsi kontrol keputusannya. Di macOS dan Linux, hook perintah berjalan dalam sesinya sendiri tanpa terminal pengendali. Proses hook dan proses turunannya tidak dapat membuka/dev/tty atau mengirim escape sequence secara langsung ke antarmuka Claude Code. Windows tidak memiliki /dev/tty.
Untuk menampilkan pesan kepada pengguna di platform apa pun, kembalikan systemMessage dalam output JSON. Beberapa event membuangnya atau mengirimkannya ke tempat lain, dan bagian setiap event menyebutkannya. Untuk memicu notifikasi desktop, mengatur judul jendela, atau membunyikan bel, kembalikan terminalSequence sebagai gantinya.
Field input umum
Event hook menerima field berikut sebagai JSON, selain field khusus event yang didokumentasikan di setiap bagian event hook. Untuk hook perintah, JSON ini tiba melalui stdin. Untuk hook HTTP, JSON ini tiba sebagai body permintaan POST.agent_id dan agent_type memberi tahu skrip Anda di agent mana hook dijalankan, seperti subagent, rekan tim in-process, atau agent yang Anda pilih dengan --agent:
Hanya hook
SessionStart yang dapat menerima field model, dan Claude Code tidak selalu menyertakannya. Hook PreModelSwitch dan PostModelSwitch menerima from_model dan to_model sebagai gantinya, jadi gunakan hook PostModelSwitch untuk mengikuti model saat model berubah selama sesi.
Tidak ada environment variable $CLAUDE_MODEL. Hook dapat membaca $ANTHROPIC_MODEL jika Anda mengaturnya di shell Anda, tetapi nilai tersebut tidak berubah ketika Anda mengganti model dengan /model selama sesi.
Proses hook mewarisi environment induk, kecuali variabel exporter OTEL_* yang dihapus Claude Code dari setiap subproses yang dijalankannya dan, ketika CLAUDE_CODE_SUBPROCESS_ENV_SCRUB diatur ke 1, variabel-variabel yang dihapusnya. Dalam sesi yang mendapatkan konfigurasi HIPAA, Claude Code juga menghapus kredensial Anthropic dari environment hook.
Misalnya, hook PreToolUse untuk perintah Bash menerima ini di stdin:
tool_name, tool_input, dan tool_use_id bersifat khusus event. Setiap bagian event hook mendokumentasikan field tambahan untuk event tersebut.
Output exit code
Ketika eksekusi hook Anda selesai, Claude Code menggunakan exit code-nya untuk memutuskan apakah akan melanjutkan tindakan yang memicunya, seperti panggilan tool atau prompt:- Berhasil: hook Anda keluar dengan 0. Claude Code menerapkan field output JSON apa pun yang dicetak hook Anda, dan tindakan dilanjutkan kecuali field tersebut memblokir atau menolaknya. Hook yang memulai proses latar belakang membahas pengecualiannya.
- Error blocking: hook Anda keluar dengan 2. Pada event yang dapat memblokir, Claude Code menghentikan tindakan.
- Error non-blocking: hook Anda keluar dengan kode lain, atau gagal dengan cara lain, seperti tidak dapat dimulai atau mencetak JSON yang tidak valid. Tindakan dilanjutkan, kecuali untuk hook yang kegagalannya memblokir tindakan, misalnya hook dengan
onFailurediatur ke"block". Pada event sepertiPreToolUse, Anda melihat pemberitahuan<hook name> hook errordi transkrip.
PreToolUse keluar dengan 1 tetapi mencetak JSON yang lolos validasi, JSON tersebut tetap dapat menentukan apa yang terjadi. Untuk menemukan hasil hook Anda pada event seperti PreToolUse, cocokkan apa yang dicetaknya ke stdout di kolom pertama dengan exit code-nya di bagian atas:
Beberapa event memiliki aturannya sendiri:
WorktreeCreate: exit code bukan nol apa pun membuat pembuatan worktree gagal, apa pun isi JSON Anda.WorktreeRemove: exit code bukan nol apa pun membuat penghapusan worktree gagal jika direktori masih ada setelahnya.Stop,SubagentStop,TaskCompleted, dan hookUserPromptSubmitdari plugin: ketika hook Anda keluar dengan 2 tanpa apa pun di stdout dan stderr-nya menyatakan bahwa suatu file tidak ada, sepertiNo such file or directory, Claude Code memperlakukan eksekusi tersebut sebagai error non-blocking.ElicitationdanElicitationResult: Claude Code menerapkanhookSpecificOutputAnda ketika hook Anda keluar dengan 0, dan mengabaikannya pada exit code lainnya.- Event yang membuang output hook, seperti
StopFailure: Claude Code mengabaikan JSON Anda pada setiap exit code, kecuali field efek samping sepertiterminalSequence, yang tetap dijalankan.
Exit code 0
Exit 0 berarti berhasil, dan merupakan exit code yang dimaksudkan ketika Anda mencetak JSON untuk kontrol terstruktur. Ketika hook Anda keluar dengan 0 tetapi mencetak JSON yang tidak dapat di-parse atau gagal validasi skema, tindakan dilanjutkan kecuali hook tersebut adalah hook yang kegagalannya memblokir tindakan. Alih-alih berhasil dengan field Anda diterapkan, eksekusi tersebut menjadi error non-blocking. Untuk memperbaiki output Anda, gunakan pesan parse atau validasi yang dimuat oleh pemberitahuan<hook name> hook error. Untuk hook yang tidak async, stdout Anda dihitung sebagai output JSON hanya ketika Anda mencetak satu objek JSON, dalam satu baris atau beberapa baris, tanpa apa pun di sekitarnya selain whitespace. Claude Code membaca stdout lainnya sebagai teks biasa atau kegagalan parse:
- Output yang tidak diawali dengan
{, atau yang diawali dengan{tetapi tidak diakhiri dengan}: teks biasa. Array JSON dan string JSON yang diberi tanda kutip merupakan teks biasa menurut aturan ini. - Dua baris atau lebih yang masing-masing dapat di-parse sebagai JSON secara terpisah, dengan baris pertama diawali
{dan baris terakhir diakhiri}: teks biasa ketika tidak ada baris yang merupakan objek output JSON yang mengatur suatu field. Ketika ada baris yang demikian, output tersebut adalah kegagalan parse, jadi cetak objek tersebut tanpa apa pun lainnya di stdout. - Apa pun lainnya yang diawali dengan
{dan diakhiri dengan}tetapi bukan JSON yang valid: kegagalan parse.
UserPromptSubmit,UserPromptExpansion,SessionStart, danPostModelSwitch: Claude Code menambahkan stdout teks biasa sebagai konteks yang dapat dilihat dan ditindaklanjuti oleh Claude. Claude Code tidak menambahkan stdout yang gagal di-parse.PreCompact: stdout Anda menjadi instruksi untuk ringkasan compaction
PreToolUse, aktifkan logging debug. Untuk menyampaikan peringatan kepada Claude dari hook PostToolUse atau PostToolUseFailure, gunakan exit 2 sehingga Claude melihat stderr meskipun tool sudah berjalan.
Exit code 2
Keluar dengan kode 2 untuk memblokir tindakan. Pada event yang dapat memblokir, Claude Code menghentikan tindakan: misalnya, hookPreToolUse memblokir panggilan tool, dan hook UserPromptSubmit menolak prompt.
Pesan yang menyertai pemblokiran adalah stderr hook Anda. Jika hook Anda juga mencetak JSON yang membuat keputusan pemblokiran, Claude Code menggunakan alasan dari keputusan tersebut sebagai gantinya.
Exit 2 memblokir bahkan ketika hook Anda mencetak JSON:
- JSON yang lolos validasi skema: Claude Code tetap membaca field output JSON, tetapi field tersebut tidak dapat menimpa pemblokiran. Bahkan
permissionDecisionbernilai"allow"tidak membuat tindakan diloloskan. PadaElicitationdanElicitationResult,hookSpecificOutputdari hook yang keluar dengan exit 2 diabaikan. - JSON yang gagal validasi skema: hook tetap memblokir. Claude Code menggunakan stderr Anda sebagai alasan pemblokiran dan mencatat kegagalan validasi di log debug.
rm dengan keluar menggunakan 2 dan menyerahkan setiap perintah lain ke alur izin normal:
PreToolUse pada Bash, perintah yang diawali dengan rm diblokir, dan Claude menerima stderr hook sebagai error tool, dengan awalan nama event, nama tool, dan perintah hook:
Exit code lainnya
Pemberitahuan<hook name> hook error dengan Failed with non-blocking status code: berarti exit code hook Anda bukan 0 atau 2 dan stdout-nya berupa teks biasa atau kosong. Kecuali hook tersebut adalah hook yang kegagalannya memblokir tindakan, ini adalah error non-blocking, sehingga hook Anda tidak menghentikan tindakan. Untuk menangkap stderr lengkap hook Anda alih-alih hanya baris pertama yang ditampilkan pemberitahuan, aktifkan logging debug.
Misalnya, ketika hook PreToolUse pada Bash mencetak something broke ke stderr dan keluar dengan 1, pemberitahuan PreToolUse:Bash hook error memuat baris ini:
No such file or directory dari shell, seperti pada baris berikut, hook tidak pernah berjalan, misalnya karena path skripnya tidak ada:
settings.json berarti hook tidak pernah berjalan. Untuk memblokir tindakan ketika hook tidak dapat dimulai, atur onFailure: "block".
Timeout
Selain hook perintah yang Anda jalankan denganasync: true, Claude Code membatalkan hook command, http, atau mcp_tool yang mencapai timeout-nya, membuang output hook, sehingga pada sebagian besar event hook yang mengalami timeout tidak memberikan keputusan. Pada PreModelSwitch, hook yang dibatalkan karena timeout memblokir pergantian model.
Pada PreToolUse, hook command, http, atau mcp_tool yang mengalami timeout tidak memblokir panggilan tool, kecuali untuk hook yang kegagalannya memblokir tindakan. Panggilan berlanjut melalui alur izin normal, jadi jangan mengandalkan hook yang macet untuk berfungsi sebagai gerbang. Untuk memblokir panggilan ketika hook command atau http mengalami timeout, atur onFailure: "block". Callback hook Agent SDK yang melebihi timeout-nya memblokir panggilan tool.
Jika Anda menggunakan Code Mode, timeout pada
PreToolUse bekerja secara berbeda. Lihat Panggilan tool ditolak oleh hook.Hook yang memulai proses latar belakang
Hook dapat memulai program yang berjalan lama, seperti server dev, dan membiarkannya tetap berjalan selama sesi berlanjut. Dalam skrip hook atau perintah bentuk shell:- Jalankan program di latar belakang
- Alihkan stdout dan stderr-nya. Misalnya, perintah Bash
npm run dev > dev-server.log 2>&1 &mengirim keduanya ke file. Jika Anda tidak mengalihkannya, hook yang tidak async memerlukan waktu hingga 10 detik untuk selesai setelah prosesnya sendiri keluar.
stdio went quiet dan refusing to honor the suspect capture.
Skrip hook PreToolUse ini mencetak keputusan dengan policy-check.sh, lalu mengunggah metrik di latar belakang:
|| exit $?: menjadikan exit code pemeriksaan sebagai exit code hook itu sendiri, sehingga pemeriksaan yang keluar dengan 2 untuk menolak tetap memblokir. Jika tidak, hook keluar dengan kode dari baris terakhir, yaitu 0.>/dev/null 2>&1 &: menjalankanupload-metrics.shdi latar belakang dengan output yang dialihkan, sehingga proses tersebut tidak membuat output hook tetap terbuka.
policy-check.sh keluar dengan 2, lalu picu panggilan tool yang cocok dengan hook. Panggilan tool diblokir.
Memblokir tindakan ketika hook gagal
Pada sebagian besar event, ketika hook gagal atau mengalami timeout, Claude Code tetap menjalankan tindakan. Hook kebijakan dengan path yang salah atau skrip yang crash kemudian meloloskan semuanya. Untuk memblokir tindakan sebagai gantinya, atur"onFailure": "block" pada hook command atau http. Memerlukan Claude Code v2.1.295 atau lebih baru. Untuk callback hook Agent SDK, lihat Memblokir tindakan ketika callback gagal.
Di Windows tanpa Git Bash, hook perintah
PreToolUse atau PermissionRequest berbentuk shell yang gagal dengan onFailure tidak diatur dapat memblokir panggilan tool sebagai gantinya.PreToolUse di .claude/settings.json ini menjalankan skrip proyek sebelum setiap perintah Bash, dan memblokir perintah jika skrip gagal:
check-command.js tidak ada dan minta Claude menjalankan perintah Bash seperti ls. Claude Code memblokir panggilan, dan error-nya menyertakan failed; blocking because onFailure is "block" diikuti output error node itu sendiri, yang dipangkas di sini menjadi satu baris:
timed out alih-alih failed. Tanpa onFailure diatur, skrip yang sama yang tidak ada adalah error non-blocking dan ls berjalan.
Setiap hal berikut dihitung sebagai kegagalan:
- Tidak dapat dimulai: hook perintah gagal dimulai, misalnya karena skrip atau executable tidak ada
- Exit code selain 0 atau 2: berlaku untuk hook perintah meskipun hook tersebut mencetak JSON yang mengizinkan tindakan, seperti
permissionDecision: "allow". Untuk mengembalikan keputusan JSON, keluar dengan 0 - Error HTTP: koneksi hook HTTP gagal, atau status respons bukan 2xx
- Timeout: hook mencapai
timeout-nya - Output tidak valid: output JSON tidak dapat di-parse atau gagal validasi skema. Untuk hook HTTP, body 2xx yang bukan kosong dan bukan objek JSON juga dihitung. Stdout teks biasa dari hook perintah bukan kegagalan
"block" diatur, kegagalan melakukan apa yang dilakukan exit code 2 pada event tersebut, kecuali pada PermissionRequest, di mana kegagalan menolak permintaan. Misalnya, kegagalan PreToolUse memblokir panggilan tool dan kegagalan UserPromptSubmit memblokir prompt.
Field ini tidak berpengaruh pada hook berikut:
- Hook
Stop,SubagentStop,TaskCompleted, danTeammateIdle: exit code 2 pada event ini mengirim Claude kembali untuk terus bekerja, dan Claude tidak dapat memperbaiki hook yang tidak mau berjalan - Hook perintah latar belakang: hook perintah yang mengatur
asyncatauasyncRewake
Jika Anda menggunakan Code Mode, kegagalan pada
PreToolUse bekerja secara berbeda. Lihat Panggilan tool ditolak oleh hook.Hook yang gagal di Windows tanpa Git Bash
Jika error untuk panggilan tool yang diblokir menyertakan teks ini, hook perintahPreToolUse atau PermissionRequest gagal atau mengalami timeout dan memblokir panggilan tersebut:
PowerShell started no program, mulailah dengan perbaikan untuk command yang hanya berisi string bertanda kutip, yang dicetak PowerShell alih-alih dijalankan.
Tanpa Git Bash, exit code apa pun selain 0 atau 2 dari hook tersebut dihitung sebagai kegagalan, bahkan ketika hook mencetak JSON yang tidak menolak panggilan. Untuk mengetahui mengapa hook gagal, periksa log debug. Lalu pilih perbaikan yang sesuai dengan hook Anda:
- Panggilan sebaiknya dilanjutkan ketika hook gagal: tambahkan
"onFailure": "continue"ke hook - Hook keluar dengan 1 untuk memberi peringatan tetapi harus memblokir ketika crash: dengan
"onFailure": "continue", panggilan dilanjutkan pada exit code 1, baik itu peringatan maupun crash. Tambahkan"onFailure": "block"sebagai gantinya, dan akhiri skrip dengan exit code 0 di bagian yang memberi peringatan, sehingga panggilan tersebut dilanjutkan. Untuk tetap menampilkan peringatan padaPreToolUse, cetak peringatan ke stdout sebagai fieldsystemMessagedari satu objek JSON. - Hook ditulis untuk Bash: tanpa Git Bash, hook berbentuk shell tanpa
shellyang diatur berjalan di PowerShell, tempat hook tersebut dapat gagal. Instal Git for Windows, atau aturCLAUDE_CODE_GIT_BASH_PATHjika Git Bash terinstal di lokasi yang tidak ditemukan Claude Code. Lalu mulai sesi baru. - Hook menjalankan satu
.exe, bukan file.cmdatau.bat: letakkan program dicommanddan argumennya diargsuntuk menggunakan bentuk exec, lalu tambahkan"onFailure": "block". Claude Code kemudian memulai program tanpa shell. Tanpa"onFailure": "block", panggilan dapat dilanjutkan ketika program gagal, tidak ada, atau mengalami timeout - Hook penjaga PowerShell yang harus memblokir saat gagal: dengan
onFailuretidak diatur, hook ini berhenti memblokir setelah Git for Windows terinstal dan Claude Code menemukan Git Bash. Tambahkan"onFailure": "block"sehingga kegagalan atau timeout memblokir panggilan dengan atau tanpa Git Bash. Atur juga"shell": "powershell"sehingga hook tetap berjalan di PowerShell sejak saat itu alih-alih di Git Bash
blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.
PowerShell started no program
Hook dengan error ini berjalan di PowerShell, yang mencetakcommand yang hanya berisi string bertanda kutip, seperti "command": "\"C:\\Tools\\guard.exe\"", alih-alih menjalankannya.
- Untuk mencetak teks, seperti JSON: atur
"shell": "powershell", dan hook keluar dengan 0 - Untuk menjalankan file
.ps1,.cmd, atau.bat, atau.exeyang path-nya memiliki variabel: atur"shell": "powershell"dan letakkan&sebelum path yang diberi tanda kutip. Biarkan$HOMEseperti yang tertulis, dan tuliskan environment variable seperti contohcheck.ps1menuliskan$env:CLAUDE_PROJECT_DIR - Untuk menjalankan satu
.exepada path tanpa$: hapus tanda kutip bagian dalam daricommand, lalu terapkan perbaikan untuk hook yang menjalankan satu.exe, dengan menulis"args": []jika program tidak menerima argumen - Untuk menjalankan skrip yang ditulis untuk Bash: terapkan perbaikan untuk hook yang ditulis untuk Bash
Perilaku exit code 2 per event
Exit code 2 adalah cara hook memberi sinyal “berhenti, jangan lakukan ini.” Efeknya bergantung pada event, karena beberapa event mewakili tindakan yang dapat diblokir (seperti panggilan tool yang belum terjadi) dan yang lain mewakili hal-hal yang sudah terjadi atau tidak dapat dicegah.
Untuk
SessionStart, SubagentStart, dan PostModelSwitch, Claude Code menampilkan stderr exit code 2 di transkrip sebagai pemberitahuan <hook name> hook error, dengan cara yang sama seperti menampilkan error non-blocking. Claude tidak melihatnya, dan sesi atau subagent berlanjut. Untuk SubagentStart, pemberitahuan muncul di transkrip subagent itu sendiri, bukan di percakapan induk.
Penanganan respons HTTP
Hook HTTP menggunakan kode status HTTP dan body respons alih-alih exit code dan stdout. Untuk memblokir tindakan ketika permintaan gagal atau mengembalikan status non-2xx, aturonFailure: "block". Untuk memblokir panggilan tool atau menolak izin, kembalikan respons 2xx dengan body JSON yang berisi field keputusan yang sesuai.
Setelah error non-blocking, tindakan dilanjutkan kecuali hook tersebut adalah hook yang kegagalannya memblokir tindakan. Kecuali pada event dengan kontrak kegagalannya sendiri dalam tabel per event, seperti WorktreeCreate, Anda mendapatkan hasil berikut untuk setiap respons atau kegagalan:
- 2xx dengan body kosong: berhasil, setara dengan exit code 0 tanpa output
- 2xx dengan body objek JSON: di-parse menggunakan skema output JSON yang sama seperti hook perintah. Body yang gagal validasi skema adalah error non-blocking
- 2xx dengan body lain, seperti teks biasa: error non-blocking, ditangani sama seperti status non-2xx. Claude Code tidak menambahkan teks tersebut ke konteks Claude
- Status non-2xx: error non-blocking
- Kegagalan koneksi: error non-blocking
- Timeout: hook dibatalkan, seperti dijelaskan di bagian Timeout
Output JSON
Exit code hanya memungkinkan Anda memblokir atau tetap diam, tetapi output JSON memberi Anda kontrol yang lebih terperinci. Alih-alih keluar dengan kode 2 untuk memblokir, keluar dengan 0 dan cetak objek JSON ke stdout. Claude Code membaca field tertentu dari JSON tersebut untuk mengontrol perilaku, termasuk kontrol keputusan untuk memblokir, mengizinkan, atau mengeskalasi ke pengguna.Pilih satu pendekatan per hook: gunakan exit code saja untuk memberi sinyal, atau keluar dengan 0 dan cetak JSON untuk kontrol terstruktur. Jika Anda mencampurnya, exit 2 tetap mempertahankan efek pemblokirannya, dan Claude Code tetap membaca field JSON, dengan satu pengecualian elicitation yang dicatat di bagian Exit code 2.
additionalContext, systemMessage, dan initialUserMessage dari hook, serta stdout biasanya, dibatasi hingga 10.000 karakter:
- Cakupan: Claude Code mengukur setiap string secara terpisah, bahkan ketika beberapa hook berjalan untuk event yang sama. Untuk output JSON, setiap field diukur secara terpisah; stdout biasa diukur secara keseluruhan.
- Melebihi batas: kecuali untuk stdout PreCompact, Claude Code menyimpan output ke file di direktori sesi. Claude Code mengganti output tersebut dengan path file serta pratinjau hingga 2.000 karakter pertama. Hasil Bash valid yang besar ditangani dengan cara yang sama, seperti dijelaskan di bagian Batas output. Tidak seperti batas atas Bash tersebut, batas ini tidak memiliki pengaturan atau environment variable untuk menaikkannya.
- Membaca file: Claude Code tidak meminta Claude untuk membaca file tersebut, jadi pastikan apa pun yang harus selalu dilihat Claude tetap berada dalam batas.
- Field universal seperti
continuetercantum dalam tabel di bawah. Setiap event menerimanya, tetapi beberapa event membuangnya atau mengirimkansystemMessageke tempat selain transkrip. Bagian setiap event menyebutkannya.terminalSequencejuga berfungsi pada event tersebut, dengan pengecualian yang tercantum di bagian Memancarkan notifikasi terminal. decisiondanreasontingkat atas digunakan oleh beberapa event untuk memblokir atau memberikan umpan balik.hookSpecificOutputadalah objek bersarang untuk event yang memerlukan kontrol lebih kaya. Objek ini memerlukan fieldhookEventNameyang diatur ke nama event.
Untuk menghentikan Claude sepenuhnya:
PreToolUse dan PostToolUse, penghentian berlaku bahkan ketika panggilan tool gagal atau selesai saat Claude masih melakukan streaming respons.
Memancarkan notifikasi terminal
Hook berjalan tanpa terminal pengendali, sehingga menulis escape sequence langsung ke/dev/tty akan gagal. Sebagai gantinya, kembalikan escape sequence di field terminalSequence dan Claude Code memancarkannya untuk Anda melalui jalur penulisan terminalnya sendiri. Cara ini bebas race condition dan berfungsi di Windows yang tidak memiliki /dev/tty.
Field ini menerima string berisi satu atau lebih escape sequence yang ada di allowlist:
- OSC
0,1,2: judul jendela dan ikon - OSC
9: notifikasi iTerm2, ConEmu, Windows Terminal, dan WezTerm, termasuk progres taskbar9;4 - OSC
99: notifikasi Kitty - OSC
777: notifikasi urxvt, Ghostty, dan Warp - BEL tunggal
systemMessage dan continue, seperti Notification dan StopFailure. Field ini memiliki beberapa batasan:
- Claude Code menulis sequence hanya dalam sesi interaktif, dan hanya selama antarmukanya terbuka. Dalam mode non-interaktif dengan flag
-pdan di Agent SDK, Claude Code mengabaikan field ini. - Field ini hanya berfungsi dari hook yang tidak berjalan di latar belakang, jadi biarkan
asyncdanasyncRewakenonaktif. - Hook perintah
WorktreeCreatetidak dapat mengembalikan JSON, karena Claude Code membaca stdout-nya sebagai path worktree. Hook HTTPWorktreeCreatemengembalikan JSON dan dapat menyertakan field ini.
Notification di terminal yang membaca OSC 777, seperti Ghostty atau Warp. Pemanggilan jq -nc --arg dalam skrip membuat output JSON dan meng-escape tanda kutip, backslash, dan baris baru apa pun dalam pesan notifikasi:
{ "terminalSequence": "..." } sama dari shell atau bahasa apa pun.
Mendapatkan notifikasi terminal di dalam tmux atau GNU screen
Ketika Claude Code berjalan di dalam tmux atau GNU screen, sequence OSC memerlukan hal berikut agar sampai ke aplikasi terminal Anda:- tmux 3.3 atau lebih baru: tambahkan
set -g allow-passthrough onke~/.tmux.conf, lalu jalankantmux source-file ~/.tmux.confdari shell. Opsi ini berlaku untuk setiap program di pane tersebut, bukan hanya Claude Code. Denganon, hanya pane yang sedang ditampilkan tmux yang dapat mengirimkannya. Agar pane lain dalam sesi yang Anda lampirkan juga dapat mengirimkannya, gunakanallsebagai penggantion, pada tmux 3.4 atau lebih baru. - GNU screen: biarkan jendela Claude Code tetap ditampilkan di screen, karena sequence dari jendela latar belakang tidak sampai ke aplikasi terminal Anda
Menambahkan konteks untuk Claude
FieldadditionalContext meneruskan string dari hook Anda ke context window Claude. Claude Code membungkus string tersebut dalam system reminder dan menyisipkannya ke dalam percakapan pada titik di mana hook dijalankan. Claude membaca reminder tersebut pada permintaan model berikutnya, tetapi reminder tidak muncul sebagai pesan chat di antarmuka.
Kembalikan additionalContext di dalam hookSpecificOutput bersama nama event:
- SessionStart dan SubagentStart: di awal percakapan, sebelum prompt pertama
- UserPromptSubmit dan UserPromptExpansion: bersama prompt yang dikirim
- PreToolUse, PostToolUse, PostToolUseFailure, dan PostToolBatch: di samping hasil tool
- Stop dan SubagentStop: di akhir giliran. Percakapan berlanjut sehingga Claude dapat menindaklanjuti umpan balik. Lihat Kontrol keputusan Stop
- PostModelSwitch: bersama permintaan berikutnya setelah pergantian. Lihat Kontrol keputusan PostModelSwitch untuk waktunya
additionalContext untuk event yang sama, Claude menerima semua nilainya.
Jika string Anda berisi tag <system-reminder> atau </system-reminder>, Claude menerima string tersebut dengan < pada tag itu diganti dengan <.
Jika suatu nilai melebihi 10.000 karakter, Claude Code menulis teks tersebut ke file di direktori sesi dan sebagai gantinya meneruskan path file kepada Claude beserta pratinjau hingga 2.000 karakter pertama. Claude dapat membaca file tersebut, tetapi Claude Code tidak memintanya untuk melakukannya.
Gunakan additionalContext untuk informasi yang perlu diketahui Claude tentang status environment Anda saat ini atau operasi yang baru saja dijalankan:
- Status environment: branch saat ini, target deployment, atau feature flag yang aktif
- Aturan proyek bersyarat: perintah pengujian mana yang berlaku untuk file yang baru saja diedit, direktori mana yang read-only di worktree ini
- Data eksternal: issue terbuka yang ditugaskan kepada Anda, hasil CI terbaru, konten yang diambil dari layanan internal
bun test” dibaca sebagai informasi proyek. Teks yang dibingkai sebagai perintah sistem di luar jalur dapat memicu pertahanan prompt injection Claude, yang menyebabkan Claude menampilkan teks tersebut kepada Anda alih-alih memperlakukannya sebagai konteks.
Claude Code menyimpan teks yang disisipkan di transkrip sesi. Untuk event di tengah sesi seperti PostToolUse atau UserPromptSubmit, ketika Anda melanjutkan dengan --continue atau --resume, Claude Code memutar ulang teks yang tersimpan alih-alih menjalankan ulang hook untuk giliran sebelumnya, sehingga nilai seperti timestamp atau SHA commit menjadi usang. Hook SessionStart berjalan lagi saat dilanjutkan dengan source diatur ke "resume", atau "fork" jika Anda menambahkan --fork-session, sehingga hook tersebut dapat memperbarui konteksnya.
Kontrol keputusan
Tidak setiap event mendukung pemblokiran atau pengendalian perilaku melalui JSON. Event yang mendukungnya masing-masing menggunakan kumpulan field yang berbeda untuk menyatakan keputusan tersebut. Gunakan tabel ini sebagai referensi cepat sebelum menulis hook:
Beberapa event juga dapat menulis ulang konten alih-alih hanya mengizinkan atau memblokirnya:
PreToolUse:updatedInputlangsung di bawahhookSpecificOutputmenggantikan argumen tool sebelum tool berjalan. Lihat Kontrol keputusan PreToolUsePermissionRequest:updatedInputdi dalam objekdecision. Lihat Kontrol keputusan PermissionRequestPostToolUse:updatedToolOutputmenggantikan hasil tool. Dalam sesi cloud Cowork, Claude Code mengabaikanupdatedToolOutputketika tool-nya adalahAskUserQuestionatauExitPlanMode, dan Claude membaca jawaban atau rencana aslinya. Lihat Kontrol keputusan PostToolUseUserPromptSubmit: tidak dapat menggantikan prompt; hanya menyisipkanadditionalContextbersamanya
PreToolUse untuk input tool keluar dan di PostToolUse untuk hasil tool masuk.
Berikut contoh setiap pola dalam praktik:
- Keputusan tingkat atas
- PreToolUse
- PermissionRequest
Satu-satunya nilai untuk
decision adalah "block". Untuk mengizinkan tindakan dilanjutkan, hilangkan decision dari JSON Anda, atau keluar dengan 0 tanpa JSON sama sekali:Event hook
Setiap event berkaitan dengan satu titik dalam siklus hidup Claude Code tempat hook dapat dijalankan. Bagian-bagian di bawah ini diurutkan sesuai siklus hidup tersebut: dari penyiapan sesi melalui agentic loop hingga akhir sesi. Setiap bagian menjelaskan kapan event dipicu, matcher apa yang didukungnya, input JSON yang diterimanya, dan cara mengontrol perilaku melalui output.SessionStart
Berjalan saat Claude Code memulai sesi baru atau melanjutkan sesi yang sudah ada. Berguna untuk memuat konteks pengembangan seperti issue yang ada atau perubahan terbaru pada codebase Anda, atau untuk menyiapkan environment variable. Untuk konteks statis yang tidak memerlukan skrip, gunakan CLAUDE.md sebagai gantinya. SessionStart berjalan di setiap sesi, jadi jaga agar hook ini tetap cepat. Hanya hooktype: "command" dan type: "mcp_tool" yang didukung. Lihat kolom hook tool MCP untuk mengetahui kapan hook mcp_tool berjalan.
Nilai matcher berkaitan dengan cara sesi dimulai:
Saat Anda memulai sesi interaktif, melanjutkan percakapan saat peluncuran dengan
--continue atau --resume, atau menjalankan /clear, hook SessionStart berjalan di latar belakang. Anda dapat langsung mengetik, dan percakapan yang Anda lanjutkan muncul tanpa menunggu hook. Respons pertama Claude tetap menunggu hook selesai, sehingga konteksnya sampai ke Claude.
Saat Anda berpindah percakapan dengan /resume di dalam sesi, perpindahan tersebut justru menunggu hook selesai. Jika Anda menjalankan /clear atau berpindah ke percakapan lain saat hook latar belakang masih berjalan, apa pun yang dikembalikannya tidak diterapkan ke sesi.
Penantian yang sama berlaku saat peluncuran, termasuk sesi yang dilanjutkan: prompt yang Anda kirim saat hook SessionStart masih berjalan tidak sampai ke Claude hingga hook tersebut selesai.
Selama salah satu penantian tersebut, tekan Esc untuk mengembalikan prompt ke input tanpa mengirimnya. Hook tetap berjalan.
Input SessionStart
Selain kolom input umum, hook SessionStart menerimasource serta secara opsional model, agent_type, dan session_title:
Sesi yang belum Anda beri nama tetap dapat memiliki judul yang dihasilkan. Judul tersebut bukan judul kustom dan tidak muncul di
session_title.
Saat source bernilai "resume" atau "fork" dan transkrip berisi setidaknya satu respons dari Claude, hook SessionStart juga menerima empat kolom di bawah ini. Hook Anda dapat menggunakannya untuk melaporkan biaya melanjutkan percakapan yang sudah lama sebelum permintaan pertama, misalnya dalam systemMessage. Kolom-kolom ini memerlukan Claude Code v2.1.251 atau lebih baru.
Contoh ini menunjukkan input untuk sesi yang dilanjutkan 90 menit setelah respons terakhirnya:
Kontrol keputusan SessionStart
Hook SessionStart dapat menambahkan konteks untuk Claude, menyediakan pesan pengguna pertama, menetapkan judul sesi, memantau file, dan memuat ulang skill. Kembalikan kolom untuk masing-masing, selain kolom output JSON yang tersedia untuk semua hook:
Output ini menambahkan konteks dan memberi nama sesi:
initialUserMessage atau sessionTitle, instal plugin sebelum sesi dimulai. Claude Code mengabaikan kedua kolom tersebut dari plugin yang selesai diinstal setelah hook SessionStart berjalan.
Memuat ulang skill yang diinstal hook
Agar skill yang diinstal oleh hook SessionStart tersedia dalam sesi yang sama, kembalikanreloadSkills. Penemuan skill biasanya berjalan sebelum hook SessionStart selesai, sehingga tanpanya, file yang ditulis hook ke ~/.claude/skills/ atau .claude/skills/ dapat belum ada saat prompt pertama berjalan.
Contoh ini menyinkronkan repositori skill bersama dan meminta pemindaian ulang:
Mempertahankan environment variable
Hook SessionStart memiliki akses ke environment variableCLAUDE_ENV_FILE, yang menyediakan path file tempat Anda dapat mempertahankan environment variable untuk perintah shell yang dijalankan Claude nanti dalam sesi.
Untuk menetapkan environment variable satu per satu, tulis pernyataan export ke CLAUDE_ENV_FILE. Gunakan append (>>) untuk mempertahankan variabel yang ditetapkan oleh hook lain:
$PATH dalam export PATH="$PATH:./node_modules/.bin".
Variabel yang dipertahankan dalam perintah PowerShell
Perintah PowerShell juga menerima variabel dariCLAUDE_ENV_FILE, di Claude Code v2.1.296 atau lebih baru, tetapi PowerShell tidak pernah menjalankan file tersebut. Sebagai gantinya, Claude Code membaca penugasan dari file itu dan menyalinnya ke environment perintah PowerShell. Hal ini hanya dilakukan jika setiap baris merupakan salah satu dari berikut ini, mencakup semua yang ditulis oleh setiap hook dalam sesi ini dan skrip apa pun yang Anda tetapkan sebagai CLAUDE_ENV_FILE sebelum peluncuran:
- Baris kosong atau komentar
# - Satu penugasan di awal baris, ditulis
export NAME=value,declare -x NAME=value, atauNAME=value, dengan nilai yang akan digunakan Bash persis seperti yang ditulis, tersusun dari campuran bagian-bagian berikut: teks tanpa tanda kutip yang hanya menggunakan huruf, angka, dan karakter_ @ % + = : , . / -, teks dalam tanda kutip tunggal, dan teks dalam tanda kutip ganda yang setiap$, backtick, atau"di dalamnya di-escape dengan backslash
export PATH="$PATH:./node_modules/.bin" dengan $PATH yang tidak di-escape, perintah source, atau string $'...' yang dicetak oleh direnv export bash, perintah PowerShell tidak menerima satu pun variabel, dan claude --debug mencatat Session environment is not all plain assignments. Perintah Bash tetap menerima semuanya. Di Windows, perintah PowerShell juga tidak menerima variabel yang nilainya mengandung / atau \, karena Git Bash dan Windows menulis path secara berbeda. Perintah PowerShell yang berjalan dalam sandbox tidak menerima satu pun variabel.
CLAUDE_ENV_FILE tersedia untuk hook SessionStart, Setup, CwdChanged, dan FileChanged. Event hook lainnya tidak memiliki akses ke variabel ini, demikian pula hook yang berjalan di PowerShell, baik melalui "shell": "powershell" maupun secara default di Windows tanpa Git Bash.Setup
Hanya dipicu saat Anda meluncurkan Claude Code dengan--init-only, atau dengan --init atau --maintenance dalam mode non-interaktif dengan flag -p. Event ini tidak dipicu pada startup normal. Gunakan untuk instalasi dependensi satu kali atau pembersihan terjadwal yang Anda picu secara eksplisit dari CI atau skrip, terpisah dari startup sesi normal. Untuk inisialisasi per sesi, gunakan SessionStart sebagai gantinya.
Nilai matcher berkaitan dengan flag CLI yang memicu hook:
Saat Anda menjalankan
claude --init-only, Claude Code menjalankan hook Setup dan hook SessionStart dengan matcher startup, lalu keluar tanpa memulai percakapan.
Saat Anda memulai atau melanjutkan percakapan dengan -p, Anda juga perlu menyediakan prompt, sebagai argumen atau di-pipe melalui stdin. Anda dapat melewati prompt saat hook SessionStart menyediakan initialUserMessage atau saat Anda melanjutkan sesi dengan panggilan tool yang ditangguhkan.
Jika berhasil, --init-only tidak mencetak apa pun ke terminal. Untuk memastikan hook telah berjalan, mulai dengan claude --debug-file <path> --init-only, ganti <path> dengan lokasi file log, lalu periksa log untuk entri hook Setup dan SessionStart.
Karena Setup tidak dipicu di setiap peluncuran, plugin yang memerlukan dependensi terinstal tidak dapat hanya mengandalkan Setup. Pola praktisnya adalah memeriksa dependensi pada penggunaan pertama dan menginstalnya jika belum ada, misalnya hook atau skill yang memeriksa ${CLAUDE_PLUGIN_DATA}/node_modules dan menjalankan npm install jika tidak ada. Lihat direktori data persisten untuk tempat menyimpan dependensi yang diinstal. Jika Anda mendistribusikan plugin melalui marketplace, Anda mungkin tidak memerlukan pola ini: Claude Code menginstal dependensi paket Node.js yang memenuhi syarat secara otomatis saat melakukan cache plugin.
Input Setup
Selain kolom input umum, hook Setup menerima kolomtrigger yang bernilai "init" atau "maintenance":
Kontrol keputusan Setup
Hook Setup tidak dapat memblokir; eksekusi berlanjut pada exit code apa pun. Pada setiap exit code, Claude Code membuang kolom output JSON dari hook Setup, sepertisystemMessage, continue, dan hookSpecificOutput.additionalContext. Dengan -p, stdout, stderr, dan exit code hook Setup muncul dalam output eksekusi hanya sebagai event hook_response saat Anda meluncurkan dengan --output-format stream-json --verbose.
Hook Setup memiliki akses ke CLAUDE_ENV_FILE. Jika hook Anda menulis variabel ke file tersebut, variabel itu bertahan ke perintah Bash berikutnya dalam sesi, seperti pada hook SessionStart. Hanya hook type: "command" yang berjalan pada Setup. Hook type: "mcp_tool" pada Setup selalu dilewati, seperti yang dijelaskan di Field hook tool MCP.
InstructionsLoaded
Dipicu saat fileCLAUDE.md atau .claude/rules/*.md dimuat ke dalam konteks. Event ini dipicu di awal sesi untuk file yang dimuat secara langsung dan dipicu lagi nanti saat file dimuat secara lazy, misalnya saat Claude mengakses subdirektori yang berisi CLAUDE.md bersarang atau saat aturan kondisional dengan frontmatter paths: cocok. Hook ini tidak mendukung pemblokiran atau kontrol keputusan. Hook ini berjalan secara asinkron untuk tujuan observabilitas.
Event ini tidak dipicu saat Claude membaca AGENTS.md secara langsung melalui pengaturan Project instructions. Event ini dipicu saat CLAUDE.md mengimpor AGENTS.md Anda, dengan load_reason bernilai include seperti file impor lainnya, dan saat CLAUDE.md merupakan symlink ke file tersebut, sebagai pemuatan CLAUDE.md biasa.
Matcher dijalankan terhadap load_reason. Misalnya, gunakan "matcher": "session_start" agar hanya dipicu untuk file yang dimuat di awal sesi, atau "matcher": "path_glob_match|nested_traversal" agar hanya dipicu untuk pemuatan lazy.
Input InstructionsLoaded
Selain kolom input umum, hook InstructionsLoaded menerima kolom-kolom berikut:Kontrol keputusan InstructionsLoaded
Hook InstructionsLoaded tidak memiliki kontrol keputusan. Hook ini tidak dapat memblokir atau mengubah pemuatan instruksi. Claude Code membuang kolom output JSON miliknya, sepertisystemMessage dan continue. Gunakan event ini untuk audit logging, pelacakan kepatuhan, atau observabilitas.
UserPromptSubmit
Berjalan saat prompt dikirim, sebelum Claude memprosesnya. Ini memungkinkan Anda menambahkan konteks tambahan berdasarkan prompt/percakapan, memvalidasi prompt, atau memblokir jenis prompt tertentu. HookUserPromptSubmit tidak hanya dipicu pada prompt yang Anda ketik. Claude Code juga menjalankannya pada:
- Tugas terjadwal bercakupan sesi yang dipicu, termasuk iterasi
/loop - Subagent latar belakang yang melapor kembali ke sesi yang memulainya
- Pesan yang dikirim sesi lain ke percakapan utama Anda
- Output kode Claude dalam sesi Code Mode, untuk hook
command,http, danmcp_tool, saat kode dapat berjalan di latar belakang
UserPromptSubmit memiliki timeout default 30 detik untuk tipe command, http, dan mcp_tool, lebih singkat daripada default 600 detik untuk tipe-tipe tersebut pada sebagian besar event lainnya. Karena hook ini berjalan sebelum setiap prompt dan memblokir pemrosesan model hingga selesai, hook yang macet akan menghentikan sesi. Jika hook Anda memerlukan lebih banyak waktu, tetapkan kolom timeout dalam entri hook.
Selain hook perintah yang Anda jalankan dengan async: true, hook perintah, HTTP, atau tool MCP UserPromptSubmit yang mencapai timeout-nya akan dibatalkan dan output-nya, termasuk additionalContext apa pun, dibuang. Prompt tetap sampai ke Claude tanpa konteks tersebut. Untuk memblokir prompt sebagai gantinya, tetapkan onFailure: "block" pada hook perintah atau HTTP. Transkrip menampilkan pemberitahuan yang menyebutkan hook, timeout yang terpicu, dan bahwa output telah dibuang.
Hook callback Agent SDK pada UserPromptSubmit yang mencapai timeout-nya memblokir prompt dengan pesan yang menyebutkan nama hook dan timeout-nya, karena callback di sana dapat berfungsi sebagai gerbang kebijakan yang tidak boleh gagal secara terbuka. Sesi tetap berlanjut.
Esc selama pemeriksaan prompt
Jika Anda menekan Esc atau menginterupsi sesi saat hook masih memeriksa prompt, pemeriksaan berhenti dan prompt tidak sampai ke Claude. Teks yang Anda ketik di terminal kembali ke input prompt, dan hook memeriksanya lagi saat Anda mengirimnya ulang.
Hook dapat memblokir prompt pada saat yang sama dengan Anda menginterupsi. Prompt kemudian tetap terblokir: Anda melihat pesan blokir dari hook tersebut, dan teks tidak kembali ke input.
Input UserPromptSubmit
Selain kolom input umum, hook UserPromptSubmit menerima kolomprompt yang berisi teks yang dikirim. Konten tempelan yang diringkas menjadi placeholder [Pasted text #N] tiba dalam keadaan sudah diperluas di tempatnya. Dalam sesi tempat Claude Code menandai teks tempelan untuk Claude, konten yang diperluas tersebut berada di antara baris <pasted_content id="…"> dan baris </pasted_content id="…">, jadi perhitungkan baris-baris tersebut jika hook Anda mengurai prompt.
Hook UserPromptSubmit juga menerima session_title saat sesi memiliki judul kustom, dengan makna yang sama seperti kolom session_title SessionStart.
/deploy staging, hook menerima baris tersebut sebagaimana Anda mengetiknya, bukan konten skill yang diperluas. Jika hook memblokir prompt, perintah tidak dijalankan, sehingga perintah shell yang disisipkan skill dengan !`<command>` juga tidak dijalankan. Untuk mencocokkan nama atau argumen perintah sebagai gantinya, gunakan UserPromptExpansion.
Kontrol keputusan UserPromptSubmit
HookUserPromptSubmit dapat mengontrol apakah prompt yang dikirim diproses dan dapat menambahkan konteks. Semua kolom output JSON tersedia.
Ada dua cara untuk menambahkan konteks ke percakapan pada exit code 0:
- Stdout teks biasa: Claude Code menambahkan stdout yang diperlakukannya sebagai teks biasa ke konteks Claude
- JSON dengan
additionalContext: gunakan format JSON di bawah ini untuk kontrol yang lebih besar. KolomadditionalContextditambahkan sebagai konteks
additionalContext masing-masing disisipkan sebagai system reminder yang diawali dengan nama hook; Claude membaca keduanya. Untuk memastikan pengiriman, periksa debug log.
Untuk memblokir prompt, kembalikan objek JSON dengan decision bernilai "block":
Jika hook Anda memblokir dengan keluar menggunakan kode 2, teks stderr-nya diperlakukan sama seperti
reason.
Jejak yang ditinggalkan prompt yang diblokir
Prompt yang diblokir tidak pernah sampai ke Claude, tetapi teksnya tidak dihapus dari semua tempat. Secara default, pesan pemblokiran yang ditampilkan kepada pengguna diakhiri denganOriginal prompt: diikuti teks yang dikirim, dan Claude Code menulis pesan tersebut ke file transkrip sesi di disk. Untuk menghilangkan teks dari pesan, cetak JSON dengan "suppressOriginalPrompt": true di dalam hookSpecificOutput. Ini berfungsi baik saat hook memblokir dengan decision: "block" maupun dengan keluar menggunakan kode 2.
suppressOriginalPrompt hanya mengubah pesan pemblokiran. Teks yang dikirim tetap dapat muncul di file lokal seperti transkrip sesi dan riwayat prompt Anda, sehingga hook pemblokiran bukanlah cara untuk menjaga rahasia agar tidak tersimpan di disk. Untuk membatasi atau menghapus file-file tersebut, lihat Penyimpanan plaintext dan Menghapus data lokal.
UserPromptExpansion
Berjalan saat perintah yang diketik pengguna diperluas menjadi prompt sebelum sampai ke Claude. Gunakan ini untuk memblokir perintah tertentu agar tidak dipanggil secara langsung, menyisipkan konteks untuk skill tertentu, atau mencatat perintah mana yang dipanggil pengguna. Misalnya, hook yang cocok dengandeploy dapat memblokir /deploy kecuali ada file persetujuan, atau hook yang cocok dengan skill review dapat menambahkan daftar periksa review tim sebagai additionalContext.
Event ini mencakup jalur yang tidak dicakup PreToolUse: hook PreToolUse yang cocok dengan tool Skill hanya dipicu saat Claude memanggil tool tersebut, tetapi mengetik /skillname secara langsung melewati PreToolUse. UserPromptExpansion dipicu pada jalur langsung tersebut.
Dicocokkan pada command_name. Biarkan matcher kosong agar dipicu pada setiap perintah bertipe prompt.
Input UserPromptExpansion
Selain kolom input umum, hook UserPromptExpansion menerimaexpansion_type, command_name, command_args, command_source, dan string prompt asli. Kolom expansion_type bernilai slash_command untuk skill dan perintah kustom, atau mcp_prompt untuk prompt server MCP.
Kontrol keputusan UserPromptExpansion
HookUserPromptExpansion dapat memblokir perluasan atau menambahkan konteks. Semua kolom output JSON tersedia.
Hook yang memblokir dengan keluar menggunakan kode 2 diarahkan dengan cara yang sama seperti
reason: pesan pemblokiran menampilkan teks stderr kepada pengguna.
MessageDisplay
Berjalan saat pesan asisten di-stream ke layar. Claude Code menampilkan pesan secara bertahap: setiap kali sekelompok baris yang baru selesai siap dirender, hook berjalan sekali dengan baris-baris tersebut dan Claude Code merender teks pengganti dari hook di tempatnya. Pesan yang panjang menghasilkan beberapa panggilan; pesan yang pendek mungkin hanya menghasilkan satu. Gunakan MessageDisplay untuk:- menghapus markdown untuk tampilan minimal
- mengubah teks yang ditampilkan aplikasi Agent SDK kepada penggunanya
- menyamarkan kunci API atau hostname internal dari respons Claude
timeout dalam entri hook.
MessageDisplay hanya untuk tampilan: teks pengganti hanya mengubah apa yang dirender di layar. Transkrip dan apa yang dilihat Claude tetap menggunakan teks asli, sehingga Claude tidak pernah melihat teks pengganti, dan mode verbose menampilkan teks asli. Hook hanya menerima teks pesan asisten, sehingga hasil tool dan teks yang Anda ketik dirender tanpa perubahan.
MessageDisplay tidak mendukung matcher dan dipicu untuk setiap pesan asisten yang men-stream teks; pesan tanpa teks, seperti respons yang hanya berisi panggilan tool, tidak memicunya.
Dalam eksekusi non-interaktif, termasuk kueri Agent SDK dan claude -p, MessageDisplay berjalan sekali per pesan asisten alih-alih sekali per kelompok baris. Panggilan tunggal tersebut tiba setelah pesan selesai dan membawa teks pesan lengkap: index bernilai 0, final bernilai true, dan delta berisi seluruh pesan. Hook yang mengumpulkan teks delta untuk setiap pesan menerima total teks yang sama di kedua mode.
Input MessageDisplay
Selain kolom input umum, hook MessageDisplay menerima pengidentifikasi untuk giliran dan pesan, posisi panggilan ini di dalam pesan, dan teks baru dalamdelta. Batas kelompok bergantung pada cara teks di-stream, jadi gunakan index dan final untuk melacak kemajuan melalui pesan alih-alih mengharapkan baris dikelompokkan dengan cara tertentu.
Output MessageDisplay
Selain kolom output JSON yang tersedia untuk semua hook, hook MessageDisplay dapat mengembalikandisplayContent untuk menggantikan delta di layar:
Hook MessageDisplay tidak memiliki kontrol keputusan. Hook ini tidak dapat memblokir pesan atau mengubah apa yang disimpan dalam transkrip atau dikirim ke Claude. Claude Code menindaklanjuti
displayContent dari output JSON-nya dan membuang systemMessage serta continue.
Contoh ini menghapus format markdown dari respons Claude untuk tampilan teks biasa. Skrip membaca setiap kelompok dari stdin, menghapus penanda tebal dan backtick kode inline dari delta, lalu mengembalikan hasilnya sebagai displayContent.
- macOS/Linux
- Windows (PowerShell)
Daftarkan hook perintah untuk event ini di file pengaturan Anda:Simpan skrip ini ke
.claude/hooks/plain-display.sh di proyek Anda dan jadikan dapat dieksekusi dengan chmod +x:jq tidak ada, Claude Code menampilkan teks asli dan mencatat kegagalan tersebut hanya di output debug, bukan di dalam sesi.
PreToolUse
Berjalan setelah Claude membuat parameter tool dan sebelum memproses panggilan tool. Cocok dengan nama tool apa pun kecualiEndConversation: tool bawaan seperti Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion, dan ExitPlanMode, serta nama tool MCP apa pun.
Untuk menjalankan hook saat file tertentu berubah di disk, apa pun yang menulisnya, gunakan FileChanged alih-alih mencocokkan tool pengeditan file berdasarkan nama. Tidak seperti PreToolUse, Claude Code menjalankan hook FileChanged setelah perubahan terjadi, dan hook tersebut tidak memiliki kontrol keputusan, sehingga tidak dapat memblokir penulisan.
Gunakan kontrol keputusan PreToolUse untuk mengizinkan, menolak, meminta konfirmasi, atau menangguhkan panggilan tool.
Hook callback Agent SDK pada PreToolUse yang melampaui timeout-nya akan memblokir panggilan tool.
Input PreToolUse
Selain kolom input umum, hook PreToolUse menerimatool_name, tool_input, dan tool_use_id.
Untuk tool MCP, input juga membawa mcp_server, sebuah objek dengan name server dan source yang menyatakan asal definisi server tersebut. Nilai source mencakup plugin, sdk, dan cakupan konfigurasi seperti user dan project. McpServerProvenance dalam referensi Agent SDK mencantumkan semuanya dan menjelaskan cara memperlakukan nilai yang tidak Anda kenali. Dasarkan keputusan kepercayaan pada source, bukan pada name atau prefiks nama tool mcp__<server>__. Kolom mcp_server memerlukan Claude Code v2.1.274 atau lebih baru.
Untuk tool file Write, Edit, dan Read, tool_input.file_path selalu absolut:
- Claude Code memperluas
~dan path relatif sebelum hook berjalan, sehingga hook yang mencocokkan path tidak dapat dilewati melalui~atau penulisan relatif dari path yang sama - Di Windows, path tiba dengan pemisah backslash, bahkan saat hook Anda berjalan di Git Bash tempat
$PWDterlihat seperti/c/project - Perbandingan yang ditulis dengan garis miring, seperti pemeriksaan
/src/, tidak pernah cocok dengan path backslash, dan panggilan tool berlanjut seolah-olah hook tidak menemukan apa pun untuk diblokir - Normalisasikan pemisah sebelum membandingkan:
FILE_PATH="${FILE_PATH//\\//}"di Bash, ataufile_path.replace("\\", "/")di Python, lalu cocokkan segmen path seperti/src/alih-alih menambatkan dengan^, karena path-nya absolut
Write di Windows mengirimkan:
tool_input bergantung pada tool:
Bash
Menjalankan perintah shell.
Saat perintah Bash mengubah file di repositori Git, Claude Code dapat mencatat apa yang berubah. Claude Code mencatat perubahan di setiap mode izin saat pengaturan
bashEditDiffEnabled mengaktifkan pencatatan; entri pengaturan tersebut menjelaskan file mana yang dapat menetapkannya. Jika tidak, Claude Code hanya mencatatnya di auto mode dan mode bypassPermissions, dan hanya saat Claude Code mengarahkan Claude untuk mengedit file melalui Bash. Tetapkan bashEditDiffEnabled ke false untuk mematikan pencatatan. Perintah latar belakang dan perintah read-only tidak membawa diff.
Hook PostToolUse Anda kemudian menerima file yang berubah dalam tool_response.bashEditDiff. Daftar tersebut mencakup apa yang berubah di dalam repositori selama perintah berjalan. File yang diabaikan Git dan file di submodule tidak dicantumkan. Memerlukan Claude Code v2.1.269 atau lebih baru.
Daftar ini bersifat upaya terbaik dan dalam beta publik. Claude Code dapat melewatkan perubahan, menyertakan file yang diubah oleh proses lain pada saat yang sama, atau berhenti pada batas ukurannya. Bentuk kolom dapat berubah. Gunakan daftar ini untuk menemukan apa yang perlu ditinjau, bukan untuk menegakkan kebijakan.
changedFiles dan files mencantumkan apa yang diubah oleh perintah; kolom lainnya menyatakan seberapa lengkap dan seberapa andal daftar tersebut.
PowerShell
Menjalankan perintah PowerShell. Lihat tool PowerShell untuk ketersediaan berdasarkan platform. Kolomnya sama dengan tool Bash, dengan string perintah dicommand:
Cocokkan
Bash|PowerShell di hook yang memeriksa perintah shell, agar mencakup kedua tool:
- Di Windows, di mana pun tool PowerShell diaktifkan, Claude memperlakukan PowerShell sebagai shell utama dan mengarahkan perintah shell melaluinya.
- Di Windows tanpa Git Bash, tool tersebut diaktifkan secara otomatis dan Claude Code sama sekali tidak mendaftarkan tool Bash.
- Hook yang hanya cocok dengan
Bashtidak pernah dipicu di sana.
Write
Membuat atau menimpa file.Edit
Mengganti string di file yang sudah ada.Read
Membaca isi file.Glob
Menemukan file yang cocok dengan pola glob.Grep
Mencari isi file dengan ekspresi reguler.WebFetch
Mengambil dan memproses konten web.WebSearch
Mencari di web.Agent
Memunculkan subagent.
Saat panggilan Agent latar depan selesai, hook PostToolUse Anda menerima hasil subagent dan telemetri eksekusi dalam
tool_response. Baca kolom-kolom ini untuk memeriksa eksekusi; untuk rekapitulasi token dan biaya di seluruh subagent, gunakan penghitung token dan biaya yang difilter ke query_source "subagent", karena totalTokens dan usage hanya mencakup permintaan terakhir:
Di Claude Code v2.1.271 atau lebih baru, subagent yang berjalan dengan tool
SubagentHandback, yang disediakan Claude Code di auto mode, menyampaikan laporannya melalui tool tersebut alih-alih mengembalikannya sebagai teks. Kolom content dari hasil completed-nya kemudian membawa catatan singkat tentang penyerahan tersebut, bukan laporannya sendiri. Untuk membaca laporan, cocokkan hook PreToolUse atau PostToolUse pada SubagentHandback dan baca tool_input.message.
Untuk subagent latar belakang, tool kembali saat tugas berpindah ke latar belakang, sehingga tool_response tidak membawa kolom penggunaan: peluncuran latar belakang langsung kembali, dan tugas latar depan yang dipindahkan Claude Code ke latar belakang di tengah eksekusi kembali pada transisi tersebut. Respons tersebut memiliki status: "async_launched", agentId, description, prompt, outputFile, dan resolvedModel.
Pada respons completed, resolvedModel menyebutkan model yang digunakan subagent saat dimulai, yang dapat berbeda dari nilai model di tool_input, misalnya ketika availableModels atau override lain berlaku. Pada respons async_launched, resolvedModel menyebutkan model yang digunakan ketika agent berpindah ke latar belakang, sehingga pertukaran yang terjadi sebelum dipindahkan ke latar belakang tercermin di sana.
AskUserQuestion
Mengajukan satu hingga empat pertanyaan pilihan ganda kepada pengguna.ExitPlanMode
Menampilkan rencana dan meminta pengguna untuk menyetujuinya sebelum Claude meninggalkan plan mode. Claude menulis rencana ke file di disk sebelum memanggil tool, sehinggatool_input literal dari model biasanya kosong. Claude Code menyisipkan isi rencana dan path file sebelum meneruskan input ke hook.
Di
PostToolUse, tool_response adalah objek dengan kolom plan dan filePath yang berisi rencana yang disetujui, ditambah flag status internal. Baca tool_response.plan untuk isi rencana alih-alih membaca ulang file dari disk.
Kontrol keputusan PreToolUse
HookPreToolUse dapat mengontrol apakah panggilan tool berlanjut. Tidak seperti hook lain yang menggunakan field decision tingkat atas, PreToolUse mengembalikan keputusannya di dalam objek hookSpecificOutput. Ini memberinya kontrol yang lebih kaya: empat hasil (allow, deny, ask, atau defer) ditambah kemampuan untuk memodifikasi input tool sebelum eksekusi. Untuk mengizinkan atau menulis ulang perintah Monitor, lihat Mencocokkan perintah Monitor.
Saat beberapa hook PreToolUse mengembalikan keputusan yang berbeda, urutan prioritasnya adalah
deny > defer > ask > allow.
Hook yang memblokir dengan keluar menggunakan kode 2 diarahkan dengan cara yang sama seperti "deny": Claude melihat pesan stderr sebagai alasan penolakan.
Saat hook mengembalikan "ask", permintaan izin yang ditampilkan kepada pengguna menyertakan label yang mengidentifikasi asal hook: [settings] untuk hook dari file pengaturan mana pun atau dari frontmatter agent, [plugin:<name>] untuk hook plugin, atau [skill] untuk hook dari frontmatter skill. Ini membantu pengguna memahami sumber konfigurasi mana yang meminta konfirmasi.
PreToolUse sebelumnya menggunakan kolom
decision dan reason tingkat atas, tetapi kolom tersebut sudah deprecated untuk event ini. Gunakan hookSpecificOutput.permissionDecision dan hookSpecificOutput.permissionDecisionReason sebagai gantinya. Nilai deprecated "approve" dan "block" masing-masing dipetakan ke "allow" dan "deny". Event lain seperti PostToolUse dan Stop tetap menggunakan decision dan reason tingkat atas sebagai format saat ini.updatedInput PreToolUse dalam auto mode
Saat hook Anda mengembalikan updatedInput tanpa permissionDecision dalam auto mode, Claude Code dapat menolak panggilan yang diubah. Server meninjau input yang ditulis Claude, bukan input yang dikembalikan hook Anda. Dalam sesi terminal interaktif dan sesi VS Code pada koneksi langsung ke API Anthropic, penolakan tersebut menyertakan teks ini:
- Hook dapat menolak panggilan atau mengirimkannya ke permintaan izin
updatedInputapa pun yang dikembalikannya dibuang, sehingga yang dijalankan adalah apa yang ditinjau server
command di pengaturan atau file hook plugin dapat mengubah panggilan Bash, PowerShell, Monitor, Edit, Write, dan NotebookEdit tanpa penolakan ini. Penolakan tetap dapat muncul, misalnya saat hook mana pun mengubah panggilan sebelumnya dalam respons Claude yang sama atau perintah Bash meminta untuk berjalan di luar sandbox.
Keputusan ask PreToolUse di auto mode
Di auto mode, "ask" dari hook PreToolUse biasanya mengirimkan panggilan tool ke permintaan izin, dan pengklasifikasi tidak memutuskannya. Beberapa panggilan mengambil jalur yang berbeda:
- Panggilan yang juga ditandai oleh pemeriksaan Claude Code sendiri: ini termasuk penulisan ke path yang dilindungi dan perintah yang meminta untuk berjalan di luar sandbox. Pengklasifikasi dapat menolak salah satu panggilan ini sebelum permintaan apa pun muncul. Jika pengklasifikasi menyetujui, panggilan biasanya tetap diteruskan ke permintaan izin.
- Panggilan
SubagentHandback: tool ini mengirimkan laporan subagent. Tidak ada permintaan izin yang muncul untuknya, dan panggilan tetap berjalan meskipun pengklasifikasi keberatan.
SubagentHandback, kembalikan "deny". Claude Code kemudian memberi tahu subagent, hingga tiga kali, bahwa laporannya tidak terkirim dan untuk memanggil tool lagi. Jika hook Anda terus menolak, subagent berakhir tanpa mengirimkan laporan melalui tool ini, dan percakapan yang menunggunya mendapat catatan yang menyatakan hal tersebut.
Tool yang memerlukan interaksi pengguna
AskUserQuestion dan ExitPlanMode memerlukan interaksi pengguna. Dalam mode non-interaktif dengan flag -p, Claude Code hanya menawarkannya saat eksekusi memiliki host izin untuk menerima permintaan, seperti callback canUseTool Agent SDK.
Hook PreToolUse memenuhi persyaratan tersebut jika melakukan hal berikut:
- Membaca input tool dari stdin
- Mengumpulkan jawaban melalui UI Anda sendiri
- Mengembalikan
permissionDecision: "allow"bersamaupdatedInputyang berisi jawaban, sehingga tool berjalan tanpa permintaan
"allow" saja tidak cukup untuk tool-tool ini.
Untuk AskUserQuestion, kembalikan array questions asli dan tambahkan objek answers yang memetakan teks setiap pertanyaan ke jawaban yang dipilih. Output ini menjawab satu pertanyaan dengan React:
_meta["anthropic/requiresUserInteraction"] lebih ketat: hook tidak dapat melewati permintaan persetujuannya dengan "allow", dengan atau tanpa updatedInput, karena Claude Code tidak dapat memastikan bahwa hook telah mengumpulkan interaksi yang dibutuhkan tool tersebut.
Menangguhkan panggilan tool untuk nanti
"defer" ditujukan untuk integrasi yang menjalankan claude -p sebagai subproses dan membaca output JSON-nya, seperti aplikasi Agent SDK atau UI kustom yang dibangun di atas Claude Code. Ini memungkinkan proses pemanggil tersebut menjeda Claude pada suatu panggilan tool, mengumpulkan input melalui antarmukanya sendiri, lalu melanjutkan dari titik terakhir.
Claude Code hanya mematuhi "defer" dalam mode non-interaktif dengan flag -p, dan hanya ketika Claude melakukan satu panggilan tool dalam giliran tersebut:
- Dalam sesi interaktif: Claude Code mencatat peringatan ke log dan mengabaikan hasil hook
- Beberapa panggilan tool sekaligus:
"defer"diabaikan dengan peringatan dan tool berjalan melalui alur izin normal
AskUserQuestion adalah kasus yang umum: Claude ingin menanyakan sesuatu kepada pengguna, tetapi tidak ada terminal untuk menjawabnya. Eksekusi -p hanya menawarkan AskUserQuestion saat memiliki host izin, seperti tool MCP yang Anda berikan dengan --permission-prompt-tool, jadi mulailah eksekusi dengan salah satunya. Alur bolak-baliknya berjalan seperti ini:
- Claude memanggil
AskUserQuestion. HookPreToolUsedipicu. - Hook mengembalikan
permissionDecision: "defer". Tool tidak dieksekusi. Giliran berakhir dengan hasil yang membawastop_reason: "tool_deferred", dan panggilan tool yang tertunda tetap berada di transkrip. - Proses pemanggil membaca
deferred_tool_usedari hasil SDK, menampilkan pertanyaan di UI-nya sendiri, dan menunggu jawaban. - Proses pemanggil menjalankan
claude -p --resume <session-id>dengan host izin yang sama. Panggilan tool yang sama memicuPreToolUselagi. - Hook mengembalikan
permissionDecision: "allow"dengan jawaban diupdatedInput. Tool dieksekusi dan Claude melanjutkan.
deferred_tool_use membawa id, name, dan input dari tool:
cleanupPeriodDays menghapusnya, setelah 30 hari secara default.
Jika jawaban belum siap saat Anda melanjutkan, hook dapat mengembalikan "defer" lagi. Jika Anda mengirim prompt bersama resume tersebut, Claude menjawabnya tanpa hasil tool dan tidak menjalankan panggilan itu. Lanjutkan tanpa prompt sampai hook mengembalikan "allow" atau "deny".
Untuk melanjutkan sesi yang ditangguhkan dalam plan mode, berikan
--permission-prompt-tool bersama --resume agar Claude Code dapat menampilkan rencana untuk disetujui. Untuk kondisi lainnya, lihat Melanjutkan dalam plan mode dengan -p. Memerlukan Claude Code v2.1.246 atau lebih baru.Saat Anda melanjutkan dengan -p, Claude Code tidak memulihkan mode izin tersimpan lainnya. Claude Code memulai run dalam mode izin yang akan digunakan oleh run claude -p baru, jadi berikan --permission-mode atau --dangerously-skip-permissions lagi jika sesi yang ditunda menggunakan salah satunya.Melanjutkan ketika tool yang ditunda tidak tersedia
Hasil denganstop_reason: "tool_deferred_unavailable" berarti tool dari panggilan yang ditunda tidak tersedia di sesi yang dilanjutkan, misalnya karena server MCP-nya tidak terhubung. Hook PreToolUse Anda tidak dijalankan, dan field deferred_tool_use pada hasil menyebutkan nama tool tersebut.
Selama panggilan masih tertunda, prompt apa pun yang Anda kirim tidak sampai ke Claude dan run keluar dengan kode 1. Field result diawali dengan <tool> isn't available, so its deferred call didn't run. Jalankan panggilan tersebut, atau buang agar tidak dijalankan:
- Untuk menjalankan panggilan: buat tool tersedia dan lanjutkan lagi.
- Untuk membuang panggilan: lanjutkan dengan prompt dan
CLAUDE_CODE_DROP_UNAVAILABLE_DEFERRED_TOOLdiatur ke1. Claude menerima error sebagai hasil panggilan dan menjawab prompt Anda. Saat Anda melanjutkan nanti, tidak ada panggilan yang tertunda.
tool_deferred_unavailable dengan field result kosong, lalu jawaban Claude dalam hasil kedua. Run keluar dengan kode 0 kecuali jawaban tersebut berupa error. Jika Anda mengatur variabel tersebut dan tidak mengirim prompt, run tetap membuang panggilan dan keluar dengan kode 1.
Sebelum v2.1.319, Claude Code menjawab prompt tanpa hasil tool dan membuang panggilan tersebut. Pada versi-versi tersebut, kirim prompt baru setelah tool tersedia.
PermissionRequest
Berjalan saat Claude Code akan meminta izin Anda untuk menggunakan tool. Dalam sesi yang tidak dapat menampilkan permintaan, seperti subagent latar belakang dalam mode non-interaktif, Claude Code tetap menjalankan hook ini, dan jika tidak ada hook yang mengembalikan keputusan, Claude Code menolak panggilan tool tersebut. Untuk panggilan yang mencapai--permission-prompt-tool atau callback canUseTool Agent SDK, hook berjalan bersamaan dengan host Anda, dan mana pun yang memutuskan lebih dulu yang berlaku.
Gunakan kontrol keputusan PermissionRequest untuk mengizinkan atau menolak atas nama pengguna.
Gunakan event ini saat Anda memerlukan sinyal tepat ketika Claude meminta izin untuk menggunakan tool. Claude Code menjalankan hook Notification dengan tipe permission_prompt hanya setelah permintaan menunggu sekitar enam detik.
Claude Code tidak menjalankan hook PermissionRequest untuk permintaan jaringan dari perintah yang berjalan dalam sandbox. Untuk mendapatkan sinyal bagi permintaan izin tersebut, gunakan tipe notifikasi permission_prompt.
Dicocokkan pada nama tool, dengan nilai yang sama seperti PreToolUse.
Input PermissionRequest
Hook PermissionRequest menerima fieldtool_name dan tool_input seperti hook PreToolUse, tetapi tanpa tool_use_id. Untuk tool MCP, hook ini juga menerima objek mcp_server. Array permission_suggestions opsional berisi pembaruan izin yang disarankan Claude Code untuk permintaan ini, seperti menambahkan aturan allow atau mengubah mode izin.
Array permission_suggestions bukan daftar persis dari opsi yang Anda lihat, karena setiap dialog izin membangun opsinya sendiri. Beberapa dialog, seperti dialog untuk pengeditan file, sama sekali tidak membaca array tersebut dan menurunkan opsinya dari permintaan itu sendiri. Dialog yang membacanya tetap dapat menahan opsi yang sarannya tetap ada di array, misalnya ketika allowManagedPermissionRulesOnly menyembunyikan opsi penyimpanan aturan. Dialog tersebut juga dapat menawarkan opsi yang tidak memiliki entri saran, seperti Yes, and switch to auto mode, yang mengubah mode izin secara langsung, bukan melalui pembaruan izin.
Hook PreToolUse berjalan sebelum setiap panggilan tool, baik panggilan tersebut memerlukan izin maupun tidak. Hook PermissionRequest hanya berjalan ketika Claude Code akan meminta izin kepada Anda, atau ketika Claude Code akan menolak secara otomatis panggilan yang tidak dapat menampilkan permintaan izin. Kedua event ini tidak dipicu untuk EndConversation.
Kontrol keputusan PermissionRequest
HookPermissionRequest dapat mengizinkan atau menolak permintaan izin. Selain field output JSON yang tersedia untuk semua hook, skrip hook Anda dapat mengembalikan objek decision dengan field khusus event berikut:
Untuk mengizinkan atau menulis ulang perintah Monitor, lihat Mencocokkan perintah Monitor.
Hook yang keluar dengan kode 2 tanpa objek
decision membiarkan alur izin tidak berubah, dan stderr-nya dibuang. Untuk memberikan atau menolak permintaan, kembalikan objek decision.
Entri pembaruan izin
Field outputupdatedPermissions dan field input permission_suggestions sama-sama menggunakan array objek entri yang sama. Setiap entri memiliki type yang menentukan field lainnya, dan destination yang mengontrol ke mana perubahan ditulis.
Di
rules, untuk aturan izin yang ditulis Tool(specifier), toolName adalah nama tool dan ruleContent adalah specifier-nya:
Bash(npm test):{ "toolName": "Bash", "ruleContent": "npm test" }BashatauBash(*):{ "toolName": "Bash" }, karena keduanya mencocokkan seluruh toolmcp__github__get_issue:{ "toolName": "mcp__github__get_issue" }
ruleContent dalam aturan allow untuk tool MCP. Jika Anda menambahkannya, aturan tersebut tidak cocok dengan panggilan apa pun.
setMode dengan bypassPermissions hanya berlaku jika Anda meluncurkan sesi dengan mode bypass yang sudah tersedia: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, atau permissions.defaultMode: "bypassPermissions" di pengaturan pengguna, --settings, atau pengaturan terkelola. Jika tidak, pembaruan tersebut tidak berpengaruh. Pembaruan juga tidak berpengaruh ketika permissions.disableBypassPermissionsMode menonaktifkan mode tersebut, atau ketika sesi dimulai dalam restricted mode.bypassPermissions tidak pernah disimpan sebagai defaultMode terlepas dari destination.destination pada setiap entri menentukan apakah perubahan tetap di memori atau disimpan ke file pengaturan.
Hook dapat mengembalikan salah satu
permission_suggestions yang diterimanya sebagai output updatedPermissions-nya sendiri.
PostToolUse
Berjalan segera setelah tool berhasil diselesaikan. Mencocokkan berdasarkan nama tool, dengan nilai yang sama seperti PreToolUse. Cocokkan secara lebih luas ketika nama tool bukan filter yang tepat:- Untuk menjalankan hook setelah tool apa pun berhasil diselesaikan, hilangkan
matcheratau atur ke"*". Hook Anda kemudian dapat menemukan sendiri apa yang berubah, misalnya dengan menjalankangit status --porcelain, yang juga mencantumkan file yang tidak terlacak yang terlewat olehgit diff. Untuk panggilan tool yang gagal, tambahkan hook yang sama di bawah PostToolUseFailure. - Untuk menjalankan hook ketika file tertentu berubah di disk, apa pun yang menulisnya, gunakan FileChanged. Claude Code tidak menjalankan hook
PostToolUseyang mencocokkanEdit|Writeketika perintahBashatau proses di luar Claude Code menulis ulang file yang sama.
PostToolUse setelah interupsi
Jika hookPostToolUse berhenti lebih awal setelah Anda mengirim pesan atau menginterupsi selama panggilan tool, periksa apakah Anda menekan Ctrl+Enter, Enter, atau Esc:
Ctrl+Enter: jika menekannya mengakhiri giliran saat tool sedang berjalan dan tool selesai, hookPostToolUseAnda berjalan pada hasilnya dan memiliki waktu 10 detik untuk selesai. Tombol ini berfungsi sebagaiEscjika Anda mengantrekan perintah!sebelum pesan Anda atau tugas latar belakang dinonaktifkan, misalnya denganCLAUDE_CODE_DISABLE_BACKGROUND_TASKS.Enter: pesan yang Anda antrekan denganEntertidak menginterupsi giliran, sehingga hook mempertahankantimeoutbiasanya.Esc: Anda langsung membatalkan hook, jadi kirim pesan denganEnteratauCtrl+Enterketika hook harus berjalan.
timeout-nya. Agar command hook yang lebih lama dapat selesai, jalankan dengan async: true, yang berarti hook tidak dapat memblokir atau menulis ulang hasilnya.
Sebelum v2.1.319, hook tidak dimulai setelah Anda menginterupsi giliran dengan pesan, dan hook yang sedang berjalan dihentikan.
Input PostToolUse
HookPostToolUse dipicu setelah tool berhasil dieksekusi. Input mencakup tool_input, yaitu argumen yang dikirim ke tool, dan tool_response, yaitu hasil yang dikembalikannya. Skema persis untuk keduanya bergantung pada tool. Path tool_input pada tool file tiba dalam format yang sama seperti untuk PreToolUse: selalu absolut, dengan separator native platform, jadi backslash di Windows. Untuk tool MCP, input juga membawa objek mcp_server.
Kontrol keputusan PostToolUse
HookPostToolUse dapat memberikan umpan balik kepada Claude setelah eksekusi tool. Selain field output JSON yang tersedia untuk semua hook, skrip hook Anda dapat mengembalikan field khusus event berikut:
Contoh di bawah ini menggantikan output dari panggilan
Bash. Nilai pengganti sesuai dengan bentuk output tool Bash:
Menganotasi hasil untuk pengklasifikasi auto mode
KembalikanclassifierContext untuk mengirim catatan singkat tentang hasil panggilan tool ke pengklasifikasi auto mode, bukan ke Claude. Catatan tersebut ditujukan untuk tinjauan pengklasifikasi terhadap tindakan selanjutnya. Field ini memerlukan Claude Code v2.1.236 atau lebih baru.
Contoh di bawah ini memberi tahu pengklasifikasi dari mana output kueri berasal:
- Hook yang dikonfigurasi di Claude Code: untuk hook dari file pengaturan, plugin, skill, dan frontmatter agent, pengklasifikasi memperlakukan catatan sebagai konteks yang tidak terverifikasi dan disediakan oleh aplikasi. Catatan tidak pernah menetapkan niat pengguna, dan jika catatan mengklaim bahwa Anda menyetujui atau meminta sesuatu, pengklasifikasi memeriksa klaim tersebut terhadap pesan Anda sendiri dalam percakapan
- Callback Agent SDK in-process: ketika aplikasi yang menyematkan Claude Code mendaftarkan hook sebagai callback TypeScript SDK dan mengembalikan catatan selama sesi aktif, pengklasifikasi dapat menimbang pernyataan pengguna yang diteruskan dalam catatan sebagai niat pengguna. Pernyataan semacam itu dapat memenuhi persyaratan persetujuan yang akan diterima pengklasifikasi dari pesan yang Anda kirim, tetapi tidak pernah mencabut blokir yang juga tidak dapat dicabut oleh pesan Anda sendiri. Setelah sesi dilanjutkan, Claude Code memperlakukan catatan yang dipulihkan sebagai konteks yang tidak terverifikasi. Ketika hook dari kedua kelompok menganotasi panggilan yang sama, pengklasifikasi memperlakukan catatan gabungan sebagai tidak terverifikasi
- Panjang: Claude Code membatasi catatan untuk satu panggilan tool hingga 2.000 karakter dan memotong sisanya. Batas ini dibagi di antara setiap hook yang merespons panggilan tersebut
- Hanya respons sinkron: Claude Code mengabaikan field ini dalam respons hook yang berjalan di latar belakang, karena respons tersebut tiba setelah Claude Code mencatat hasil tool
- Panggilan yang tidak dicatat pengklasifikasi: transkrip pengklasifikasi menghilangkan pencarian read-only seperti pembacaan file dan pencarian. Claude Code membuang catatan yang dilampirkan pada salah satu panggilan tersebut
- Interaksi dengan penulisan ulang: ketika catatan mendeskripsikan output yang Anda ganti dengan
updatedToolOutput, kembalikan kedua field dalam respons hook yang sama. Claude Code membuang catatan jika penulisan ulang tersebut ditolak atau penulisan ulang dari hook lain menggantikannya. Claude Code mengirimkan catatan yang Anda kembalikan tanpa penulisan ulang bahkan ketika hook lain menulis ulang output
PostToolUseFailure
Berjalan ketika tool yang mulai dieksekusi gagal: tool melempar error, atau tool MCP mengembalikan hasil error. Gunakan ini untuk mencatat kegagalan, mengirim peringatan, atau memberikan umpan balik korektif kepada Claude. Mencocokkan berdasarkan nama tool, dengan nilai yang sama seperti PreToolUse.Event ini tidak dipicu untuk panggilan tool yang ditolak sebelum eksekusi: nama tool yang tidak dikenal, input yang gagal dalam validasi skema atau validasi khusus tool, atau penolakan izin. Penolakan validasi dikembalikan sebagai hasil
tool_use_error dan terjadi sebelum hook berjalan, sehingga tidak memicu PreToolUse maupun PostToolUseFailure. Penolakan izin memicu PreToolUse tetapi tidak memicu event ini; lihat PermissionDenied.Input PostToolUseFailure
Hook PostToolUseFailure menerima fieldtool_name dan tool_input yang sama seperti PostToolUse, bersama dengan informasi error sebagai field tingkat atas. Untuk tool MCP, hook ini juga menerima objek mcp_server. Misalnya, perintah npm test yang gagal mungkin mengirimkan:
String
error umumnya adalah teks yang sama yang diterima Claude sebagai hasil tool yang gagal. Formatnya bervariasi menurut tool dan kegagalan. Dasarkan hook Anda pada tool_name, is_interrupt, dan baris pertama Exit code N; perlakukan sisa string sebagai teks tampilan, bukan format yang stabil.
- Untuk Bash dan PowerShell, perintah yang berjalan dan keluar menghasilkan baris pertama
Exit code N, lalu output apa pun yang dihasilkan perintah sebagai satu blok dengan stdout dan stderr yang saling berselang-seling - Payload juga dapat membawa pesan kegagalan polos tanpa baris exit code, ketika Claude Code tidak dapat memulai proses shell itu sendiri
- Claude Code memotong bagian tengah string panjang di sekitar penanda
... [N characters truncated] ..., dan dapat menyisipkan barisnya sendiri, sepertiCommand timed out after 2m 0s
Kontrol keputusan PostToolUseFailure
HookPostToolUseFailure dapat memberikan konteks kepada Claude setelah kegagalan tool. Selain field output JSON yang tersedia untuk semua hook, skrip hook Anda dapat mengembalikan field khusus event berikut:
PostToolBatch
Berjalan sekali setelah setiap panggilan tool dalam satu batch telah diselesaikan, sebelum Claude Code mengirim permintaan berikutnya ke model.PostToolUse dipicu sekali per tool, yang berarti dipicu secara bersamaan ketika Claude melakukan panggilan tool paralel. PostToolBatch dipicu tepat sekali dengan batch lengkap, sehingga menjadi tempat yang tepat untuk menyisipkan konteks yang bergantung pada kumpulan tool yang berjalan, bukan pada satu tool tertentu. Tidak ada matcher untuk event ini.
Input PostToolBatch
Selain field input umum, hook PostToolBatch menerimatool_calls, yaitu array yang mendeskripsikan setiap panggilan tool dalam batch:
tool_response berisi konten yang sama yang diterima model dalam blok tool_result yang bersangkutan. Nilainya adalah string yang diserialisasi atau array content-block, persis seperti yang dikeluarkan tool. Untuk Read, itu berarti teks dengan awalan nomor baris, bukan konten file mentah. Respons bisa berukuran besar, jadi parse hanya field yang Anda butuhkan.
Bentuk
tool_response berbeda dari milik PostToolUse. PostToolUse meneruskan objek Output terstruktur dari tool, seperti {filePath: "...", type: "create"} untuk Write; PostToolBatch meneruskan konten tool_result terserialisasi yang dilihat model.Kontrol keputusan PostToolBatch
HookPostToolBatch dapat menyisipkan konteks untuk Claude. Selain field output JSON yang tersedia untuk semua hook, skrip hook Anda dapat mengembalikan field khusus event berikut:
decision: "block" atau continue: false menghentikan agentic loop sebelum panggilan model berikutnya. Pesan pemblokiran berasal dari reason atau stopReason JSON, atau dari stderr pada exit 2. Anda melihatnya sebagai peringatan di transkrip, dan pesan tersebut tetap ada dalam percakapan, sehingga Claude melihatnya ketika percakapan berlanjut.
PermissionDenied
Berjalan ketika auto mode menolak panggilan tool, termasuk ketika menolak tanpa putusan pengklasifikasi karena pemeriksaan keamanan yang terpisah dari auto mode menolak permintaan pengklasifikasi itu sendiri atau responsnya tidak dapat di-parse. Hook ini hanya dipicu dalam auto mode: hook ini tidak berjalan ketika Anda menolak dialog izin secara manual, ketika hookPreToolUse memblokir panggilan, atau ketika aturan deny cocok. Gunakan ini untuk mencatat penolakan, menyesuaikan konfigurasi, atau memberi tahu model bahwa model dapat mencoba ulang panggilan tool.
Mencocokkan berdasarkan nama tool, dengan nilai yang sama seperti PreToolUse.
Input PermissionDenied
Selain field input umum, hook PermissionDenied menerimatool_name, tool_input, tool_use_id, dan reason. Untuk tool MCP, hook ini juga menerima objek mcp_server.
Kontrol keputusan PermissionDenied
Hook PermissionDenied dapat memberi tahu model bahwa model dapat mencoba ulang panggilan tool yang ditolak. Kembalikan objek JSON denganhookSpecificOutput.retry diatur ke true:
retry bernilai true, Claude Code menambahkan pesan ke percakapan yang memberi tahu model bahwa model dapat mencoba ulang panggilan tool. Claude Code tidak membatalkan penolakan itu sendiri. Jika hook Anda tidak mengembalikan JSON, atau mengembalikan retry: false, penolakan tetap berlaku dan model menerima pesan penolakan asli.
Claude Code mengabaikan retry: true ketika pengklasifikasi tidak menghasilkan putusan atas tindakan tersebut: responsnya tidak dapat di-parse, atau pemeriksaan keamanan yang terpisah dari auto mode menolak permintaan pengklasifikasi itu sendiri. Untuk penolakan tersebut, Claude Code sudah memberi tahu model dalam pesan penolakan apakah harus mencoba ulang nanti atau melanjutkan.
Notification
Berjalan ketika Claude Code mengirim notifikasi. Mencocokkan berdasarkan jenis notifikasi. Hilangkan matcher untuk menjalankan hook untuk semua jenis notifikasi. Anda menerima event hook ini bahkan ketika notifikasi desktop dimatikan: pengaturanpreferredNotifChannel, termasuk notifications_disabled, hanya mengubah cara Anda diberi peringatan, bukan apakah hook Anda berjalan.
Tipe
quota_auto_resume_fired, quota_auto_resume_stale, dan quota_auto_resume_disabled memerlukan Claude Code v2.1.234 atau yang lebih baru. Tipe auth_storage_failure memerlukan v2.1.288 atau yang lebih baru.
Dalam sesi terminal, permission_prompt untuk permintaan jaringan dari perintah yang di-sandbox memerlukan Claude Code v2.1.246 atau lebih baru.
agent_needs_input untuk pertanyaan penyiapan terminal dari rekan tim memerlukan Claude Code v2.1.248 atau lebih baru.
Jenis
permission_prompt, idle_prompt, elicitation_dialog, dan elicitation_url_dialog berbagi pengaturan waktu dengan notifikasi desktop, sehingga dalam sesi terminal Anda hanya melihatnya ketika Anda tampak jauh dari terminal:- Harapkan
permission_promptsetelah Anda tidak mengetik selama sekitar enam detik. Timer dimulai ketika permintaan izin muncul, dan setiap penekanan tombol menundanya. Untuk menjalankan hook segera ketika Claude meminta izin untuk menggunakan tool, gunakan PermissionRequest sebagai gantinya. - Harapkan
idle_promptsekitar 60 detik setelah Claude selesai merespons, dan hanya jika Anda belum mengetik sejak itu dan tidak ada agent latar belakang, seperti subagent latar belakang, yang masih berjalan. Claude Code tidak mengirimidle_promptsaat menunggu batas penggunaan claude.ai di-reset. Ketika penantian berakhir dengan sendirinya, salah satu jenisquota_auto_resume_*dipicu sebagai gantinya. - Harapkan
elicitation_dialoguntuk formulir elicitation, atauelicitation_url_dialoguntuk permintaan URL browser, setelah Anda tidak mengetik selama sekitar enam detik. Keduanya berbagi gerbang enam detik yang sama denganpermission_prompt: timer dimulai ketika dialog muncul, dan setiap penekanan tombol menundanya.
permission_prompt secara berbeda dalam sesi di mana Claude Code mengirim permintaan izin ke callback canUseTool milik Agent SDK, yang merupakan cara Claude Desktop dan ekstensi VS Code menghosting Claude Code:
- Harapkan
permission_promptsekitar enam detik setelah Claude meminta izin. Claude Code tidak menundanya saat Anda mengetik. - Jika Anda atau hook PermissionRequest menjawab lebih cepat, Claude Code tidak menjalankan
permission_prompt. - Atur
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSke1untuk mematikanpermission_promptdalam sesi ini.
permission_prompt tidak dipicu dalam sesi ini.
Gunakan matcher terpisah untuk menjalankan handler yang berbeda tergantung pada jenis notifikasi. Konfigurasi ini memicu skrip peringatan khusus izin ketika Claude membutuhkan persetujuan izin dan notifikasi yang berbeda ketika Claude sedang idle:
Input Notification
Selain field input umum, hook Notification menerimamessage dengan teks notifikasi, title opsional, dan notification_type yang menunjukkan jenis mana yang dipicu.
systemMessage dan continue mereka tetapi tetap mengeluarkan terminalSequence, yang menjadi andalan contoh notifikasi desktop. Hook Notification ditujukan untuk efek samping seperti meneruskan notifikasi ke layanan eksternal.
SubagentStart
Berjalan ketika Claude memunculkan subagent dengan tool Agent, ketika Claude melanjutkan subagent, dan setiap kali rekan tim tim agent in-process menangani pesan baru. Mendukung matcher untuk memfilter berdasarkan nama jenis agent. Untuk agent bawaan, ini adalah nama agent sepertigeneral-purpose, Explore, atau Plan. Untuk subagent kustom, ini adalah field name dari frontmatter agent, bukan nama file.
Untuk subagent yang dikirimkan oleh plugin, jenis agent adalah pengidentifikasi dengan cakupan plugin seperti my-plugin:reviewer, bukan nama frontmatter polos. Titik dua menempatkan nama dengan cakupan plugin pada jalur ekspresi reguler, jadi jangkarkan matcher dengan ^ dan $ untuk kecocokan persis: ^my-plugin:reviewer$.
Input SubagentStart
Selain field input umum, hook SubagentStart menerimaagent_id dengan pengidentifikasi unik untuk subagent dan agent_type dengan nama agent yang difilter oleh matcher.
SubagentStop
Berjalan ketika subagent Claude Code selesai merespons. Mencocokkan berdasarkan jenis agent, dengan nilai yang sama seperti SubagentStart.Input SubagentStop
Selain field input umum, hook SubagentStop menerimastop_hook_active, agent_id, agent_type, agent_transcript_path, dan last_assistant_message. Field agent_type adalah nilai yang digunakan untuk pemfilteran matcher. transcript_path adalah transkrip sesi utama, sedangkan agent_transcript_path adalah transkrip milik subagent itu sendiri yang disimpan dalam folder subagents/ bersarang. Field last_assistant_message berisi konten teks dari respons akhir subagent, sehingga hook dapat mengaksesnya tanpa mem-parse file transkrip.
Tidak setiap event SubagentStop berasal dari subagent yang dimunculkan Claude. Claude Code juga menjalankan agent internal untuk beberapa fiturnya sendiri, seperti saran prompt dan pertanyaan sampingan /btw, dan SubagentStop juga dipicu ketika salah satu dari agent tersebut selesai. Untuk event tersebut, agent_type adalah nama agent yang dijalankan oleh sesi itu sendiri, seperti yang diatur dengan --agent atau pengaturan agent, dan string kosong ketika sesi berjalan tanpanya.
matcher yang menyebutkan jenis agent tidak mencocokkan agent_type yang kosong. Hook yang matcher-nya dihilangkan, "", atau "*", atau merupakan ekspresi reguler yang cocok dengan string kosong, juga berjalan untuk event dengan agent_type kosong.
Pada Claude Code v2.1.271 atau lebih baru, subagent yang berjalan dengan tool SubagentHandback mengirimkan laporannya melalui tool tersebut sebelum berhenti. Field last_assistant_message kemudian memuat teks penutup subagent, jika ada, yang bukan merupakan laporan yang dikirimkan. Laporan tersebut adalah input message dari panggilan itu, yang diterima oleh hook PreToolUse atau PostToolUse yang mencocokkan SubagentHandback sebagai tool_input.message.
Hook SubagentStop juga menerima array background_tasks dan session_crons yang dijelaskan di bawah Input Stop. Kedua array tersebut dicakup ke sesi induk, bukan subagent.
hookSpecificOutput.additionalContext dengan hookEventName diatur ke "SubagentStop", untuk umpan balik non-error yang membuat subagent tetap berjalan. Mengembalikan decision: "block" dengan reason membuat subagent tetap berjalan dan mengirimkan reason ke subagent sebagai instruksi berikutnya. Hook yang memblokir dengan keluar dengan kode 2 mengirimkan pesan stderr-nya dengan cara yang sama. Untuk menyisipkan konteks ke sesi induk setelah subagent kembali, gunakan hook PostToolUse pada tool Agent sebagai gantinya.
TaskCreated
Berjalan ketika tugas sedang dibuat melalui toolTaskCreate. Gunakan ini untuk menegakkan konvensi penamaan, mewajibkan deskripsi tugas, atau mencegah tugas tertentu dibuat. Dalam sesi tanpa tool Task, event ini tidak dipicu.
Hook TaskCreated tidak mendukung matcher dan dipicu pada setiap kejadian.
Input TaskCreated
Selain field input umum, hook TaskCreated menerimatask_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
Kontrol keputusan TaskCreated
Hook TaskCreated dapat memblokir pembuatan dengan exit code 2 atau dengan keputusan JSON. Dengan cara mana pun, Claude Code menghapus tugas dan mengembalikan pesan Anda kepada Claude sebagai error tool. Claude Code mengabaikancontinue: false dari event ini dan Claude terus bekerja.
- Exit code 2: Claude Code mengembalikan teks stderr sebagai pesan.
- JSON
{"decision": "block", "reason": "..."}: Claude Code mengembalikanreasonsebagai pesan.
TaskCompleted
Berjalan ketika tugas sedang ditandai sebagai selesai. Ini dipicu dalam dua situasi: ketika agent mana pun secara eksplisit menandai tugas sebagai selesai melalui tool TaskUpdate, atau ketika rekan tim tim agent menyelesaikan gilirannya dengan tugas yang sedang berlangsung. Gunakan ini untuk menegakkan kriteria penyelesaian seperti lulus pengujian atau pemeriksaan lint sebelum tugas dapat ditutup. Hook TaskCompleted tidak mendukung matcher dan dipicu pada setiap kejadian.Input TaskCompleted
Selain field input umum, hook TaskCompleted menerimatask_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
Kontrol keputusan TaskCompleted
Hook TaskCompleted mendukung dua cara untuk mengontrol penyelesaian tugas:- Exit code 2: tugas tidak ditandai sebagai selesai dan pesan stderr diumpankan kembali ke model sebagai umpan balik.
- JSON
{"continue": false, "stopReason": "..."}: ketika rekan tim yang menyelesaikan gilirannya memicu event, menghentikan rekan tim sepenuhnya, sesuai dengan perilaku hookStop.stopReasonditampilkan kepada pengguna. Ketika toolTaskUpdatememicu event, Claude Code mengabaikancontinue: false; exit code 2 tetap memblokir penyelesaian.
Stop
Berjalan ketika agent utama Claude Code selesai merespons. Tidak berjalan jika penghentian terjadi karena interupsi pengguna. Error API memicu StopFailure sebagai gantinya.Input Stop
Selain field input umum, hook Stop menerimastop_hook_active, last_assistant_message, background_tasks, dan session_crons. Field stop_hook_active bernilai true ketika Claude Code sudah melanjutkan sebagai akibat dari hook stop. Periksa nilai ini atau proses transkrip untuk menghindari pemblokiran pada kondisi yang tidak akan pernah terselesaikan.
Claude Code menerapkan batas 8 kelanjutan berturut-turut: setelah hook stop melanjutkan giliran delapan kali berturut-turut, Claude Code menimpa blokir berikutnya dan mengakhiri giliran. Hitungan kelanjutan berturut-turut di-reset setiap kali Claude memanggil tool. Untuk menaikkan batas, atur CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Field last_assistant_message berisi konten teks dari respons akhir Claude, sehingga hook dapat mengaksesnya tanpa mem-parse file transkrip. Untuk hook yang bertindak pada giliran yang baru saja selesai, seperti hook pembacaan nyaring atau notifikasi, gunakan field ini alih-alih membaca transcript_path: file transkrip tidak dijamin menyertakan pesan akhir pada saat Stop di semua versi.
Array background_tasks dan session_crons memungkinkan hook membedakan “sesi telah selesai” dari “sesi dijeda menunggu pekerjaan latar belakang untuk membangunkannya kembali”. Kedua array ada ketika registri tugas dapat dijangkau dan kosong ketika tidak ada yang sedang berjalan atau dijadwalkan.
Setiap entri dalam background_tasks mendeskripsikan satu tugas yang sedang berjalan dan menggunakan field berikut:
Setiap entri dalam
session_crons mendeskripsikan satu wakeup terjadwal dengan cakupan sesi, yang bersumber dari CronCreate, ScheduleWakeup, dan /loop:
Contoh ini menunjukkan input Stop dengan satu tugas shell yang sedang berjalan dan satu cron berulang:
Kontrol keputusan Stop
HookStop dan SubagentStop dapat mengontrol apakah Claude melanjutkan. Selain field output JSON yang tersedia untuk semua hook, skrip hook Anda dapat mengembalikan field khusus event berikut:
Hook yang memblokir dengan keluar dengan kode 2 dirutekan dengan cara yang sama seperti
reason: Claude menerima pesan stderr sebagai penjelasan mengapa harus melanjutkan.
additionalContext ketika hook bekerja sesuai rancangan dan memberikan panduan kepada Claude, seperti “jalankan test suite sebelum selesai”. Ini membuat percakapan terus berjalan melalui perlindungan loop yang sama seperti decision: "block", yaitu input stop_hook_active dan batas 8 kelanjutan berturut-turut, tetapi transkrip melabelinya Stop hook feedback dan tidak ada notifikasi error hook yang ditampilkan:
StopFailure
Berjalan alih-alih Stop ketika giliran berakhir karena error API. Claude Code mengabaikan output dan exit code hook, kecualiterminalSequence. Gunakan ini untuk mencatat kegagalan, mengirim peringatan, atau mengambil tindakan pemulihan ketika Claude tidak dapat menyelesaikan respons karena rate limit, masalah autentikasi, atau error API lainnya.
Input StopFailure
Selain field input umum, hook StopFailure menerimaerror, error_details opsional, dan last_assistant_message opsional. Field error mengidentifikasi jenis error dan digunakan untuk pemfilteran matcher.
TeammateIdle
Berjalan ketika rekan tim tim agent akan menjadi idle setelah menyelesaikan gilirannya. Gunakan ini untuk menegakkan gerbang kualitas sebelum rekan tim berhenti bekerja, seperti mewajibkan lulus pemeriksaan lint atau memverifikasi bahwa file output ada. Hook TeammateIdle tidak mendukung matcher dan dipicu pada setiap kejadian.Input TeammateIdle
Selain field input umum, hook TeammateIdle menerimateammate_name dan team_name.
Kontrol keputusan TeammateIdle
Hook TeammateIdle mendukung dua cara untuk mengontrol perilaku rekan tim:- Exit code 2: rekan tim menerima pesan stderr sebagai umpan balik dan terus bekerja alih-alih menjadi idle.
- JSON
{"continue": false, "stopReason": "..."}: menghentikan rekan tim sepenuhnya, sesuai dengan perilaku hookStop.stopReasonditampilkan kepada pengguna.
ConfigChange
Berjalan ketika file konfigurasi berubah selama sesi. Gunakan ini untuk mengaudit perubahan pengaturan, menegakkan kebijakan keamanan, atau memblokir modifikasi yang tidak sah pada file konfigurasi. Claude Code menjalankan hook ConfigChange ketika file pengaturan, file kebijakan terkelola, atau file skill berubah. Untuk kebijakan terkelola, Claude Code menjalankannya hanya ketikamanaged-settings.json atau file di managed-settings.d/ berubah.
Matcher memfilter berdasarkan sumber konfigurasi:
Perubahan seperti berikut berlaku tanpa menjalankan hook:
- Aturan dan pengaturan yang Anda simpan dengan Yes, and don’t ask again,
/permissions, atau/model, di sesi tempat Anda menyimpannya. Jika sesi lain yang terbuka memuat file yang sama, hook dijalankan di sana. - Pembaruan pada pengaturan terkelola server, preferensi terkelola macOS, atau kebijakan registry Windows
- Di WSL dengan
wslInheritsWindowsSettings, file pengaturan terkelola sisi Windows yang berubah, yang diambil Claude Code pada polling kebijakannya
Input ConfigChange
Selain field input umum, hook ConfigChange menerimasource dan secara opsional file_path. Field source menunjukkan jenis konfigurasi mana yang berubah, dan file_path memberikan path ke file spesifik yang dimodifikasi.
Kontrol keputusan ConfigChange
Hook ConfigChange dapat memblokir perubahan konfigurasi agar tidak berlaku. Gunakan exit code 2 ataudecision JSON untuk mencegah perubahan. Ketika diblokir, pengaturan baru tidak diterapkan ke sesi yang sedang berjalan.
policy_settings tidak dapat diblokir. Hook tetap dipicu untuk sumber policy_settings ketika file pengaturan terkelola di mesin berubah, sehingga Anda dapat menggunakannya untuk mencatat pengeditan tersebut, tetapi setiap keputusan pemblokiran diabaikan. Ini memastikan pengaturan yang dikelola perusahaan selalu berlaku. Claude Code tidak menjalankan hook ConfigChange ketika pengaturan yang dikelola server tiba atau diperbarui.
Claude Code bertindak berdasarkan keputusan pemblokiran dari output JSON hook ConfigChange dan membuang systemMessage dan continue. Perubahan yang diblokir tidak menampilkan pesan apa pun kepada Anda atau kepada Claude, baik Anda memblokir dengan reason maupun dengan stderr pada exit 2. Claude Code hanya menulis satu baris ke log debug.
CwdChanged
Berjalan ketika perintah shell dalam percakapan utama mengubah direktori kerja, misalnya ketika Claude mengeksekusi perintahcd. Gunakan ini untuk bereaksi terhadap perubahan direktori: memuat ulang environment variable, mengaktifkan toolchain khusus proyek, atau menjalankan skrip penyiapan secara otomatis. Berpasangan dengan FileChanged untuk alat seperti direnv yang mengelola lingkungan per direktori.
Hook CwdChanged memiliki akses ke CLAUDE_ENV_FILE. Jika hook Anda menulis variabel ke file tersebut, variabel tersebut bertahan ke perintah Bash berikutnya hingga event CwdChanged berikutnya, ketika Claude Code menghapusnya.
CwdChanged tidak mendukung matcher dan dipicu pada setiap kejadian.
Input CwdChanged
Selain field input umum, hook CwdChanged menerimaold_cwd dan new_cwd.
Output CwdChanged
Selain field output JSON yang tersedia untuk semua hook, hook CwdChanged dapat mengembalikanwatchPaths untuk mengatur secara dinamis path file mana yang dipantau oleh FileChanged:
Hook CwdChanged tidak memiliki kontrol keputusan. Hook ini tidak dapat memblokir perubahan direktori.
Claude Code membaca
watchPaths dan systemMessage dari output JSON mereka dan membuang continue. Dalam sesi interaktif, Claude Code menampilkan systemMessage sebagai notifikasi terminal singkat. Pesan tersebut tidak mencapai aliran pesan SDK.
DirectoryAdded
Berjalan setelah Anda menambahkan direktori kerja di tengah sesi dengan perintah/add-dir, atau setelah klien SDK menambahkannya dengan permintaan kontrol register_repo_root. Gunakan ini untuk menyiapkan repositori yang baru ditambahkan, misalnya dengan menginstal dependensinya.
Claude Code tidak memicu event ini ketika:
- Anda meneruskan direktori dengan flag startup
--add-dir; SessionStart mencakup direktori tersebut - Anda menambahkan direktori di tab Workspace
/permissions - Anda menambahkan direktori yang sudah menjadi direktori kerja atau berada di dalamnya
Input DirectoryAdded
Selain field input umum, hook DirectoryAdded menerimadirectory dan source.
continue dari output JSON mereka dan menampilkan sisanya secara berbeda per sumber:
slash_command: Claude Code mengirimkansystemMessagehook kepada Claude sebagai konteks pada giliran percakapan berikutnya, alih-alih menampilkannya kepada Anda. Jumlah hook yang gagal muncul di transkrip. Output kegagalan lengkap masuk ke log debugregister_repo_root: Claude Code menulis outputsystemMessagedan output kegagalan hanya ke log debug
FileChanged
Berjalan ketika file yang dipantau berubah di disk. Claude Code mendeteksi perubahan dengan filesystem watcher, bukan dengan memeriksa panggilan tool, sehingga hook dijalankan apa pun yang mengubah file tersebut: panggilan toolEdit atau Write, skrip yang dijalankan Claude dengan Bash, atau proses yang sepenuhnya berada di luar Claude Code. Penggunaan umumnya adalah memuat ulang environment variable ketika file konfigurasi proyek berubah.
matcher untuk event ini memiliki dua peran:
- Membangun daftar pantauan: nilainya dipisahkan pada
|dan setiap segmen didaftarkan sebagai nama file literal di direktori kerja, sehingga".envrc|.env"memantau tepat kedua file tersebut. Pola regex tidak berguna di sini: nilai seperti^\.envakan memantau file yang secara literal bernama^\.env. - Memfilter hook mana yang berjalan: ketika file yang dipantau berubah, nilai yang sama memfilter grup hook mana yang berjalan menggunakan aturan matcher standar terhadap basename file yang berubah.
data.csv setelah perubahan apa pun, termasuk perintah Bash atau skrip eksternal yang menulis ulang file tersebut:
file_path pada input JSON di stdin. Pemeriksaan grep-nya menguji hal yang sama dengan yang dihapus oleh perl, yaitu CR di akhir baris, sehingga eksekusi setelah normalisasi keluar tanpa menyentuh file. Pemeriksaan yang lebih longgar akan berulang tanpa henti, karena perl -i menulis ulang file bahkan ketika tidak mengganti apa pun dan Claude Code menjalankan hook lagi setelah setiap penulisan ulang. Simpan skrip ini di /path/to/normalize-line-endings.sh dan jadikan dapat dieksekusi:
data.csv dengan perintah Bash. Claude Code menjalankan hook dan file akhirnya memiliki akhir baris LF.
Untuk memantau file yang tidak dapat Anda sebutkan di awal, kembalikan watchPaths dari hook untuk memperbarui daftar pantauan secara dinamis. Claude Code memulai watcher hanya ketika ada sesuatu yang menyebutkan file untuk dipantau, jadi isi daftar awal dengan grup FileChanged yang matcher-nya menyebutkan setidaknya satu file, atau dengan hook SessionStart atau CwdChanged yang mengembalikan watchPaths. Matcher tetap memfilter grup hook mana yang berjalan ketika file yang dipantau berubah, jadi berikan grup yang menangani path dinamis matcher yang dihilangkan, yang cocok dengan setiap file yang dipantau dan tidak menambahkan apa pun ke daftar pantauan. Matcher "*" juga cocok dengan setiap file, tetapi Claude Code mendaftarkannya di daftar pantauan seperti nilai lainnya, sebagai file literal bernama *.
Hook FileChanged memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file tersebut bertahan ke perintah Bash berikutnya hingga event CwdChanged berikutnya, saat Claude Code menghapusnya.
Input FileChanged
Selain field input umum, hook FileChanged menerimafile_path dan event.
Output FileChanged
Selain field output JSON yang tersedia untuk semua hook, hook FileChanged dapat mengembalikanwatchPaths untuk memperbarui secara dinamis path file mana yang dipantau:
Hook FileChanged tidak memiliki kontrol keputusan. Hook ini tidak dapat memblokir terjadinya perubahan file.
Claude Code membaca
watchPaths dan systemMessage dari output JSON-nya dan membuang continue. Dalam sesi interaktif, Claude Code menampilkan systemMessage sebagai notifikasi terminal singkat. Pesan tersebut tidak mencapai aliran pesan SDK.
WorktreeCreate
Berjalan ketika Claude Code membuat worktree dalam kasus seperti berikut:- Anda memulai sesi dengan
claude --worktree - Claude membuat worktree selama sesi dan beralih ke dalamnya dengan tool
EnterWorktree - Sebuah subagent menggunakan
isolation: "worktree" - Sebuah workflow menjalankan agent di worktree-nya sendiri
- Claude Code mengisolasi sesi latar belakang di worktree-nya sendiri
claude remote-controldalam modeworktreememulai sesi di worktree-nya sendiri
--worktree dan --tmux:
git worktree addmelalui Bash: untuk memeriksa atau memblokir perintah tersebut, tambahkan hookPreToolUsedengan matcherBash.--worktreedengan--tmux: Claude Code membuat worktree sebelum memuat plugin, sehingga hook WorktreeCreate dari plugin tidak berjalan. Agar hook Anda berjalan dengan kedua flag tersebut, definisikan hook di file pengaturan sebagai gantinya. Sebelum v2.1.300, hook WorktreeCreate dari plugin juga tidak berjalan dengan--worktreesaja.
git worktree. Mengonfigurasi hook WorktreeCreate menggantikan perilaku git default tersebut, sehingga Anda dapat menggunakan sistem kontrol versi lain 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 ke direktori worktree yang dibuat. Claude Code menggunakan path ini sebagai direktori kerja untuk sesi terisolasi. Lihat output WorktreeCreate untuk cara setiap jenis hook mengembalikan path.
Claude Code bertindak berdasarkan keberhasilan hook dan path yang dikembalikan, serta membuang systemMessage dan continue.
Contoh ini membuat salinan kerja SVN dan mencetak path untuk digunakan Claude Code. Ganti URL repositori dengan milik Anda:
name worktree dari input JSON di stdin, melakukan checkout salinan baru ke direktori baru, dan mencetak path direktori tersebut. echo pada baris terakhir adalah yang dibaca Claude Code sebagai path worktree. Alihkan output lain ke stderr agar tidak mengganggu path.
Input WorktreeCreate
Selain field input umum, hook WorktreeCreate menerima fieldname. Ini adalah pengidentifikasi slug untuk worktree baru, baik yang ditentukan oleh pengguna maupun yang dibuat otomatis, misalnya bold-oak-a3f2.
Output WorktreeCreate
Hook WorktreeCreate tidak menggunakan model keputusan allow/block standar. Sebaliknya, keberhasilan atau kegagalan hook menentukan hasilnya. Hook harus mengembalikan path ke direktori worktree yang dibuat:- Hook command (
type: "command"): cetak path sebagai baris tidak kosong terakhir dari stdout. Claude Code menghapus kode escape ANSI sebelum membaca baris tersebut, sehingga banner startup shell yang dicetak sebelumechoAnda diabaikan. Alihkan output hook lainnya ke stderr. - Hook HTTP (
type: "http"): kembalikan{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }di body respons.
. atau .. di dalamnya. Jika path yang dihasilkan bukan direktori yang dapat dimasuki Claude Code, sesi mencetak error yang menyebutkan path tersebut dan keluar dengan kode 1.
Claude Code menolak path absolut yang berisi segmen . atau .., serta path apa pun yang melewati symlink di bawah root repositori. Error menyebutkan komponen yang ditolak. Symlink yang di-commit ke repositori dapat mengalihkan worktree ke luar repositori. Kembalikan path yang dinormalisasi dan tidak melewati symlink di dalam repositori.
WorktreeRemove
Berjalan ketika Claude Code membersihkan worktree yang dibuat oleh hookWorktreeCreate Anda. Event ini terpicu ketika:
- Anda keluar dari sesi worktree interaktif dan memilih untuk menghapus worktree ketika Claude Code meminta Anda
- Anda keluar dari sesi worktree interaktif yang belum Anda beri nama, Claude Code tidak menemukan file yang berubah atau tidak terlacak, dan Claude Code menghapus worktree tanpa meminta konfirmasi Anda
- Anda menghapus sesi latar belakang yang berjalan di worktree tersebut
- Anda meminta Claude untuk keluar dan menghapus worktree selama sesi, dan Claude menghapusnya dengan tool
ExitWorktree - Sesi yang dimulai oleh
claude remote-controldalam modeworktreeberakhir tanpa crash, dan Claude Code tidak menemukan file yang berubah atau tidak terlacak serta tidak ada commit baru di worktree-nya - Anda menghentikan
claude remote-controlsaat sesi yang dimulainya dalam modeworktreemasih berjalan, dan Claude Code tidak menemukan file yang berubah atau tidak terlacak serta tidak ada commit baru di worktree-nya
ExitWorktree atau pembersihan claude remote-control. ExitWorktree menolak menghapus worktree yang dibuat hook kecuali panggilan tersebut menyertakan discard_changes: true, yang menjadikan hook Anda sebagai pemeriksaan terakhir pada jalur tersebut.
Claude Code menggunakan git untuk mencari file yang berubah atau tidak terlacak, sehingga tidak menemukan apa pun di worktree yang bukan checkout git atau tidak berada di dalamnya, bahkan ketika direktori tersebut berisi pekerjaan yang belum di-commit. Periksa pekerjaan tersebut di hook WorktreeRemove Anda sebelum hook menghapus apa pun.
Untuk worktree berbasis git, Claude Code menangani pembersihan secara otomatis dengan git worktree remove. Jika Anda mengonfigurasi hook WorktreeCreate, pasangkan dengan hook WorktreeRemove untuk mengontrol pembersihan:
- Tanpa hook WorktreeRemove: Claude Code melakukan fallback ke git untuk penghapusan saat keluar atau melalui
ExitWorktree, dan menyimpan worktree ketikaclaude remote-controlmelakukan pembersihan:- Anda keluar dari sesi worktree, atau Claude memanggil
ExitWorktree: Claude Code menghapus path yang dikembalikan oleh hook WorktreeCreate Anda seperti yang dilakukangit worktree remove --force, sehingga worktree yang dikenali git akan dihapus. Worktree yang tidak dikenali git, misalnya yang dibuat hook Anda dengan sistem kontrol versi non-git, tetap ada di disk. claude remote-controlmembersihkan worktree sesi: worktree tetap ada di disk, dan terminal yang menjalankanclaude remote-controlmenampilkanworktree removal failed, kept: <path>.- Anda menghapus sesi latar belakang: lihat Apa yang dihapus saat menghapus sesi.
- Anda keluar dari sesi worktree, atau Claude memanggil
- Hook keluar dengan 0: worktree dianggap telah dihapus. Claude Code tidak membaca apa pun lagi dari hook, jadi pastikan hook Anda telah menghapus direktori tersebut.
- Hook keluar dengan non-zero: penghapusan gagal jika direktori di
worktree_pathmasih ada setelahnya, dan worktree tetap ada di disk tanpa fallback git. Hook yang menghapus direktori sebelum keluar dengan non-zero dianggap telah menghapus. Untuk cara kegagalan dilaporkan, lihat input WorktreeRemove.
systemMessage dan continue.
Untuk penghapusan sesi latar belakang, Claude Code memverifikasi path worktree yang tersimpan sebelum menjalankan hook, dan menolak path yang merupakan symlink atau melewati symlink di bawah root repositori. Pada jalur tersebut, hook berjalan untuk worktree yang masih berisi file hanya ketika Anda mengonfirmasi penghapusan di agent view. Untuk worktree seperti itu, claude rm justru mempertahankan sesi dan worktree.
Claude Code meneruskan path yang dikembalikan oleh WorktreeCreate sebagai worktree_path dalam input hook. Contoh ini membaca path tersebut dan menghapus direktorinya:
Input WorktreeRemove
Selain field input umum, hook WorktreeRemove menerima fieldworktree_path, yaitu path absolut ke worktree yang sedang dihapus.
worktree_path masih ada setelahnya, penghapusan gagal:
- Worktree tetap ada di disk, dan perintah serta stderr hook dikirim ke log debug.
- Jika Anda sedang menghapus sesi latar belakang, sesi tersebut juga tetap ada. Pesan penolakan di agent view melaporkan bagaimana hook berakhir, seperti
exited 1, mengutip awal stderr-nya, dan menyatakan apakah menghapus sesi lagi tetap akan menghapus direktori tersebut. - Jika
claude remote-controlsedang membersihkan worktree sesi, terminal yang menjalankannya menampilkanworktree removal failed, kept: <path>.
PreCompact
Berjalan sebelum Claude Code akan menjalankan operasi compact. Nilai matcher menunjukkan apakah compaction dipicu secara manual atau otomatis:
Keluar dengan kode 2 untuk memblokir compaction. Untuk
/compact manual, pesan stderr ditampilkan kepada pengguna. Anda juga dapat memblokir dengan mengembalikan JSON dengan "decision": "block".
Memblokir compaction otomatis memiliki efek berbeda tergantung kapan terpicu. Jika compaction dipicu secara proaktif sebelum batas konteks, Claude Code melewatinya dan percakapan berlanjut tanpa di-compact. Jika compaction dipicu untuk memulihkan dari error batas konteks yang sudah dikembalikan oleh API, error yang mendasarinya muncul dan permintaan saat ini gagal.
Claude Code membuang field systemMessage dan continue dari hook PreCompact.
Input PreCompact
Selain field input umum, hook PreCompact menerimatrigger dan custom_instructions. Untuk manual, custom_instructions berisi apa yang diteruskan pengguna ke /compact dan bernilai null ketika mereka tidak meneruskan apa pun. Untuk auto, custom_instructions bernilai null.
Output PreCompact
Ketika hook PreCompact keluar dengan 0 dan tidak memblokir, Claude Code menggunakan stdout-nya sebagai instruksi untuk ringkasan compaction. Jaga stdout tersebut di bawah 10.000 karakter per hook. Claude Code memotong output yang lebih panjang pada batas tersebut dan menambahkan penanda.PostCompact
Berjalan setelah Claude Code menyelesaikan operasi compact. Gunakan event ini untuk bereaksi terhadap state baru yang sudah di-compact, misalnya untuk mencatat ringkasan yang dihasilkan atau memperbarui state eksternal. Claude Code membuang fieldsystemMessage dan continue dari hook PostCompact.
Nilai matcher yang sama berlaku seperti untuk PreCompact:
Input PostCompact
Selain field input umum, hook PostCompact menerimatrigger dan compact_summary. Field compact_summary berisi ringkasan percakapan yang dihasilkan oleh operasi compact.
PreModelSwitch
Berjalan sebelum Claude Code menerapkan pergantian model yang diminta oleh Anda atau klien. Gunakan untuk memblokir pergantian, meminta konfirmasi, atau menampilkan biaya pergantian sebelum terjadi. PreModelSwitch memerlukan Claude Code v2.1.251 atau yang lebih baru. Claude Code menjalankannya untuk permintaan berikut:/model <name>dan pemilih/model- Pemilih model
Option+PatauAlt+P - Pengaturan Model di
/config - Mengaktifkan fast mode ketika hal itu mengubah model sesi
- Permintaan
set_model, atau perubahan model dalam permintaanapply_flag_settings, dari host Agent SDK atau Remote Control
[1m] apa pun. Alias seperti opus, ID model bertanggal, dan ID khusus penyedia seperti ID model Amazon Bedrock semuanya cocok dengan satu nama kanonis yang menjadi hasil resolusinya, sehingga claude-opus-5 mencakup setiap penulisan Opus 5.
Ketika Claude Code tidak dapat menentukan nama kanonis untuk target, misalnya ID model kustom yang hanya diketahui oleh LLM gateway Anda, Claude Code menjalankan setiap hook PreModelSwitch terlepas dari matcher-nya. Oleh karena itu, hook yang memblokir harus memeriksa to_model dari input-nya alih-alih hanya mengandalkan matcher.
Tulis matcher sebagai nama persis, daftar yang dipisahkan | seperti claude-opus-4-6|claude-opus-5, atau ekspresi reguler seperti .*opus.*. Contoh ini menggunakan matcher nama persis dan juga memeriksa to_model dari input hook, sehingga menolak pergantian ke Opus 4.6 dengan keluar menggunakan kode 2 dan membiarkan target lain lolos:
- macOS/Linux
- Windows (PowerShell)
Perintah ini memeriksa
to_model dengan jq:/model claude-opus-4-6 dari sesi yang menjalankan model lain. Claude Code mempertahankan model saat ini dan melaporkan bahwa hook PreModelSwitch memblokir pergantian, dengan pesan Anda sebagai alasannya.
Input PreModelSwitch
Selain field input umum, hook PreModelSwitch menerima field dalam tabel ini. Lima field terakhir menjelaskan biaya pengiriman ulang percakapan ke model baru, sehingga hook dapat menampilkan angka tersebut sebelum pergantian terjadi.
Contoh ini menunjukkan input untuk
/model opus dalam sesi yang menjalankan Sonnet 5:
Kontrol keputusan PreModelSwitch
HookPreModelSwitch dapat membatalkan pergantian, meminta pengguna untuk mengonfirmasinya, atau membiarkannya berlanjut. Exit code 2 atau decision: "block" tingkat atas membatalkan pergantian.
Untuk kontrol yang lebih rinci, kembalikan permissionDecision dan permissionDecisionReason dalam objek hookSpecificOutput, seperti pada PreToolUse. PreModelSwitch menerima "allow", "deny", dan "ask". Hook ini tidak menerima "defer", updatedInput, atau additionalContext. Tabel di bawah menjelaskan kedua field tersebut:
Hanya
/model dalam sesi interaktif yang dapat menampilkan dialog "ask". Pada setiap surface lain, termasuk mode non-interaktif dengan flag -p, /config, dan permintaan set_model, Claude Code memperlakukan "ask" sebagai penolakan.
Contoh ini meminta pengguna untuk mengonfirmasi dan mengutip jumlah token dari context_tokens:
deny > ask > allow.
Claude Code menampilkan kepada pengguna setiap systemMessage yang dikembalikan hook Anda terlepas dari keputusannya, sehingga hook pelaporan biaya dapat mengembalikan {"systemMessage": "..."} dan keluar dengan 0.
Timeout PreModelSwitch dan exit code lainnya
Hook PreModelSwitch yang tidak merespons sebelum timeout-nya memblokir peralihan. Timeout default untuk event ini adalah 30 detik. Exit code selain 0 atau 2 tanpa output JSON yang valid merupakan error yang tidak memblokir, sehingga peralihan tetap berlangsung, kecuali untuk hook yang kegagalannya memblokir peralihan.PostModelSwitch
Berjalan setelah model sesi berubah. Gunakan untuk memberikan panduan khusus model kepada Claude tanpa mengedit setiap CLAUDE.md, misalnya instruksi tingkat organisasi yang berlaku pada model tertentu. PostModelSwitch memerlukan Claude Code v2.1.251 atau yang lebih baru. Hook ini tidak dapat memblokir, karena model sudah berubah. Claude Code menjalankan hook PostModelSwitch setelah salah satu perubahan berikut:- Pergantian yang diminta oleh Anda atau klien
- Fallback model otomatis, yang mengubah model sesi
- Pengaturan seperti
opusplanyang masuk atau keluar dari plan mode - Claude Code memulihkan model ketika Anda melanjutkan sesi
/model opus dari sesi Sonnet, lalu tanyakan kepada Claude panduan apa yang dimilikinya tentang model saat ini.
Input PostModelSwitch
Hook PostModelSwitch menerima field yang sama seperti PreModelSwitch, denganhook_event_name diatur ke "PostModelSwitch" dan dua nilai source tambahan: "auto" untuk fallback otomatis atau perubahan lain yang dilakukan Claude Code sendiri, dan "resume" untuk model yang dipulihkan ketika Anda melanjutkan sesi.
requested_model bernilai null ketika source adalah "auto". Ketika source adalah "resume", nilainya adalah pengaturan model tersimpan yang dipulihkan Claude Code.
Kontrol keputusan PostModelSwitch
Claude Code mengambil stdout teks biasa hook Anda saat keluar dengan 0, atauadditionalContext dari output JSON, dan mengirimkannya ke Claude bersama permintaan berikutnya setelah pergantian. Selain field output JSON yang tersedia untuk semua hook, Anda dapat mengembalikan:
Jika hook belum selesai dalam lima detik setelah Anda mengirim prompt berikutnya, Claude Code mengirim permintaan tersebut tanpa output dan melampirkannya ke permintaan berikutnya. Jika model berubah beberapa kali sebelum permintaan berikutnya, Claude Code hanya mengirimkan output untuk model target pergantian terakhir.
SessionEnd
Berjalan ketika sesi Claude Code berakhir. Berguna untuk tugas pembersihan, mencatat statistik sesi, atau menyimpan state sesi. Mendukung matcher untuk memfilter berdasarkan alasan keluar. Fieldreason dalam input hook menunjukkan mengapa sesi berakhir:
Input SessionEnd
Selain field input umum, hook SessionEnd menerima fieldreason yang menunjukkan mengapa sesi berakhir. Lihat tabel reason di atas untuk semua nilai.
systemMessage.
Hook SessionEnd memiliki timeout default 1,5 detik. Ini berlaku ketika Anda keluar, menjalankan /clear, atau beralih sesi dengan /resume interaktif. Anda dapat memberi hook lebih banyak waktu dengan dua cara:
timeoutper hook: aturtimeoutdalam konfigurasi hook tersebut. Anggaran keseluruhan naik secara otomatis untuk menyamaitimeoutper hook tertinggi di file pengaturan Anda, hingga 60 detik. Jika Anda menaikkan anggaran dengan cara ini, hook tanpatimeoutsendiri tetap menggunakan default. Timeout yang diatur pada hook yang disediakan plugin tidak menaikkan anggaran.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: atur environment variable ini dalam milidetik untuk menimpa anggaran secara eksplisit. Nilai yang Anda atur juga menjadi timeout untuk setiap hook tanpatimeoutsendiri.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS hanya menaikkan anggaran keseluruhan, dan hook tanpa timeout sendiri tetap dibatalkan setelah 1,5 detik.
Elicitation
Berjalan ketika server MCP meminta input pengguna di tengah tugas. Secara default, Claude Code menampilkan dialog interaktif agar pengguna merespons. Hook dapat mencegat permintaan ini dan merespons secara terprogram, melewati dialog sepenuhnya. Untuk hook lengkap beserta entri pengaturan dan skripnya, lihat Menjawab permintaan formulir dari skrip. Field matcher dicocokkan dengan nama server MCP.Input Elicitation
Selain field input umum, hook Elicitation menerimamcp_server_name, message, serta field opsional mode, url, elicitation_id, dan requested_schema.
Untuk elicitation mode formulir, kasus yang paling umum:
Output Elicitation
Hook Elicitation dapat menjawab permintaan atas nama pengguna, menolak atau membatalkannya, atau menyerahkannya ke dialog. Untuk menjawab, menolak, atau membatalkan, keluar dengan 0 dan cetak objekhookSpecificOutput dengan action. Server menerima jawaban Anda dan tidak ada dialog yang muncul. Setiap baris tabel ini menunjukkan apa yang dikembalikan untuk satu hasil dan apa yang diterima server MCP:
Output ini menjawab permintaan mode formulir yang ditampilkan di Input Elicitation. Kunci dalam
content adalah nama properti dari requested_schema permintaan tersebut:
decline dan menekan Esc mengirim cancel, jadi kembalikan yang ingin Anda tampilkan ke server.
Untuk permintaan mode URL, hook yang mengembalikan accept melewati dialog, sehingga URL tidak pernah dibuka.
Claude Code membuang reason, systemMessage, dan continue dari output JSON hook Elicitation, apa pun action yang Anda kembalikan.
Cara lain untuk menolak elicitation
Hook Anda juga dapat menolak dengan cara berikut. Server menerimadecline yang sama seperti untuk "action": "decline":
- Keluar dengan kode 2: Claude Code mengabaikan
hookSpecificOutputyang dicetak oleh hook yang sama - Mencetak
"decision": "block"tingkat atas: block menimpaactiondalam output yang sama
accept atau cancel dari hook lain.
Skrip ini menolak permintaan mode URL dan menyerahkan permintaan formulir ke dialog:
reason Anda.
Claude Code mengabaikan decision tingkat atas dari hook Elicitation dan ElicitationResult sejak v2.1.105 hingga perbaikan di v2.1.284.
Menjawab permintaan formulir dari skrip
Contoh ini menjawab satu pertanyaan berulang atas nama pengguna. Server MCP bernamaissue-tracker meminta kunci proyek dalam formulir, dan hook mengisinya dengan DOCS. Skrip menerima ketika project_key adalah satu-satunya field formulir. Untuk permintaan lainnya, skrip tidak mencetak apa pun, sehingga dialog muncul.
- macOS/Linux
- Windows (PowerShell)
Daftarkan hook command untuk event ini di file pengaturan Anda, dengan nama server sebagai matcher:Simpan skrip ini ke
.claude/hooks/answer-project-key.sh di proyek Anda dan jadikan dapat dieksekusi dengan chmod +x:claude --debug dan berikan Claude tugas yang membuat server meminta kunci proyek. Tidak ada dialog yang muncul, dan log debug memiliki baris yang diakhiri dengan Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}.
ElicitationResult
Berjalan setelah pengguna merespons elicitation MCP. Hook dapat mengamati, memodifikasi, atau memblokir respons sebelum dikirim kembali ke server MCP. Ketika hook Elicitation menjawab suatu permintaan, Claude Code mengirim jawaban tersebut ke server tanpa menjalankan hook ElicitationResult. Field matcher dicocokkan dengan nama server MCP.Input ElicitationResult
Selain field input umum, hook ElicitationResult menerimamcp_server_name, action, serta field opsional mode, elicitation_id, dan content.
Output ElicitationResult
Hook ElicitationResult dapat meloloskan respons pengguna, mengubah nilainya, atau memblokirnya. Untuk mengubah atau memblokir respons, keluar dengan 0 dan cetak objekhookSpecificOutput dengan action. Setiap baris tabel ini menunjukkan apa yang dikembalikan untuk satu hasil dan apa yang diterima server MCP:
Output ini mengubah respons yang ditampilkan di Input ElicitationResult, sehingga server menerima
alice@example.com padahal pengguna mengirimkan alice:
content Anda menggantikan seluruh objek content pengguna, jadi sertakan field yang tidak Anda ubah. Kembalikan action bersamanya, karena Claude Code mengabaikan hookSpecificOutput yang tidak memiliki action.
Hook ElicitationResult juga berjalan ketika pengguna menolak atau membatalkan, dan action Anda menggantikan milik mereka. Periksa bahwa action pada input adalah accept sebelum Anda mengembalikan accept, atau hook Anda akan mengubah permintaan yang ditolak menjadi diterima. Skrip ini melakukan perubahan yang sama ketika pengguna menerima, mempertahankan field lainnya, dan tidak mencetak apa pun dalam kasus lain:
"decision": "block" tingkat atas juga memblokir respons. Cara lain untuk menolak elicitation menjelaskan mana yang berlaku ketika hook menggabungkannya, apa yang dilihat pengguna, dan versi mana yang mengabaikan decision.
Claude Code membuang reason, systemMessage, dan continue dari output JSON hook ElicitationResult, apa pun action yang Anda kembalikan.
Prompt-based hooks
Selain command, HTTP, dan MCP tool 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 lima tipe hook (command, http, mcp_tool, prompt, dan agent):
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest mendukung hooks command, http, mcp_tool, dan prompt tetapi bukan hooks agent. Jika Anda mengonfigurasi agent hook pada event ini, Claude Code melewatinya dan aliran izin berlanjut tidak berubah. Untuk mengizinkan atau menolak dari hook, kembalikan objek keputusan dari command atau HTTP hook.
Events yang mendukung hooks command, http, dan mcp_tool tetapi bukan prompt atau agent:
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart dan Setup mendukung hooks command dan mcp_tool, dan MCP tool hook fields menjelaskan kapan hooks mcp_tool mereka berjalan. Mereka tidak mendukung hooks http, prompt, atau agent.
Bagaimana prompt-based hooks bekerja
Alih-alih menjalankan perintah Bash, prompt-based hooks:- Mengirimkan input hook dan prompt Anda ke model Claude, secara default model yang digunakan Claude Code untuk fungsionalitas latar belakang
- LLM merespons dengan JSON terstruktur yang berisi keputusan
- Claude Code memproses keputusan secara otomatis
Konfigurasi prompt hook
Aturtype ke "prompt" dan sediakan string prompt alih-alih command. Gunakan placeholder $ARGUMENTS untuk menyuntikkan data JSON input hook ke dalam teks prompt Anda.
Dalam prompt hook atau agent hook, Anda dapat menulis prompt sebagai aturan tentang apa yang harus diblokir atau diizinkan, seperti “Blokir setiap perintah Bash yang membaca file .env”, atau sebagai kondisi yang harus terpenuhi, seperti “Semua unit test lulus”.
Hook Stop ini meminta LLM untuk mengevaluasi apakah semua tugas selesai sebelum mengizinkan Claude selesai:
Skema respons
LLM harus merespons dengan JSON yang berisi:
Apa yang terjadi pada
ok: false tergantung pada event:
StopdanSubagentStop: alasan diumpankan kembali ke Claude sebagai instruksi berikutnya dan giliran berlanjut, kecuali respons juga menetapkanimpossible: true, dalam hal ini Claude Code mengizinkan stop dan giliran berakhirPreToolUse: panggilan tool ditolak; secara default giliran berakhir dan alasan penolakan muncul dalam chat sebagai baris peringatan. AturcontinueOnBlock: trueuntuk mengembalikan alasan ke Claude sebagai kesalahan tool sehingga dapat menyesuaikan dan melanjutkan, setara denganpermissionDecision: "deny"dari command hookPostToolUse: secara default giliran berakhir dan alasan muncul dalam chat sebagai baris peringatan. AturcontinueOnBlock: trueuntuk mengirimkan alasan kembali ke Claude dan melanjutkan giliran alih-alihPostToolBatch,UserPromptSubmit, danUserPromptExpansion: giliran berakhir dan alasan muncul sebagai baris peringatan. Events ini mengakhiri giliran padadecision: "block"terlepas daricontinuePostToolUseFailuredanTaskCreated: alasan dikembalikan ke Claude sebagai kesalahan tool dan giliran berlanjut, terlepas daricontinueOnBlockTaskCompleted: ketika terjadi karena tugas ditandai selesai selama giliran, alasan dikembalikan ke Claude sebagai kesalahan tool dan giliran berlanjut, terlepas daricontinueOnBlock. Ketika terjadi karena rekan kerja berhenti, berperilaku sepertiTeammateIdledan menghentikan rekan kerja secara defaultTeammateIdle: secara default rekan kerja berhenti dan alasan muncul sebagai baris peringatan. AturcontinueOnBlock: trueuntuk mengirimkan alasan kembali ke rekan kerja dan membiarkannya tetap bekerja alih-alihPermissionRequest:ok: falsetidak berpengaruh. Untuk menolak persetujuan dari hook, gunakan command hook yang mengembalikanhookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falsetidak berpengaruh karena penolakan sudah terjadi. Satu-satunya output yang dibaca event ini adalahhookSpecificOutput.retry, yang prompt dan agent hooks tidak dapat atur. Mereka berjalan pada event ini, tetapi output mereka diabaikan. Gunakan command hook untuk mengembalikanretry
Periksa beberapa kondisi sebelum berhenti
HookStop ini menggunakan prompt detail untuk memeriksa tiga kondisi sebelum mengizinkan Claude berhenti. Hooks SubagentStop menggunakan format yang sama untuk mengevaluasi apakah subagent harus berhenti. Jika model mengembalikan "ok": false karena kondisi belum terpenuhi, Claude terus bekerja dengan alasan yang disediakan sebagai instruksi berikutnya:
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, kecuali PermissionRequest.
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 memungkinkan tindakan jika
okadalahtrue. Jikaokadalahfalse, Claude Code menangani blokir dengan cara yang sama seperti prompt hook dengancontinueOnBlock: truepada event tersebut, seperti yang tercantum di bawah Response schema
Konfigurasi agent hook
Aturtype ke "agent" dan sediakan string prompt, menggunakan $ARGUMENTS sebagai placeholder untuk JSON input hook. Bidang konfigurasi sama seperti prompt hooks, kecuali bahwa agent hooks memiliki timeout default yang lebih lama yaitu 60 detik dan tidak ada field continueOnBlock.
Skema respons adalah { "ok": true } untuk mengizinkan atau { "ok": false, "reason": "..." } untuk memblokir. Pada ok: false, Claude Code menangani agent hook dengan cara yang sama seperti menangani prompt hook dengan continueOnBlock: true pada event yang sama; agent hooks tidak memiliki field continueOnBlock, dan tidak mendukung field impossible dari prompt hook.
Hook Stop ini memverifikasi bahwa semua unit tests lulus sebelum mengizinkan Claude selesai:
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. Ketika skrip selesai, outputnya disampaikan pada turn percakapan berikutnya:
timeout padanya. Claude Code masih memberlakukan timeout pada hook yang Anda jalankan dengan asyncRewake.
Claude Code mengirimkan hasil async hook hanya saat sesi berjalan:
- Dalam mode non-interaktif dengan flag
-p, Claude Code membunuh hook async apa pun yang masih berjalan saat teardown dan menyelesaikannya dengan outcomecancelled - Jika pekerjaan hook Anda harus bertahan lebih lama dari sesi
claude -p, mulai proses yang sepenuhnya terpisah darinya
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, Claude Code mengirimkan bidangadditionalContext dan systemMessage dari respons JSON hook ke Claude pada giliran percakapan berikutnya, kecuali untuk output SessionStart yang berulang. Tidak seperti systemMessage hook sinkron, tidak ada bidang yang ditampilkan kepada Anda.
Cetak respons JSON sendirian di stdout atau pada satu barisnya sendiri:
- Sendirian di stdout: ketika respons adalah satu-satunya teks di stdout, respons tersebut dapat mencakup beberapa baris, seperti output
jqyang diformat rapi. Mencakup beberapa baris memerlukan Claude Code v2.1.295 atau yang lebih baru. - Pada satu barisnya sendiri: async hook dapat mencetak teks lain ke stdout ketika respons muat dalam satu baris tersendiri, misalnya dengan
jq -c.
systemMessage yang bukan string, alih-alih menyampaikannya. Jalankan dengan --debug untuk melihat peringatan yang menyebutkan setiap bidang yang dihapus.
Notifikasi penyelesaian async hook ditekan secara default. Untuk melihatnya, aktifkan mode verbose dengan Ctrl+O atau mulai Claude Code dengan --verbose.
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:
.claude/settings.json dalam akar proyek Anda. Flag async: true memungkinkan Claude terus bekerja sementara tests dijalankan:
Keterbatasan
Async hooks memiliki batasan tambahan dibandingkan dengan hooks sinkron:- Output hook disampaikan pada turn percakapan berikutnya. Jika sesi idle, respons menunggu sampai interaksi pengguna berikutnya. Pengecualian: hook
asyncRewakeyang keluar dengan kode 2 membangunkan Claude segera bahkan ketika sesi idle. - Setiap eksekusi membuat proses latar belakang terpisah.
- Async hook
SessionStartberjalan setiap kali event dipicu. Claude Code melewati output hook ketika teksadditionalContextdansystemMessagecocok dengan apa yang terakhir diterima Claude dari hook tersebut, kecuali hookSessionStartsinkron telah menambahkan teks lain sejak saat itu. Claude Code mengirimkan teks tersebut ketika teksnya berubah, dan sekali lagi saat hook berjalan berikutnya setelah compaction atau/clear. Sebelum v2.1.295, Claude Code menambahkan teks yang sama ke percakapan lagi pada setiap resume.
Pertimbangan keamanan
Penafian
Kepercayaan workspace
Claude Code memeriksa kepercayaan workspace sebelum menjalankan hook apa pun dari file pengaturan. Apa yang dianggap terpercaya tergantung pada jenis sesi:- Sesi interaktif: Claude Code menahan hook dari setiap file pengaturan, termasuk
~/.claude/settings.jsonAnda sendiri, sampai Anda menerima dialog kepercayaan workspace untuk folder, atau untuk direktori induk yang kepercayaannya meluas ke dalamnya - Sesi
-patau SDK: Claude Code tidak pernah menampilkan dialog dan memperlakukan folder sebagai terpercaya, sehingga hook yang dikomit dalam.claude/settings.jsonrepositori berjalan di folder yang belum pernah Anda percayai
claude -p di atas repositori yang tidak Anda tulis, tinjau file pengaturan .claude/ nya, mulai dengan --bare, atau matikan hooks untuk run itu dengan --settings '{"disableAllHooks": true}'. Hook frontmatter dalam subagent proyek mengikuti aturan yang lebih ketat daripada hook file pengaturan. Apa yang berjalan sebelum Anda mempercayai folder mencantumkan setiap jenis konten repositori berdasarkan jenis sesi.
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. Dalam bentuk exec, gunakan
${CLAUDE_PROJECT_DIR}dan path tidak perlu dikutip. Dalam bentuk shell, bungkus dengan tanda kutip ganda - Lewati file sensitif: hindari
.env,.git/, keys, dll.
Windows PowerShell tool
Di Windows, Anda dapat menjalankan hook individual dalam PowerShell dengan menetapkan"shell": "powershell" pada command hook. Claude Code auto-detects pwsh.exe, executable PowerShell 7 dan yang lebih baru, dan fallback ke powershell.exe untuk Windows PowerShell 5.1.
Mereferensikan skrip berdasarkan jalur dalam hook PowerShell
Untuk mereferensikan root proyek dari perintah bentuk shell PowerShell, tulis${CLAUDE_PROJECT_DIR} atau $env:CLAUDE_PROJECT_DIR. Claude Code menulis ulang placeholder ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT}, dan ${CLAUDE_PLUGIN_DATA} dalam perintah bentuk shell PowerShell ke bentuk ${env:NAME} PowerShell, baik hook didefinisikan dalam settings.json, plugin, atau skill. PowerShell kemudian menyelesaikan nilai dari lingkungan yang diekspor setelah parsing, jadi placeholder bekerja di dalam string dengan tanda kutip ganda tetapi tidak di dalam string dengan tanda kutip tunggal, di mana PowerShell tidak pernah memperluas variabel.
Jangan tulis ejaan bare $CLAUDE_PROJECT_DIR dalam hook PowerShell. PowerShell menguraikannya sebagai variabel lokal yang tidak terdefinisi dan menyelesaikannya ke $null, yang meninggalkan jalur skrip tanpa awalan root proyeknya. Claude Code tidak menulis ulang bentuk itu; sebaliknya, ia mencatat peringatan dalam debug log.
Contoh di bawah menunjukkan hook settings.json yang menjalankan skrip proyek dengan bentuk $env::
Debug hooks
Detail eksekusi hook ditulis ke file debug log. Mulai Claude Code denganclaude --debug-file <path> untuk menulis log ke lokasi yang diketahui, atau jalankan claude --debug dan baca log di ~/.claude/debug/<session-id>.txt. Flag --debug tidak mencetak ke terminal.
Sebagai contoh, hook PostToolUse pada Write yang perintahnya mencetak hook-ran menghasilkan entri seperti:
Hooks: di log yang diakhiri dengan durasi. Di Claude Code v2.1.296 atau yang lebih baru, setiap hook perintah pada event tool, UserPromptSubmit, SessionStart, Stop, dan beberapa event lainnya meninggalkan satu baris tersebut saat selesai, apa pun yang dicetaknya. Baris tersebut memuat nama event, digabungkan dengan titik dua ke nama tool atau nilai lain yang dicocokkan oleh hook, lalu perintah hook dalam tanda kurung siku, plugin asalnya jika ada, bagaimana eksekusi berakhir, dan berapa lama waktu yang dibutuhkan, seperti pada Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). Eksekusi juga dapat berakhir sebagai timed out after <N>ms, cancelled, moved to the background, atau failed to start. Pada beberapa event, seperti Notification, SessionEnd, dan PreCompact, hook perintah justru meninggalkan baris completed with status tanpa durasi.
Untuk detail pencocokan hook yang lebih granular, 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, Stop hooks yang terus memblokir, atau kesalahan konfigurasi, lihat Limitations and troubleshooting dalam panduan. Untuk panduan diagnostik yang lebih luas mencakup /context, /doctor, dan precedence pengaturan, lihat Debug your config.